首页 > 解决方案 > TextRenderer.MeasureText 似乎要四舍五入字体大小

问题描述

据我所知,当你使用TextRenderer.MeasureText查找字符的宽度时,在我的情况下是固定宽度CourierNew字符,当字体大小具有小数部分时,我会得到奇怪的结果,例如 11.3 而不是 11.0 或 12.0。

例如

Dim font As Font = New Font("Courier New", emSize:=11.3)
Dim width As Double = TextRenderer.MeasureText(New String("0"c, 1000), font).Width / 1000

(我正在平均一个长字符串的长度以考虑填充。)

结果

emSize = 11.0 -> width = 9.008
emSize = 12.0 -> width = 10.008
emSize = 11.3 -> width = 10.008
emSize = 11.7 -> width = 10.008

我做错了什么还是这是预期的行为?

标签: .net

解决方案


唯一与返回的值TextRenderer.MeasureText()相关的是,当文本由TextRenderer.DrawText(). 所以,真正的问题是:当方法被调用时,返回的大小是否MeasureText()与发生的匹配。DrawText()事实上,他们确实:

示例文本,使用 TextRenderer 和 Graphics.DrawString 绘制了四种不同大小,带和不带抗锯齿

以上是一个简单程序(下面的代码)的输出,该程序以您所询问的四种不同字体大小、四种不同方式呈现十位数字的示例字符串。前两组使用 渲染TextRenderer.DrawText(),后两组使用 渲染Graphics.DrawString()

请注意,使用 时TextRenderer,四种尺寸中的三种呈现完全相同。因此,当然,当您测量文本时,测量的宽度也将完全相同。

对于使用的两种渲染方法中的每一种,实际上都会进行两次渲染,一次使用Graphics对象的默认设置,然后再次使用Graphics.TextRenderingHintset to AntiAlias。有趣的是,至少在我的电脑上,虽然AntiAlias设置了让TextRenderer输出看起来更好,但使用该Graphics.DrawString()方法时它看起来更糟(事实上,它看起来很像TextRenderer设置为默认值时的输出SystemDefault)。

更重要的是,请注意,虽然TextRenderer不随小数字体大小缩放,Graphics.DrawString()但可以。这两种渲染方式有着根本的不同。如果你想要小数字体大小,你需要使用支持它的渲染方法。TextRenderer不是吗。


代码示例:
在 Visual Studio 中一个全新的 Windows 窗体项目中,您可以将这些成员添加到Form1类中,以生成上面的输出:

private readonly IReadOnlyList<float> _emSizes = new[] { 11.0f, 11.3f, 11.7f, 12.0f };
private const string _text = "0123456789";

protected override void OnPaint(PaintEventArgs e)
{
    base.OnPaint(e);

    int y = _DrawTextExamples(e.Graphics, _text, 0, _emSizes) + 10;

    e.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
    y = _DrawTextExamples(e.Graphics, _text, y, _emSizes) + 10;
    e.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.SystemDefault;
    y = _DrawTextExamplesWithDrawString(e.Graphics, _text, y, _emSizes) + 10;
    e.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
    y = _DrawTextExamplesWithDrawString(e.Graphics, _text, y, _emSizes) + 10;
}

private int _DrawTextExamples(Graphics g, string text, int y, IReadOnlyList<float> emSizes)
{
    Point pt = new Point(0, y);

    foreach (float emSize in emSizes)
    {
        using (Font font = new Font("Courier New", emSize))
        {
            Size size = TextRenderer.MeasureText(g, text, font);
            TextRenderer.DrawText(g, text, font, pt, Color.Black);
            pt.X += size.Width + 20;
            TextRenderer.DrawText(g, $"emSize: {emSize}, width: {size.Width}", font, pt, Color.Black);
            pt = new Point(0, pt.Y + size.Height);
        }
    }

    return pt.Y;
}

private int _DrawTextExamplesWithDrawString(Graphics g, string text, int y, IReadOnlyList<float> emSizes)
{
    PointF pt = new Point(0, y);

    foreach (float emSize in emSizes)
    {
        using (Font font = new Font("Courier New", emSize))
        {
            SizeF size = g.MeasureString(text, font);
            g.DrawString(text, font, Brushes.Black, pt);
            pt.X += size.Width + 20;
            g.DrawString($"emSize: {emSize}, width: {size.Width}", font, Brushes.Black, pt);
            pt = new PointF(0, pt.Y + size.Height);
        }
    }

    return (int)pt.Y + 1;
}

推荐阅读