Почему Graphics.MeasureString () возвращает более высокий, чем ожидалось, номер?

Я создаю квитанцию ​​и использую объект Graphics, чтобы вызвать метод DrawString для печати требуемого текста.

graphics.DrawString(string, font, brush, widthOfPage / 2F, yPoint, stringformat); 

Это прекрасно работает для того, что мне нужно. Я всегда знал, что я печатаю, поэтому я мог бы вручную обрезать любые строки, чтобы он правильно поместился на 80-миллиметровой квитанционной бумаге. Затем мне пришлось добавить дополнительный бит функциональности, который сделает его более гибким. Пользователь может передать строки, которые будут добавлены в нижнюю часть.

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

 float width = document.DefaultPageSettings.PrintableArea.Width; int max = (int)(width / graphics.MeasureString("a", font).Width); 

Теперь ширина возвращает мне 283, что в мм составляет около 72, что имеет смысл, когда вы учитываете поля на бумаге 80 мм.

Но метод MeasureString возвращает 10.5 шрифта Courier New 8pt. Поэтому вместо того, чтобы обойти то, что я ожидал от 36 до 40, мне становится 26, в результате 2 строки текста превращаются в 3-4.

Единицы для PrintableArea.Width составляют 1/100-дюймовый дюйм, а для PageUnit для графического объекта – Display (который обычно составляет 1/100-дюймовый дюйм для принтеров). Так почему я только получаю 26 назад?

    Из WindowsClient.net:

    GDI + добавляет небольшое количество (1/6 em) к каждому концу каждой отображаемой строки. Этот 1/6 em позволяет использовать глифы с нависшими концами (например, курсивом f ), а также дает GDI + небольшую свободу действий, чтобы помочь с расширением сетки.

    Действие DrawString по DrawString будет работать против вас при отображении смежных прогонов:

    • Во-первых, StringFormat по умолчанию добавляет дополнительные 1/6 em на каждом конце каждого выхода;
    • Во-вторых, когда ширина сетки меньше, чем была спроектирована, строка разрешается сжимать до em.

    Чтобы избежать этих проблем:

    • Всегда передавайте MeasureString и DrawString a StringFormat на основе типографского StringFormat ( GenericTypographic ).
      Установите Graphics TextRenderingHint в TextRenderingHintAntiAlias . Этот метод рендеринга использует сглаживание и подпиксельное позиционирование глифов, чтобы избежать необходимости в подгонке сетки и, следовательно, по своей сути является независимым от разрешения.

    Существует два способа рисования текста в .NET:

    • GDI + ( graphics.MeasureString и graphics.DrawString )
    • GDI ( TextRenderer.MeasureText и TextRenderer.DrawText )

    Из превосходного блога Майкла Каплана (rip), сортирующего все это , в .NET 1.1 все использовалось GDI + для рендеринга текста. Но были некоторые проблемы:

    • Возникают некоторые проблемы с производительностью, вызванные неагрессивным характером GDI +, где будут установлены контексты устройств, а затем оригинал, восстановленный после каждого вызова.
    • Формирующие двигатели для международного текста неоднократно обновлялись для Windows / Uniscribe и для Avalon (Windows Presentation Foundation), но не были обновлены для GDI +, что заставляет международную поддержку перевода на новые языки не иметь такой же уровень качества.

    Поэтому они знали, что хотят изменить платформу .NET, чтобы прекратить использование системы визуализации GDI + и использовать GDI . Сначала они надеялись, что они могут просто измениться:

     graphics.DrawString 

    для вызова старого API DrawText вместо GDI +. Но они не могли сделать соответствие текста и интервал точно так же, как и GDI +. Таким образом, они были вынуждены сохранять graphics.DrawString для вызова GDI + (соображения совместимости, люди, которые вызывали graphics.DrawString вдруг обнаружил, что их текст не обернулся так, как он использовался).

    Для TextRenderer GDI-текста был создан новый статический class TextRenderer . Он имеет два метода:

     TextRenderer.MeasureText TextRenderer.DrawText 

    Примечание. TextRenderer – это shell вокруг GDI, в то время как graphics.DrawString все еще является оберткой вокруг GDI +.


    Затем возникла проблема, что делать со всеми существующими элементами управления .NET, например:

    • Label
    • Button
    • TextBox

    Они хотели переключить их на использование TextRenderer (т.е. GDI), но они должны были быть осторожны. Могут быть люди, которые зависели от их элементов управления, как это было в .NET 1.1. И так родился « совместимый текстовый рендеринг ».

    По умолчанию элементы управления в приложении ведут себя так же, как в .NET 1.1 (они « совместимы »).

    Вы выключаете режим совместимости, вызывая:

     Application.SetCompatibleTextRenderingDefault(false); 

    Это делает ваше приложение лучше, быстрее, с лучшей международной поддержкой. Подводить итоги:

     SetCompatibleTextRenderingDefault(true) SetCompatibleTextRenderingDefault(false) ======================================= ======================================== default opt-in bad good the one we don't want to use the one we want to use uses GDI+ for text rendering uses GDI for text rendering graphics.MeasureString TextRenderer.MeasureText graphics.DrawString TextRenderer.DrawText Behaves same as 1.1 Behaves *similar* to 1.1 Looks better Localizes better Faster 

    Также полезно отметить сопоставление между GDI + TextRenderingHint и соответствующим качеством LOGFONT используемым для рисования шрифта GDI:

     TextRenderingHint mapped by TextRenderer to LOGFONT quality ======================== ========================================================= ClearTypeGridFit CLEARTYPE_QUALITY (5) (Windows XP: CLEARTYPE_NATURAL (6)) AntiAliasGridFit ANTIALIASED_QUALITY (4) AntiAlias ANTIALIASED_QUALITY (4) SingleBitPerPixelGridFit PROOF_QUALITY (2) SingleBitPerPixel DRAFT_QUALITY (1) else (egSystemDefault) DEFAULT_QUALITY (0) 

    образцы

    Ниже приведены некоторые сравнения GDI + (graphics.DrawString) стилей GDI (TextRenderer.DrawText):

    GDI + : TextRenderingHintClearTypeGridFit , GDI : CLEARTYPE_QUALITY :

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

    GDI + : TextRenderingHintAntiAlias , GDI : ANTIALIASED_QUALITY :

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

    GDI + : TextRenderingHintAntiAliasGridFit , GDI : не поддерживается, использует ANTIALIASED_QUALITY :

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

    GDI + : TextRenderingHintSingleBitPerPixelGridFit , GDI : PROOF_QUALITY :

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

    GDI + : TextRenderingHintSingleBitPerPixel , GDI : DRAFT_QUALITY :

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

    я считаю странным, что DRAFT_QUALITY идентичен PROOF_QUALITY , который идентичен CLEARTYPE_QUALITY .

    Смотрите также

    • UseCompatibleTextRendering – Совместим с whaaaaaat?
    • Сортировка всего: Быстрый взгляд на TextRenderer Whidbey
    • MSDN: структура LOGFONT
    • AppCompat Guy: производительность GDI и GDI +
    • GDI + текст, независимость разрешения и методы рендеринга. Или – Почему мой текст выглядит по-разному в GDI + и в GDI?

    Курьер Новый размер 11

    Когда вы создаете Font ‘Courier New’ с размером = 11, вы получите выход, как на изображении выше. Вы видите, что высота составляет 14 пикселей, не включая подчеркивание. Ширина составляет ровно 14 пикселей (7 пикселей для каждого символа).

    Таким образом, этот шрифт отображает 14×14 пикселей.

    Но TextRenderer.MeasureText() возвращает ширину вместо 21 пикселя. Если вам нужны точные значения, это бесполезно.

    Решением является следующий код:

     Font i_Courier = new Font("Courier New", 11, GraphicsUnit.Pixel); Win32.SIZE k_Size; using (Bitmap i_Bmp = new Bitmap(200, 200, PixelFormat.Format24bppRgb)) { using (Graphics i_Graph = Graphics.FromImage(i_Bmp)) { IntPtr h_DC = i_Graph.GetHdc(); IntPtr h_OldFont = Win32.SelectObject(h_DC, i_Courier.ToHfont()); Win32.GetTextExtentPoint32(h_DC, "Áp", 2, out k_Size); Win32.SelectObject(h_DC, h_OldFont); i_Graph.ReleaseHdc(); } } 

    k_Size будет содержать правильный размер: 14×14

    ВАЖНО: этот код правильно определяет правильный шрифт. Если вам нужны точные значения и для курсивных шрифтов (которые всегда имеют свес справа), вы должны прочитать ссылки, упомянутые в этой статье: http://www.codeproject.com/Articles/14915/Width-of-of- текст-в-наклонной-купель

    ПРИЛОЖЕНИЕ: для тех, кто никогда не использовал вызовы API в C # здесь, подсказка о том, как создать class Win32. Это не полно. Для получения дополнительной информации см. http://www.pinvoke.net

     using System.Runtime.InteropServices; public class Win32 { [StructLayout(LayoutKind.Sequential)] public struct SIZE { public int cx; public int cy; } [DllImport("Gdi32.dll")] public static extern bool GetTextExtentPoint32(IntPtr hdc, string lpString, int cbString, out SIZE lpSize); [DllImport("Gdi32.dll")] public static extern IntPtr SelectObject(IntPtr hdc, IntPtr hgdiobj); } 

    Вот объяснение, которое поможет вам понять, как это работает. и что вызывает пробелы более или менее до и после каждого персонажа.

    Приложение GDI DrawString Configurator

    Скриншот

    Давайте будем гением компьютера.