50

我正在生成收据并使用 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 方法在 Courier New 8pt 字体上返回 10.5。因此,我没有绕过我预期的 36 - 40,而是得到 26,导致 2 行文本变成 3-4。

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

4

3 回答 3

162

来自 WindowsClient.net:

GDI+ 将少量(1/6 em)添加到显示的每个字符串的每一端。这个 1/6 em 允许具有悬垂末端的字形(例如斜体“ f ”),并且还给 GDI+ 少量余地以帮助网格拟合扩展。

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

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

为了避免这些问题:

  • 始终传递一个基于印刷 StringFormat ( )MeasureString的StringFormat。 将图形设置为. 这种渲染方法使用抗锯齿和亚像素字形定位来避免网格拟合,因此本质上与分辨率无关。DrawStringGenericTypographic
    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

调用旧DrawTextAPI 而不是 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+ 和用于 GDI 字体绘制TextRenderingHint的相应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)

样品

以下是 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,这与 相同CLEARTYPE_QUALITY

也可以看看

于 2011-06-19T20:00:18.490 回答
9

快递新尺寸 11

当您创建大小为 11 的字体“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();
    }
}

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);
}
于 2014-05-08T04:46:07.987 回答
0

这是一个解释,可以帮助您了解它是如何工作的。以及是什么导致每个字符前后或多或少的空格。

GDI 拉绳配置器应用程序

屏幕截图

于 2018-06-12T08:55:43.597 回答