如何测量给定字体/大小的数字的像素宽度(C#)
Posted
技术标签:
【中文标题】如何测量给定字体/大小的数字的像素宽度(C#)【英文标题】:How to measure the pixel width of a digit in a given font / size (C#) 【发布时间】:2010-12-22 10:17:04 【问题描述】:我正在尝试使用 OpenXML 规范中的官方公式计算 Excel 列的像素宽度,如this post 中所述。但是,为了应用这个公式,我需要知道 Normal 字体的 Maximum Digit Width,即最宽数字的像素宽度。 OpenXML 规范给出了这个例子作为说明:
以Calibri字体为例,11点字体的最大位宽 大小为 7 像素(96 dpi)。
我已经通过目视检查 Calibri 11 点数字来检查这是否正确,它确实是 7 像素宽。因此,我正在尝试创建一种方法,该方法将返回任何字体/大小的最大数字宽度。
我遵循了this question 中的建议,但并没有产生我期望的结果。
这是我的测试代码:
var font = new Font("Calibri", 11.0f, FontStyle.Regular);
for (var i = 0; i < 10; i++)
Debug.WriteLine(TextRenderer.MeasureText(i.ToString(), font));
这报告所有数字的宽度都是 15。
有什么建议吗?
谢谢, 蒂姆
【问题讨论】:
【参考方案1】:MeasureText 添加了内边距,尽管它与字体大小大致呈线性关系,但其数量是相当不可预测的。诀窍是减去两个测量值以消除填充。这段代码生成了非常开心的数字:
float size = 5;
for (int ix = 0; ix < 10; ++ix)
var font = new Font("Calibri", size, FontStyle.Regular);
string txt = new string('0', 100);
SizeF sz1 = TextRenderer.MeasureText("00", font) - TextRenderer.MeasureText("0", font);
Console.WriteLine("0 1:N3", size, sz1.Width);
size += 2;
输出:
5 4.000
7 5.000
9 6.000
11 7.000
13 9.000
15 10.000
17 12.000
19 13.000
21 14.000
23 16.000
这将为您提供所需的 7 个像素。请注意,您必须适应 TrueType 提示,它可以调整数字的形状,使其主干落在像素边界上。添加至少一个像素。
【讨论】:
【参考方案2】:首先让我说,我将要展示的代码看起来像一个可怕的 hack,我不会把它作为解决方案来展示。相反,我希望它能够更多地揭示 MeasureText 的行为,这可能会引导您找到最终解决方案。
我对您的代码做了些许改动。我正在测量两个下划线字符的长度。然后,在 for 循环的主体中,我正在测量所需的字符,两边都有下划线,并单独减去两个下划线字符的长度。对于测试场景,我得到的结果为 7。这样做的主要目的是确定 MeasureText 是在逐个字符的基础上进行填充,还是在字符串的开头/结尾处进行填充。似乎是后者。也许这与您收到的其他一些意见相结合,将引导您找到更优雅的解决方案。
var font = new Font("Calibri", 11.0f, FontStyle.Regular);
int underscoreWidth = TextRenderer.MeasureText("__", font).Width;
for (var i = 0; i < 10; i++)
int charWidth = TextRenderer.MeasureText(String.Concat("_", i.ToString(), "_"), font).Width - underscoreWidth;
Debug.WriteLine(charWidth);
【讨论】:
感谢您的解决方案。正如你所说,这有点不正统,但它实际上产生了正确的结果,这比我在过去四个小时内取得的成绩要多:)。我的应用程序的第二个好处是它不需要 Graphics 对象就可以做到这一点。再次感谢。【参考方案3】:测量文本宽度可能是个噩梦……我不知道为什么它们会让人如此困惑……
这是我测量文本长度的最准确方法:
protected int _MeasureDisplayStringWidth ( Graphics graphics, string text, Font font )
if ( text == "" )
return 0;
StringFormat format = new StringFormat ( StringFormat.GenericDefault );
RectangleF rect = new RectangleF ( 0, 0, 1000, 1000 );
CharacterRange[] ranges = new CharacterRange ( 0, text.Length ) ;
Region[] regions = new Region[1];
format.SetMeasurableCharacterRanges ( ranges );
format.FormatFlags = StringFormatFlags.MeasureTrailingSpaces;
regions = graphics.MeasureCharacterRanges ( text, font, rect, format );
rect = regions[0].GetBounds ( graphics );
return (int)( rect.Right );
【讨论】:
@Fungus:谢谢,您的代码最接近正确答案,将 7 像素数字报告为 8 像素宽。不幸的是,在我的应用程序中,1 个像素的错误会放大到更大的错误。此外,似乎 Font 构造函数的参数会影响结果。有没有正确的方法来构造一个 Font 对象来表示 11pt Calibri? 嗯...这可能会返回 1 像素宽,因为它正在测量字符占用的空间,然后会考虑字符周围的边框。【参考方案4】:TextRenderer 并不总是像您期望的那样准确:
来自 MSDN:http://msdn.microsoft.com/en-us/library/8wafk2kt.aspx
例如,TextRenderer 的默认行为是向绘制文本的边界矩形添加填充以适应悬垂的字形
您有Graphics
对象或控制对象吗?如果您使用这些,您可以按照您期望的方式获得准确的结果。
看看http://msdn.microsoft.com/en-us/library/6xe5hazb(VS.80).aspx
【讨论】:
小心 MeasureString - 它还填充:“MeasureString 方法设计用于单个字符串,并在字符串前后包含少量额外空间,以允许悬垂字形。”此外,请注意,测量单个字形可能无法在上下文之外给出准确的结果。由于字距调整,字符串的字符宽度之和并不总是字符串的宽度。 @plinth:是的,我使用 MeasureString 尝试了我的代码,它报告 7 像素数字的宽度为 9.408365 像素。【参考方案5】:我不能 100% 确定它是您真正需要的。
但您可能想看看这个 Microsoft 页面:
http://msdn.microsoft.com/en-us/library/xwf9s90b.aspx【讨论】:
以上是关于如何测量给定字体/大小的数字的像素宽度(C#)的主要内容,如果未能解决你的问题,请参考以下文章
如何在远程桌面的 Swing 应用程序中确定以像素为单位的字体宽度