Click here to Skip to main content
Click here to Skip to main content

Bypass Graphics.MeasureString limitations

By , 22 Apr 2003
 

Sample Image - measurestring.gif

Introduction

Graphics.MeasureString can be used to compute the height and width of a text string. Often, however, the dimensions returned do not match the size of what gets drawn on screen when calling Graphics.DrawString. The red box above shows the dimensions returned by Graphics.MeasureString, which is about an em too wide...

The differences between what is computed and what is really drawn on the screen are related to how GDI+ computes its widths when using hinting and antialiasing. Here are the  gory details. A known work-around is to make GDI+ display its string antialiased, in which case the measured width matches the displayed result. If you want to draw standard strings (to match the GUI appearance, for instance), you are left out.

First, naive solution

The code I present here can be inserted into any class which needs to compute the real width of a string (shown by the yellow background above). The trick I use to compute the real string width is to ask GDI+ to draw the string into a bitmap and then find the position of the last character by reading back the pixels. A few optimisations ensure that this gets done as fast as possible (small bitmap, few pixels).

static public int MeasureDisplayStringWidth(Graphics graphics, string text,
                                            Font font)
{
    const int width = 32;

    System.Drawing.Bitmap   bitmap = new System.Drawing.Bitmap (width, 1, <BR>                                                                graphics);
    System.Drawing.SizeF    size   = graphics.MeasureString (text, font);
    System.Drawing.Graphics anagra = System.Drawing.Graphics.FromImage(bitmap);

    int measured_width = (int) size.Width;

    if (anagra != null)
    {
        anagra.Clear (Color.White);
        anagra.DrawString (text+"|", font, Brushes.Black,
                           width - measured_width, -font.Height / 2);

        for (int i = width-1; i >= 0; i--)
        {
            measured_width--;
            if (bitmap.GetPixel (i, 0).R != 255)    // found a non-white pixel ?
                break;
        }
    }

    return measured_width;
}

That's all, folks. Right-to-left scripts won't probably work with this piece of code.

Another solution...

It is also possible to get the accurate string geometry by using MeasureCharacterRanges, which returns a region matching exactly the bounding box of the specified string. This is faster and more elegant than the first solution I posted on CodeProject.

static public int MeasureDisplayStringWidth(Graphics graphics, string text,
                                            Font font)
{
    System.Drawing.StringFormat format  = new System.Drawing.StringFormat ();
    System.Drawing.RectangleF   rect    = new System.Drawing.RectangleF(0, 0,
                                                                  1000, 1000);
    System.Drawing.CharacterRange[] ranges  = <BR>                                       { new System.Drawing.CharacterRange(0, 
                                                               text.Length) };
    System.Drawing.Region[]         regions = new System.Drawing.Region[1];

    format.SetMeasurableCharacterRanges (ranges);

    regions = graphics.MeasureCharacterRanges (text, font, rect, format);
    rect    = regions[0].GetBounds (graphics);

    return (int)(rect.Right + 1.0f);
}

Post Script

Both functions only work with non-empty strings. The second solution will strip the trailing spaces; the first solution will take them in account. Choose the one which best fits your needs

License

This article, along with any associated source code and files, is licensed under The GNU Lesser General Public License (LGPLv3)

About the Author

Pierre Arnaud
Web Developer
Switzerland Switzerland
Member

Pierre Arnaud got a Ph.D. in computer science at the Swiss Federal Institute of Technology; he currently works both as an independent contractor on hardware and software projects at OPaC bright ideas and as a senior software designer at EPSITEC.

Pierre was a key contributor to the Smaky computer, a real time, multitasking system based on the Motorola 680x0 processor family.

Now, Pierre works on his spare time for the Creative Docs .NET project: it is a vector based drawing and page layout software based on .NET and AGG.


Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralMy vote of 5memberMember 915697628 Aug '12 - 5:23 
Just what I was looking for
GeneralMy vote of 5memberSweegle21 Jul '12 - 9:12 
Works great
GeneralThanks!memberAlexandru Pupsa10 Jul '12 - 21:35 
Smile | :)
GeneralMy vote of 5membermanoj kumar choubey18 Feb '12 - 3:28 
Nice
QuestionDoes it measure only width?memberTushar Trivedi16 Mar '10 - 20:09 
I tried to use this function to get the height. I have checked the height of the region that is derived from this function and it gives same height what MeasureString() gives, no difference. So I'm wondering if it gives only correct width and not the height?
AnswerRe: Does it measure only width?memberPierre Arnaud16 Mar '10 - 20:22 
As its name implies (MeasureStringWidth), this function is only useful to measure the width of the string. If you are using a language with vertical layout or if you need to compute the height of a set of characters, you'll have to rewrite one of the two variants, based on your preferences.
GeneralHey, this article has been pointed to by Patrick Smacchia (MVP C#, NDepend author)memberPierre Arnaud23 Sep '09 - 8:36 
See the full article about how you can use NDepend to find out how other .NET applications work, and learn about, say, MeasureCharacterRanges.
QuestionCalculating height?memberfuzzek2 Sep '08 - 23:32 
Hi there, I really found this helpful so thanks a lot. I have an issue, because I have certain width I can paint on... Then I need height to know where string finished. Probably this is really easy, but just cannot get it.
AnswerRe: Calculating height?memberPierre Arnaud3 Sep '08 - 1:17 
If you want to know the height, the easiest is to rely on what the call to
graphics.MeasureString
returns. This is not the exact height occupied by the bits painted on screen, but rather the maximum possible height that the text might occupy.
GeneralRe: Calculating height?memberRoland Li15 Sep '09 - 23:29 
This method has a nasty bug that when the Font is set to bold the measure result will be wrong.
 

http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=115410[^]

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web04 | 2.6.130523.1 | Last Updated 23 Apr 2003
Article Copyright 2002 by Pierre Arnaud
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid