Directwrite: получение высоты шрифта - PullRequest
7 голосов
/ 08 апреля 2011

Моя цель: Я хочу получить высоту шрифта IDWriteTextFormat, чтобы вычислить, сколько строк текста может поместиться в IDWriteTextLayout определенной высоты.

Моя проблема: Прямо сейчас я использую этот код для вычисления видимого количества строк:

inline int kmTextCtrl::GetVisLines() const
{

    /* pTextFormat is an IDWriteTextFormat pointer, dpi_y is the desktop's vertical dpi,
       and GetHeight() returns the height (in pixels) of the render target. */
    float size = (pTextFormat->GetFontSize()/72.0f)*dpi_y;
    return (int)(GetHeight()/size);
}

Расчет представляется точным для некоторых шрифтов, но не для каких-либо шрифтов TrueType (например: Courier New, Arial, Times New Roman). Для этих шрифтов показанный текст обрезается значительно ниже нижней вертикальной границы цели рендеринга.

Некоторый контекст: Я делаю текстовый элемент управления буфером прокрутки назад, который использует IDWriteTextLayout, чтобы поместить текст в цель рендеринга элемента управления. Я использую результат GetVisLines (), чтобы определить, сколько строк текста из кольцевого буфера (который хранит текст в строке std :: strings), чтобы вставить в макет, и воссоздаю его каждый раз, когда окно прокручивается или изменяется.

Это делается с использованием "родного" Win32 API C ++.

Ответы [ 2 ]

8 голосов
/ 10 сентября 2011

Самый простой и надежный подход - просто запросить у самого макета текстовые метрики, поскольку это одна из двух вещей, для которых он был разработан: рисование и измерение.Вы должны создать IDWriteTextLayout, используя текстовый формат, и вызвать GetMetrics, чтобы получить DWRITE_TEXT_METRICS::height.Я предполагаю, что вы используете ID2D1RenderTarget::DrawText и передаете текстовый формат, так что вы, возможно, не создали макет напрямую, но вызов DrawText - это все равно, что вызвать CreateTextLayout самостоятельно, а затем DrawTextLayout.

Остерегайтесь того, что, проходя через нижние уровни, чтобы получить этот ответ (IDWriteFontFace и т. П.), Делаются определенные предположения, которые не должен предполагать общий текстовый готовый текстовый элемент управления, например, предполагается, что будет использован базовый шрифт, и что все строкитакой же высоты.До тех пор, пока все символы присутствуют в данном базовом шрифте, это сработает (скорее всего, вы в основном отображаете английский, поэтому все выглядит хорошо), но добавьте некоторые языки CJK или RTL (базовый шрифт, такой как Times)Новый римлянин, конечно, не поддерживает), и высота строки будет увеличиваться или уменьшаться в соответствии с заменяемыми шрифтами.GDI изменяет размеры замененных шрифтов таким образом, чтобы они соответствовали высоте основного шрифта, но это приводит к плохо сжатым буквам на языках, таких как тайский и тибетский, которые нуждаются в большем пространстве для подъема и спуска.IDWriteTextLayout и другие макеты, подобные тем, которые есть в WPF / Word, сохраняют все глифы шрифтов одинакового размера em, что означает, что они располагаются лучше, когда они расположены рядом друг с другом;но это означает, что высота строки является переменной.

Если вы просто нарисуете каждую строку текста, как если бы они были все одинаковой высоты, вы можете увидеть перекрытие между глифами и неоднородные базовые линии между строками или отсечениевверху и внизу элемента управления.Таким образом, идеальная вещь - это использовать фактическую высоту каждой строки;но если вам нужно, чтобы они все были одинаковой высоты (или если это слишком усложняет элемент управления), то, по крайней мере, установите явный межстрочный интервал, используя SetLineSpacing с DWRITE_LINE_SPACING_UNIFORM равным основному шрифту - таким образом, базовые линииравномерно распределены.

Хотя, для любопытных, да, высота линии действительно определяется с использованием метрик дизайна шрифта ascent + descent, плюс любой lineGap, который присутствует (большинство шрифтов устанавливают это значение на ноль, но Gabriolaявляется хорошим примером большого разрыва строки), умноженного на размер em и деленного на единицы на em.Обратите внимание, что все размеры em указаны в DIP (что при типичном 96DPI означает 1: 1, DIP точно == пиксели), а не в точках (1/72 дюйма).

7 голосов
/ 10 апреля 2011

Я нашел ответ . Чтобы найти межстрочный интервал (высота шрифта плюс зазор) в Directwrite, вы должны сделать что-то похожее на следующее:

inline int kmTextCtrl::GetVisLines() const
{

    IDWriteFontCollection* collection;
    TCHAR name[64]; UINT32 findex; BOOL exists;
    pTextFormat->GetFontFamilyName(name, 64);
    pTextFormat->GetFontCollection(&collection);
    collection->FindFamilyName(name, &findex, &exists); 
    IDWriteFontFamily *ffamily;
    collection->GetFontFamily(findex, &ffamily);
    IDWriteFont* font;
    ffamily->GetFirstMatchingFont(pTextFormat->GetFontWeight(), pTextFormat->GetFontStretch(), pTextFormat->GetFontStyle(), &font);
    DWRITE_FONT_METRICS metrics;
    font->GetMetrics(&metrics);
    float ratio = pTextFormat->GetFontSize() / (float)metrics.designUnitsPerEm;
    float size = (metrics.ascent + metrics.descent + metrics.lineGap) * ratio;
    float height = GetHeight();
    int retval = static_cast<int>(height/size);
    ffamily->Release();
    collection->Release();
    font->Release();
    return retval;
}

Конечно, вы, вероятно, не хотите делать все это каждый раз, когда вам приходится вызывать часто используемую встроенную функцию.

...