启用TextRenderingHint.ClearTypeGridFit时,Graphics.MeasureCharacterRanges不正确

时间:2014-03-17 16:45:46

标签: .net vb.net winforms graphics

我遇到的问题是,当我使用Graphics.MeasureCharacterRanges方法测量字符串的宽度时,结果与显示的内容不匹配。

我发现这是由TextRenderingHint.ClearTypeGridFit引起的。设置此项后,文本将被绘制得更宽。如果未设置,则绘制文本与测量匹配。我要求启用此功能,因为它可确保以最高质量绘制字体。

我需要一种快速方法来评估字符串的宽度。唯一的选择是在图像上实际绘制字符串,检查颜色的像素吗?

这是MeasureCharacterRanges方法的错误还是我做错了什么?

NB。这个字体似乎特别糟糕。其他字体,其他Arial风格和其他尺寸的Arial似乎更准确。我写了一个工具来枚举所有字体/大小,这是迄今为止最大的差异。

以下代码获取测量值,绘制文本和测量的边界框。

    Dim g As Graphics = Nothing
    Dim text As String = "MSCI EMU Net"
    Dim font As Font = New Font("Arial", 8, FontStyle.Regular)
    Dim wordwrapwidth As Single = Single.MaxValue

    g = Graphics.FromHwnd(IntPtr.Zero)
    g.PageUnit = GraphicsUnit.Pixel
    g.TextContrast = 2
    g.TextRenderingHint = Drawing.Text.TextRenderingHint.ClearTypeGridFit

    Dim ranges As System.Drawing.CharacterRange() = {New System.Drawing.CharacterRange(0, text.Length)}
    Dim regions As System.Drawing.Region() = New System.Drawing.Region(0) {}

    Dim STRFormat As StringFormat = StringFormat.GenericTypographic.Clone
    STRFormat.SetTabStops(0, New Single() {72})
    STRFormat.SetMeasurableCharacterRanges(ranges)

    regions = g.MeasureCharacterRanges(text, font, New System.Drawing.RectangleF(0, 0, wordwrapwidth, Single.MaxValue), STRFormat)

    Dim tempRectF As RectangleF = regions(0).GetBounds(g)


    Dim textImage As New Bitmap(500, 100)
    Dim textImageGraphics As Graphics = Graphics.FromImage(textImage)
    textImageGraphics.TextContrast = 2
    textImageGraphics.TextRenderingHint = Drawing.Text.TextRenderingHint.ClearTypeGridFit
    textImageGraphics.Clear(Color.White)
    textImageGraphics.DrawString(text, font, Brushes.Black, New Point(0, 0))
    textImageGraphics.DrawRectangle(Pens.Red, Rectangle.Round(tempRectF))
    PictureBox1.Image = textImage

如有任何问题或疑问,请告知我们。

2 个答案:

答案 0 :(得分:1)

我无法告诉你GDI +内部会发生什么,但无论如何我都可以提供一些澄清。

基本上,如果您希望字符串与测量的宽度完全相同,则需要在相同的表面上绘制并使用相同的选项。

在原始代码中,Graphics.FromHwnd用于测量,并在内部使用GdipCreateFromHWND来创建绘图表面。相反,Graphics.FromImage用于绘图,在内部使用GdipGetImageGraphicsContext。即使Graphics对象上的.NET属性看起来相同,但内部GDI +绘图表面仍然存在内部差异。

因此,为了简化您的解决方案,使用

就足够了
Dim Im As New Bitmap(500, 100)
g = Graphics.FromImage(Im)

在我的Server 2008系统和Win7上工作正常,看起来像这样:

Result with FromImage()

这不完全准确,红框与文字完全不符。这应该是预期的,因为您使用 StringFormat标志测量并在没有的情况下绘制

因此,为了改善测量,请使用以下代码行进行绘制:

textImageGraphics.DrawString(text, font, Brushes.Black, New Point(0, 0), STRFormat)

然后在两个操作系统上结果看起来都非常准确:

Result with StringFormat flags

另外,在Me.CreateGraphics()内部,您实际上可以使用Graphics.FromHwnd(IntPtr.Zero)代替Control。但它不会在这里解决问题。

这有帮助吗?

答案 1 :(得分:0)

解决方案是不使用

g = Graphics.FromHwnd(IntPtr.Zero)

如果用它替换它可以正常工作:

Dim resolution As Single = 96
Dim Im As New Bitmap(500, 100)
Im.SetResolution(resolution, resolution)
g = Graphics.FromImage(Im)

我不明白为什么。我已经对图形对象进行了比较,无法确定可以解释它的差异。

Bounty将会找到可以告诉我原因的人。