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

Bra*_*don 46 c# printdocument measurement

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

graphics.DrawString(string, font, brush, widthOfPage / 2F, yPoint, stringformat);
Run Code Online (Sandbox Code Playgroud)

这适用于我需要它做的事情.我总是知道我要打印出来的东西,所以我可以手动修剪任何琴弦,这样它就可以正确地放在80毫米的收据纸上.然后我不得不添加额外的功能,这将使这更灵活.用户可以传入将添加到底部的字符串.

由于我不知道他们要放什么,我只是创建了自己的自动换行功能,它包含了许多字符来包装和字符串本身.为了找出字符数,我做了这样的事情:

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

现在宽度正在返回283,以mm为单位约为72,这在80mm纸张上考虑边距时是有意义的.

但MeasureString方法在Courier New 8pt字体上返回10.5.因此,不是绕过我期望的36 - 40,我得到26,导致2行文本变成3-4.

PrintableArea.Width的单位是1/100英寸,图形对象的PageUnit是Display(对于打印机来说通常是1/100英寸).那么为什么我只回来26?

Ian*_*oyd 149

来自WindowsClient.net:

GDI +在显示的每个字符串的每一端添加少量(1/6 em).这个1/6 em允许具有悬垂末端的字形(例如斜体" f "),并且还为GDI +提供少量余地以帮助进行网格拟合扩展.

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

  • 首先,默认的StringFormat在每个输出的每一端添加额外的1/6 em;
  • 其次,当网格适合宽度小于设计时,允许字符串收缩到em.

为了避免这些问题:

  • 总是传递MeasureStringDrawString基于排版的StringFormat一个的StringFormat( GenericTypographic).
    将图形设置TextRenderingHintTextRenderingHintAntiAlias.该渲染方法使用抗锯齿和子像素字形定位来避免对网格拟合的需要,因此本质上是分辨率独立的.

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

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

来自Michael Kaplan(rip)优秀博客Sorting It All Out,在.NET 1.1中,所有内容都使用GDI +进行文本渲染.但是有一些问题:

  • 由于GDI +的某种无状态特性会导致一些性能问题,其中将设置设备上下文,然后在每次调用之后恢复原始设备上下文.
  • 用于Windows/Uniscribe和Avalon(Windows Presentation Foundation)的国际文本的整形引擎已经多次更新,但尚未针对GDI +进行更新,这导致对新语言的国际渲染支持不具有相同的质量水平.

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

graphics.DrawString
Run Code Online (Sandbox Code Playgroud)

调用旧DrawTextAPI而不是GDI +.但它们无法使文本包装和间距与GDI +完全匹配.所以他们不得不继续graphics.DrawString打电话给GDI +(兼容原因;那些打电话的人graphics.DrawString会突然发现他们的文字没有像过去那样包装).

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

TextRenderer.MeasureText
TextRenderer.DrawText
Run Code Online (Sandbox Code Playgroud)

注意: TextRenderer是GDI的包装器,同时graphics.DrawString仍然是GDI +的包装器.


然后是如何处理所有现有.NET控件的问题,例如:

  • Label
  • Button
  • TextBox

他们希望将它们切换到使用TextRenderer(即GDI),但他们必须小心.可能有人依赖于他们的控件绘制,就像在.NET 1.1中那样.因此诞生了" 兼容的文本渲染 ".

默认情况下,应用程序中的控件的行为与它们在.NET 1.1中的行为相同(它们是" 兼容的 ").

您通过调用以下命令关闭兼容模式:

Application.SetCompatibleTextRenderingDefault(false);
Run Code Online (Sandbox Code Playgroud)

这使您的应用程序更好,更快,具有更好的国际支持.总结一下:

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
Run Code Online (Sandbox Code Playgroud)

注意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 (e.g.SystemDefault)    DEFAULT_QUALITY (0)
Run Code Online (Sandbox Code Playgroud)

样品

这里是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_QUALITYPROOF_QUALITY,它是相同的,与...相同CLEARTYPE_QUALITY.

也可以看看

  • 小心TextRenderer.DrawText()不支持颜色透明度. (3认同)

Elm*_*mue 7

快递新尺寸11

当您使用Size = 11创建Font'Courier New'时,您将获得如上图所示的输出.您会看到高度为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();
    }
}
Run Code Online (Sandbox Code Playgroud)

k_Size将包含正确的大小:14x14

重要说明: 此代码正确测量常规字体.如果您还需要斜体字体的确切值(右侧总是有一个悬垂),您应该阅读本文中提到的链接:http://www.codeproject.com/Articles/14915/Width-of-文本在斜体的字体

附录: 对于那些从未在C#中使用过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);
}
Run Code Online (Sandbox Code Playgroud)

  • 什么?我的颂歌是C#。 (3认同)