Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Graphics.MeasureCharacterRanges incorrect when TextRenderingHint.ClearTypeGridFit is enabled

I have an issue in that when I measure the width of a string using the Graphics.MeasureCharacterRanges method the result doesn't match what is displayed.

I have found that this is due to TextRenderingHint.ClearTypeGridFit. When this is set the text is drawn wider. If it is not set the draw text matches the measurement. I require this enabled as it ensures fonts are drawn with the highest of quality.

I need a fast method to evaluate the width of a string. Is the only option remaining to actually draw the string on an image an check pixels for colour?

Is this a bug with the MeasureCharacterRanges method or am I doing something wrong?

NB. The issue seems to be particularly bad with this font. Other fonts, other styles of Arial and other sizes of Arial seem to be much more accurate. I have written a tool to enumerate all fonts/sizes and this is by far the largest discrepancy.

The below code gets the measurement, draws the text and the bounding box for the measurement.

    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

Any questions or issues please let me know.

like image 497
Sam Makin Avatar asked Nov 15 '25 15:11

Sam Makin


1 Answers

I can't tell you what happens internally in GDI+, but I can provide some clarification anyway.

Basically, if you expect a string to draw exactly with the same width as measured, you need to draw it on the same surface and with the same options.

In the original code, Graphics.FromHwnd is used for measurement and internally uses GdipCreateFromHWND to create a drawing surface. Graphics.FromImage instead, used for drawing here, uses GdipGetImageGraphicsContext internally. Even though the .NET properties on the Graphics object look the same, there may still be internal differences in regard to the internal GDI+ drawing surface.

So to simplify your solution, it would actually suffice to use

Dim Im As New Bitmap(500, 100)
g = Graphics.FromImage(Im)

Works fine on my Server 2008 system and Win7, and looks like this:

Result with FromImage()

This is not entirely accurate, the red box does not match the text exactly. This should be expected though, because you are measuring with StringFormat flags and drawing without.

So to improve measurement, use the following line of code for drawing:

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

Then the result looks like this, pretty accurate, on both OSes:

Result with StringFormat flags

On a side note, you can actually use Me.CreateGraphics() instead of Graphics.FromHwnd(IntPtr.Zero) when inside a Control. But it won't fix the problem here.

Does that help?

like image 92
floele Avatar answered Nov 17 '25 08:11

floele



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!