Положение текста DirectWrite отличается размером шрифта

Я использую DirectWrite для отображения текста в окне. Кажется, все работает, кроме позиционирования при использовании разных размеров шрифта: я ожидаю, что 2 текста с размером шрифта v1 и v2 и оба с (x, y) = (0, 0) будут вверху слева, но как вы можете видеть:

введите здесь описание изображения

ни «Тест», ни «X» на самом деле не находятся в левом верхнем углу.

Есть ли способ заставить это работать?


person user1233963    schedule 07.08.2013    source источник
comment
это связано с интервалом, который определен внутри шрифта и должен соответствовать фактическому размеру, поэтому вы видите, что с «Test» больше, чем с X. Я считаю, что это должен быть способ контролировать это в DirectDraw, но, честно говоря нет большого опыта в этом   -  person evilruff    schedule 07.08.2013


Ответы (3)


Добро пожаловать в мир шрифтов. Шрифты, вероятно, являются самой сложной вещью в использовании, потому что в самих шрифтах есть сюрпризы (есть так много новых стандартов, которые должны решить все и только еще больше запутать, потому что почти ни один шрифт не поддерживает его на 100%, даже некоторые «классические» шрифты имеют частичная/плохая информация в них) GDI, GDI+, DirectDraw не рисуют шрифт в той же позиции в пикселях из-за математики, округления координат, сглаживания... (у вас может быть еще один бонус, если вы делаете математику с свободный тип).

При попытке распечатать шрифт появляются другие pb. Так что единственный способ обойти это для меня. Даже не пытайтесь рисовать шрифт в определенных координатах пикселей. Делайте свою работу по рисованию шрифта, изображения, линий на экране, которые хорошо отображаются, делайте все возможное, чтобы преобразовать их в координаты печати для экспорта, но никогда не ожидайте контролировать пиксели в шрифтах, все округлено приблизительно.

PS: не доверяйте внутренним полям в шрифтах. В Arial они хороши для всех других шрифтов, некоторые из них отсутствуют или инициализированы до нуля, но «забавная» часть — это не всегда одно и то же поле, которого нет, это зависит от шрифтов. Вы можете использовать поля только в том случае, если вы попробуете их раньше по шрифту. Да, шрифты отличные!

person ColdCat    schedule 12.08.2013
comment
Итак, вы говорите, что нет общего решения (для всех шрифтов и размеров шрифтов)? Черт возьми... - person user1233963; 12.08.2013
comment
100% общий, извините, но мы не нашли никакого способа. Вы можете найти ограничивающую рамку для символа или слова, но не можете быть уверены в точном положении шрифта. Например, в слове я почти уверен, что подчеркивание сделано вручную, потому что иначе это не была бы идеальная линия с разными шрифтами/размерами. Для механизма рендеринга, использующего шрифт в GDI, ограничительной рамки Direct2D и PDF было достаточно, чтобы иметь идеальный вид. Но иногда на экране буква не совсем там, где вы хотите. - person ColdCat; 13.08.2013
comment
Вы можете найти ограничивающую рамку для символа или слова, которое, я думаю, должно подойти. Как я могу это сделать? - person user1233963; 13.08.2013
comment
В GDI DrawText DT_CALCRECT даст вам поле. В D2D это немного сложнее. Надеюсь, это поможет вам msdn.microsoft.com/en-us/library/windows/desktop/ - person ColdCat; 13.08.2013

Термин, на который ссылается @evilruff, называется «внутреннее лидерство». Вы можете использовать IDWriteFontFace::GetMetrics или, возможно, IDWriteFontFace ::GetDesignGlyphMetrics, чтобы получить это значение (для GetMetrics искомое значение, скорее всего, metrics.ascent - metrics.capHeight).

Значения здесь указаны в единицах оформления шрифта, а не в пикселях (любых). Вы можете преобразовать эти значения в высоту em, разделив на metrics.designUnitsPerEm; как правило, размеры шрифта в DirectWrite определяются размером пикселя (нижнего регистра) m; поэтому, если вы умножите значения в ems на размер шрифта, вы должны получить значения в пикселях.

person Eric Brown    schedule 07.08.2013
comment
Это вычитание дает мне что-то около 700 (для некоторой комбинации шрифта и размера шрифта), что является огромным смещением, поэтому я предполагаю, что оно не в пикселях? Если нет, то как мне его преобразовать? Спасибо за Ваш ответ ! - person user1233963; 08.08.2013
comment
Обновлен ответ, чтобы добавить конверсию. - person Eric Brown; 08.08.2013
comment
Деление на unitPerEm и последующее умножение на размер шрифта, кажется, всегда дает 0, с другой стороны, это почти идеально: float ratio = fontSize / (float)metrics.designUnitsPerEm; float offset = (metrics.ascent - metrics.capHeight) * отношение; но все же недостаточно хорошо. Любые другие идеи? - person user1233963; 10.08.2013
comment
Не совсем. Я нашел большую часть данных через Bing (например, преобразовать единицы дизайна шрифта в пиксели). Под «близким к идеальному» вы подразумеваете отклонение на один-два пикселя? В этом случае вы можете посмотреть на свое округление. - person Eric Brown; 10.08.2013
comment
Под «близким к идеальному» я подразумеваю отклонение по X на 4-5 пикселей и ~ 15 по Y (что, опять же, отличается для других размеров шрифта). - person user1233963; 10.08.2013
comment
Windows (и, следовательно, многие шрифты, установленные в ней) поддерживают Unicode. Учитывали ли вы, что есть некоторые глифы, такие как, например, «Ť» или «É», которым нужно больше места вверху? - person mity; 12.08.2013
comment
Проверьте разрыв строки. Необходимая дельта должна быть (metrics.ascent + metrics.lineGap — metrics.capHeight) * fontEmSize / metrics.designUnitsPerEm - person Dwayne Robinson; 21.09.2013

Я предполагаю, что вы используете IDWriteTextLayout в сочетании с DrawTextLayout (а не создаете свой собственный DWRITE_GLYPH_RUN). IDWriteTextLayout выравнивает глифы по ячейке их макета (включая полное восхождение и разрыв между строками), а не по рисованию глифов, и это верно почти для всех текстовых макетов, будь то веб-браузеры, текстовые процессоры или простые элементы управления редактированием. Если бы они этого не сделали (вместо выравнивания по верхней части буквы), то диакритические знаки в таких словах, как Ťhis, были бы обрезаны.

Если вы всегда хотите выровнять рукописный текст, создайте IDWriteTextLayout, вызовите IDWriteTextLayout::GetOverhangMetrics, а затем вызовите DrawTextLayout с источником, равным отрицательному значению DWRITE_OVERHANG_METRICS::left&top. Если вы хотите всегда выравнивать по высоте кепки (таким образом, «привет» и «Привет» будут отображаться в одной и той же вертикальной координате), тогда подход Эрика будет работать.

person Dwayne Robinson    schedule 21.09.2013