Graphics.MeasureString возвращает значения, отличные от Win32 GetTextExtent

Я пытался найти метод в C # для измерения размера строки. Стандартный метод измерения строки в Win32 - это использование GetTextExtent. Настоящая цель - найти среднюю ширину и высоту символа шрифта. Стандартный метод определения средней ширины символа начинается с получения ширины всех буквенных символов и деления на 52:

size = dc.GetTextExtent(
       "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", 52);
averageWidth = size / 52;

У Microsoft есть страница со списком средней ширины символов для некоторых размеров шрифта. при определенных настройках DPI, и я подтвердил их своими собственными вызовами GetTextExtent.

  • Tahoma 8pt, 96dpi: 13 x 6 пикселей.
  • Tahoma 9pt, 96 точек на дюйм: 14x7 пикселей
  • Segoe UI, 9 пикселей, 96 точек на дюйм: 15 x 7 пикселей.

Теперь я хочу выполнить те же вычисления в .NET WinForms. Используя Graphics.MeasureString (), я придумываю код:

public static SizeF GetAvgCharSizeF(Graphics g, Font font)
{
   String s = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";

   SizeF textSize = g.MeasureString(s, font);

   float baseUnitX = (textSize.Width / s.Length);
   float baseUnitY = textSize.Height;

   return new SizeF(baseUnitX, baseUnitY);
}

К сожалению, значения не соответствуют известным, принятым и истинным значениям:

  • Tahoma 8pt, 96dpi: 14 x 6 пикселей (14,21 x 6,09 пикселей)
  • Tahoma 9pt, 96dpi: 16x7 пикселей (15,98x6,85 пикселей)
  • Segoe UI 9pt, 96 точек на дюйм: 17x7 пикселей (17,46x6,91 пикселей)

Средняя ширина символов в порядке, но высота символов слишком велика примерно на 13%. Я предполагаю, что дополнительная высота связана с разницей в классификации высоты, которая была изменена, чтобы включить восходящие и нисходящие элементы. Если измерительная струна была слишком высокой, я попытался изменить ее с:

String s = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";

to

String s = "acemnorsuvwxyz";

думая, что теперь веревка не такая высокая, и поэтому измеренная высота должна быть короче. Нет, он возвращает точную высоту, что и версия строки, которая включала восходящие и нисходящие элементы (хотя средняя ширина немного больше).

Что может быть причиной этого и можно ли получить среднюю высоту символа, что можно сделать со средней высотой текста, чтобы она соответствовала принятым значениям, возвращаемым GetTextExtent?

Примечание. Несмотря на то, что стандартной практикой для получения средней высоты символа является использование GetTextMetrics, высота, возвращаемая GetTextExtents, возвращает то же значение.


person Ian Boyd    schedule 15.10.2008    source источник
comment
Вы пробовали TextRenderer.MeasureText?   -  person Martin Plante    schedule 24.12.2008


Ответы (1)


Я подозреваю, что это связано с тем, что механизм визуализации текста в .NET отличается. Возможно, вы захотите найти различия между GDI и GDI +. Все может снова измениться для вас, если вы вызовете метод Application.SetCompatibleTextRenderingDefault(<bool>) с параметром true или false (я считаю, что значение по умолчанию истинно).

В общем, если вы рисуете с помощью Windows API, измеряйте с помощью Windows API, а если вы рисуете с помощью .NET, измеряйте с помощью .NET.

person David Wengier    schedule 15.10.2008
comment
Стандартные положения и размеры элементов управления выражены в виде среднего символа с и высотой. Если средняя ширина и высота меняются в зависимости от того, кто измеряет, вы теряете стандарт. - person Ian Boyd; 15.10.2008
comment
Я понял, что действительно есть разница между Graphics.MeasureString и текстом GDI. Проблема была настолько серьезной, что, начиная с .NET Framwork 2.0, Microsoft переключила все в .NET на использование GDI для рендеринга текста. Вот что такое TextRenderer класс. Он содержит два метода (MeasureString и DrawString), которые просто вызывают GDI. А поскольку GDI продолжает улучшаться (скорость, лигатуры и т. Д.), TextRenderer является предпочтительным способом визуализации текста в .NET. Graphics.DrawString мертв и существует только для совместимости. - person Ian Boyd; 16.11.2011