Вопрос по measurement, printdocument, c# – Почему Graphics.MeasureString () возвращает больше ожидаемого числа?

44

Я генерирую квитанцию и использую объект 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 назад?

Ваш Ответ

3   ответа
0

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

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

Скриншот

5

Courier New Size 11

Когда вы создаете шрифт «Новый курьер» с Size = 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

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

APPENDIX: Для тех, кто никогда не использовал вызовы API в C #, здесь есть подсказка, как создать класс 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);
}
Хорошо, у меня просто были проблемы с компиляцией вашего предложения, и Google сказал мне, что эти вызовы функций доступны только в библиотеках C ++, которые я не знал, как использовать в своем коде C #, поэтому я спросил. Благодарю.
ЧТО? Моя ода это C #.
В коде C # вы также можете вызвать собственный API Windows. Но это не меняет того факта, что это код C #.
Странный. Я не смог найти библиотеку для включения через & quot; с использованием & quot; который содержал & quot; GetTextExtentPoint32 (...) & quot ;. Не могли бы вы помочь новичку?
Я думаю, что оригинальный вопрос был C #, похоже, что это ответ C ++
144

Из WindowsClient.net:

GDI+ adds a small amount (1/6 em) to each end of every string displayed. This 1/6 em allows for glyphs with overhanging ends (such as italic 'f'), and also gives GDI+ a small amount of leeway to help with grid fitting expansion.

The default action of DrawString will work against you in displaying adjacent runs:

  • Firstly the default StringFormat adds an extra 1/6 em at each end of each output;
  • Secondly, when grid fitted widths are less than designed, the string is allowed to contract by up to an em.

To avoid these problems:

  • Always pass MeasureString and DrawString a StringFormat based on the typographic StringFormat (GenericTypographic).
    Set the Graphics TextRenderingHint to TextRenderingHintAntiAlias. This rendering method uses anti-aliasing and sub-pixel glyph positioning to avoid the need for grid-fitting, and is thus inherently resolution independent.

Есть два способа рисования текста в .NET:

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

От отличного блога Майкла КапланаСортировка всего этогоВ .NET 1.1 все использовалосьGDI+ для рендеринга текста. Но были некоторые проблемы:

  • There are some performance issues caused by the somewhat stateless nature of GDI+, where device contexts would be set and then the original restored after each call.
  • The shaping engines for international text have been updated many times for Windows/Uniscribe and for Avalon (Windows Presentation Foundation), but have not been updated for GDI+, which causes international rendering support for new languages to not have the same level of quality.

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

graphics.DrawString

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

Новая статикаTextRenderer класс был создан для переноса рендеринга текста GDI. У него есть два метода:

TextRenderer.MeasureText
TextRenderer.DrawText

Note: TextRenderer is a wrapper around GDI, while graphics.DrawString is still a wrapper around GDI+.


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

  • Label
  • Button
  • TextBox

Они хотели переключить их на использованиеTextRenderer (то есть GDI), но они должны были быть осторожными. Могут быть люди, которые зависели от того, как их элементы управления рисовали, как в .NET 1.1. И так родился & quot;compatible text rendering& Quot ;.

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

Выturn off Режим совместимости по телефону:

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 Quality используется для рисования шрифта 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)

Samples

Вот некоторые сравнения рендеринга текста в 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: not supported, uses 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.

See also

Подчеркнутый ответ никогда. Спасибо !!!
Осторожно, здесь TextRenderer.DrawText () не поддерживает прозрачность цвета.

Похожие вопросы