如何精确测量字符的宽度?

5
也许我理解有误,但是...我想模拟字符间距。我将单词(文本)分成单个字符列表,测量它们的宽度,然后在位图上一个接一个地绘制它们。我认为,渲染后的文本的整体宽度将与未分割字符串的宽度相同,但似乎有些不对。在循环中渲染字符会显示更宽的结果。是否有任何方法可以获得常见(预期)结果?
以下是代码片段:
private struct CharWidths
{
    public char Char;
    public float Width;
}

private List<CharWidths> CharacterWidths = new List<CharWidths>();

...

private void GetCharacterWidths(string Text, Bitmap BMP)
{
    int i;
    int l = Text.Length;
    CharacterWidths.Clear();
    Graphics g = Graphics.FromImage(BMP);

    CharWidths cw = new CharWidths();
    for (i = 0; i < l; i++)
    {
        Size textSize = TextRenderer.MeasureText(Text[i].ToString(), Font);
        cw.Char = Text[i];
        cw.Width = textSize.Width;
        CharacterWidths.Add(cw);
    }
}

...

public void RenderToBitmap(Bitmap BMP)
{
    //MessageBox.Show("color");

    Graphics g = Graphics.FromImage(BMP);
    GetCharacterWidths("Lyborko", BMP);

    int i;
    float X = 0;
    PointF P = new PointF(); 
    for (i = 0; i < CharacterWidths.Count; i++)
    {
        P.X = X;
        P.Y = 0;

        g.DrawString(CharacterWidths[i].Char.ToString(), Font, Brushes.White, P);

        X = X+CharacterWidths[i].Width;
    }

    P.X = 0;
    P.Y = 30;
    g.DrawString("Lyborko", Font, Brushes.White, P);
    // see the difference
}

非常感谢。

2
一旦涉及到“不寻常”的字符,你的方法就几乎注定要失败了。例如,有些字符是组合字符,在屏幕上呈现为单个符号,需要多个码点来表示。而你的代码也将文本分割成UTF-16的码元,而不是码点。 - CodesInChaos
2
@CodeInChaos 好的,这肯定不是最好的,但我该怎么办? - lyborko
1
这个问题很好地说明了为什么这不起作用:http://stackoverflow.com/q/9945972/17034。测量字符串而不是字符。 - Hans Passant
1个回答

2
首先,应该指出没有一种万能的解决方案,但是对于这个问题有几个建议:
  1. 考虑到您在调用TextRenderer.MeasureText时没有传递当前的设备上下文(您用于绘制字符串的相同上下文),并且知道一个简单的事实,即MeasureText只是在缺少该参数的情况下创建与桌面兼容的新的上下文,并调用DrawTextEx WindowsSDK函数,我建议首先使用MeasureText的重载,在其中指定像第一个参数一样的设备上下文,该上下文用于在之后呈现文本。这可能会产生不同的效果。

  2. 如果失败,我将尝试使用Control.GetPreferredSize方法来猜测控件在屏幕上最适合渲染的尺寸,因此实际上是您未来字符串位图的尺寸。为此,您可以创建一些临时控件,分配一个字符串,进行渲染,然后调用此函数。我清楚地知道这个解决方案可能很难适应您的应用程序架构,但可能会产生更好的结果。

希望这有所帮助。

我尝试按照您的建议指定DeviceContext: Size textSize = TextRenderer.MeasureText(g, Text[i].ToString(), Font); 但是没有发生任何变化... - lyborko
@lyborko:第二个选项怎么样? - Tigran
我没有尝试第二个选项。通过设置TextFlags,我获得了显著的结果,但仍然可以看到差异(虽然是以像素为单位)。请参见:TextFormatFlags flags = TextFormatFlags.NoPadding; Size si = new Size(1, 1); Size textSize = TextRenderer.MeasureText(g, Text[i].ToString(), Font, si, flags); - lyborko

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接