Подтвердить что ты не робот

Почему 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 для графического объекта - это дисплей (который, как правило, составляет 1/100-дюймовый дюйм для принтеров). Так почему я только получаю 26 назад?

4b9b3361

Ответ 1

Из WindowsClient.net:

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

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

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

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

  • Всегда передавайте MeasureString и DrawString 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. Он имеет два метода:

TextRenderer.MeasureText
TextRenderer.DrawText

Примечание. TextRenderer является оберткой вокруг 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 (e.g.SystemDefault)    DEFAULT_QUALITY (0)

Примеры

Здесь некоторые сравнения GDI + (graphics.DrawString) стихи GDI (TextRenderer.DrawText):

GDI +: TextRenderingHintClearTypeGridFit, GDI: CLEARTYPE_QUALITY:

enter image description here

GDI +: TextRenderingHintAntiAlias, GDI: ANTIALIASED_QUALITY:

enter image description here

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

enter image description here

GDI +: TextRenderingHintSingleBitPerPixelGridFit, GDI: PROOF_QUALITY:

enter image description here

GDI +: TextRenderingHintSingleBitPerPixel, GDI: DRAFT_QUALITY:

enter image description here

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

См. также

Ответ 2

Courier New Size 11

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

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

Но 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 будет содержать правильный размер: 14x14

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

ПРИЛОЖЕНИЕ: Для тех, кто никогда не использовал вызовы API в С# здесь, подсказка о том, как создать класс 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);
}

Ответ 3

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

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

Скриншот