为什么Graphics.MeasureString()返回高于预期的数字?

我正在生成收据,并使用Graphics对象来调用DrawString方法来打印出所需的文本。

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

这工作正常,我需要它做的事情。 我总是知道我打印的是什么,所以我可以手工修剪任何琴弦,使它适合于80mm收据纸。 然后,我不得不添加一些额外的function,这将使这更加灵活。 用户可以传入将被添加到底部的string。

由于我不知道他们要放什么,我只是创build了自己的文字包装函数,它接受了许多字符来包装string本身。 为了找出字符的数量,我做了这样的事情:

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

现在宽度正在返回给我283,mm为72,这对于80mm纸张的边距是有意义的。

但是MeasureString方法在Courier New 8pt字体上返回10.5。 所以我没有想到36 – 40的时候,我得到26,导致2行文字变成了3-4。

PrintableArea.Width的单位是1/100英寸,graphics对象的PageUnit是Display(它通常是打印机的1/100英寸)。 那为什么我只能拿回26?

从WindowsClient.net:

GDI +在显示的每个string的每一端添加一小部分(1/6 em)。 这个1/6 em允许带有突出端的字形(比如斜体' f '),同时也给了GDI +less量的余地来帮助扩展网格。

DrawString的默认操作将在显示相邻的运行时对您不利:

  • 首先默认的StringFormat在每个输出的每一端添加额外的1/6 em;
  • 其次,当网格宽度小于devise值时,允许string收缩。

为了避免这些问题:

  • 总是传递MeasureStringDrawString基于印刷StringFormat( GenericTypographic )的StringFormat。
    将Graphics TextRenderingHint设置为TextRenderingHintAntiAlias 。 该渲染方法使用抗锯齿和子像素字形定位来避免对网格拟合的需要,因此其固有地独立于分辨率。

在.NET中绘制文本有两种方法:

  • GDI +( graphics.MeasureStringgraphics.DrawString
  • GDI( TextRenderer.MeasureTextTextRenderer.DrawText

从Michael Kaplan(rip)出色的博客sorting全部出来 ,在.NET 1.1中,一切都使用GDI +进行文本渲染。 但是有一些问题:

  • 有一些性能问题是由于GDI +的某些无状态特性造成的,在这种情况下会设置设备上下文,然后在每次调用之后恢复原来的状态。
  • 对于Windows / Uniscribe和Avalon(Windows Presentation Foundation)来说,国际文本的形状引擎已经被更新了很多次,但是对于GDI +没有更新,这导致了对于新语言的国际渲染支持不具有相同的质量水平。

所以他们知道他们想改变.NET框架来停止使用GDI +的文本渲染系统,并使用GDI 。 起初,他们希望他们可以简单地改变:

 graphics.DrawString 

调用旧的DrawText API而不是GDI +。 但是他们不能使文本换行和间距完全匹配GDI +所做的。 所以他们被迫保持graphics.DrawString来调用GDI +(兼容性的原因;调用graphics.DrawString会突然发现他们的文本没有包装过去的方式)。

创build了一个新的静态TextRenderer类来包装GDI文本呈现。 它有两种方法:

 TextRenderer.MeasureText TextRenderer.DrawText 

注意: TextRenderer是GDI的一个包装,而graphics.DrawString仍然是GDI +的一个包装。


然后是所有现有的.NET控件做什么的问题,例如:

  • Label
  • Button
  • TextBox

他们想切换到使用TextRenderer (即GDI),但他们必须小心。 可能有些人依赖.NET 1.1中的控件绘图。 所以诞生了“ 兼容文本渲染 ”。

默认情况下,应用程序中的控件的行为就像在.NET 1.1中一样(它们是“ 兼容的 ”)。

通过调用以下closures兼容模式:

 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和用于GDI字体绘制的相应LOGFONT质量之间的映射也很有用:

 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 +TextRenderingHintClearTypeGridFitGDICLEARTYPE_QUALITY

在这里输入图像描述

GDI +TextRenderingHintAntiAliasGDIANTIALIASED_QUALITY

在这里输入图像描述

GDI +TextRenderingHintAntiAliasGridFitGDI不支持,使用ANTIALIASED_QUALITY

在这里输入图像描述

GDI +TextRenderingHintSingleBitPerPixelGridFitGDIPROOF_QUALITY

在这里输入图像描述

GDI +TextRenderingHintSingleBitPerPixelGDIDRAFT_QUALITY

在这里输入图像描述

我发现DRAFT_QUALITY是相同的,这和PROOF_QUALITY是一样的。

也可以看看

  • UseCompatibleTextRendering – 与whaaaaaat兼容?
  • sorting全部:快速浏览Whidbey的TextRenderer
  • MSDN:LOGFONT结构
  • AppCompat专家:GDI与GDI +文本呈现性能
  • GDI +文本,分辨率独立性和呈现方法。 或者 – 为什么我的文本在GDI +和GDI中看起来不一样?

快递新尺寸11

当你创build一个Size = 11的字体'Courier New'时,你会得到一个如上图所示的输出。 你看到的高度是14像素不包括下划线。 宽度正好是14像素(每个字符7像素)。

所以这个字体呈现14×14像素。

但是TextRenderer.MeasureText()返回21像素的宽度。 如果你需要确切的值,这是无用的。

解决scheme是以下代码:

 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-文本在斜体的字体

附录:对于那些从来没有在C#中使用API​​调用的人,提示如何创buildWin32类。 这不完整。 有关更多详情,请参阅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); }