如何测量给定字体/大小的数字的像素宽度(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 应用程序中确定以像素为单位的字体宽度

C# winform如何计算控件上文字的实际宽度(像素)

如何计算一个数字的正确宽度(以像素为单位)?

在 Javascript 中将 em 转换为 px(并获取默认字体大小)

如何计算 JavaFX 中字符串的像素宽度?

字体大小(以像素为单位)