我正在生成收据,并使用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,以mm为单位约为72,这在80mm纸张上占据边距时是有意义的。
但MeasureString方法在Courier New 8pt字体上返回10.5。因此,不是绕过我期望的36 - 40,而是得到26,导致2行文本变成3-4。
PrintableArea.Width的单位是1/100英寸,图形对象的PageUnit是Display(对于打印机来说通常是1/100英寸)。那么为什么我只回来26?
答案 0 :(得分:145)
来自WindowsClient.net:
GDI +在显示的每个字符串的每一端添加少量(1/6 em)。这个1/6 em允许具有悬垂末端的字形(例如斜体“ f ”),并且还为GDI +提供少量余地以帮助进行网格拟合扩展。
DrawString
的默认操作会对您显示相邻的游戏:
- 首先,默认的StringFormat在每个输出的每一端添加额外的1/6 em;
- 其次,当网格适合宽度小于设计值时,允许字符串收缩至最大值。
为了避免这些问题:
- 始终根据印刷字符串格式(
MeasureString
)传递DrawString
和GenericTypographic
字符串格式。
将图形TextRenderingHint
设置为TextRenderingHintAntiAlias
。此渲染方法使用抗锯齿和子像素字形定位,以避免需要网格拟合,因此本质上与分辨率无关。
在.NET中有两种绘制文本的方法:
graphics.MeasureString
和graphics.DrawString
)TextRenderer.MeasureText
和TextRenderer.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
调用旧的DrawText
API而不是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 + TextRenderingHint
与用于GDI字体绘制的相应LOGFONT
Quality之间的映射也很有用:
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
相同。
另见
答案 1 :(得分:5)
当您创建Font' Courier New'如果尺寸= 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
重要:强> 此代码正确测量常规字体。 如果您还需要斜体字体的确切值(右侧总是有一个悬垂),您应该阅读本文中提到的链接:http://www.codeproject.com/Articles/14915/Width-of-text-in-italic-font
<强>附录强> 对于那些从未在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);
}
答案 2 :(得分:0)