为什么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收缩。
为了避免这些问题:
- 总是传递
MeasureString
和DrawString
基于印刷StringFormat(GenericTypographic
)的StringFormat。
将GraphicsTextRenderingHint
设置为TextRenderingHintAntiAlias
。 该渲染方法使用抗锯齿和子像素字形定位来避免对网格拟合的需要,因此其固有地独立于分辨率。
在.NET中绘制文本有两种方法:
- GDI +(
graphics.MeasureString
和graphics.DrawString
) - GDI(
TextRenderer.MeasureText
和TextRenderer.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 + : 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
是一样的。
也可以看看
- UseCompatibleTextRendering – 与whaaaaaat兼容?
- sorting全部:快速浏览Whidbey的TextRenderer
- MSDN:LOGFONT结构
- AppCompat专家:GDI与GDI +文本呈现性能
- GDI +文本,分辨率独立性和呈现方法。 或者 – 为什么我的文本在GDI +和GDI中看起来不一样?
当你创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); }