TextRenderer.MeasureText似乎要舍入字体大小

时间:2019-07-12 00:59:20

标签: .net

据我所知,当您使用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

我做错什么了吗?或者这是预期的行为?

1 个答案:

答案 0 :(得分:2)

TextRenderer.MeasureText()返回的值有关的唯一事情是,知道由TextRenderer.DrawText()渲染文本时的文本大小。因此,问题实际上是:MeasureText()返回的大小是否与调用DrawText()方法时发生的大小匹配?实际上,他们确实这样做:

sample text, drawn four different sizes, with and without anti-aliasing, with TextRenderer and with Graphics.DrawString

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

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

对于所使用的两种渲染方法中的每一种,实际上都会进行两次渲染,一次是使用Graphics对象的默认设置,然后是将Graphics.TextRenderingHint设置为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;
}