Measure a String without using a Graphics object?

Go To StackoverFlow.com

39

I am using pixels as the unit for my font. In one place, I am performing a hit test to check if the user has clicked within the bounding rectangle of some text on screen. I need to use something like MeasureString for this. Unfortunately, the code doing the hit test is deep within a library which does not have access to a Graphics object or even a Control.

How do I get the bounding box of a string given the font without using the Graphics class? Why do I even need a Graphics object when my font is in pixels?

2009-06-16 19:03
by Agnel Kurian
What do you have if you don't have the Control? I'm assuming Font and the string, but is there anything else - micahtan 2009-06-16 19:09
Nothing else. My library is a kind of a Scenegraph. I am trying to avoid dependencies on System.Drawing and System.Windows.Form - Agnel Kurian 2009-06-17 04:29


46

If you have a reference to System.Windows.Forms, try using the TextRenderer class. There is a static method (MeasureText) which takes the string and font and returns the size. MSDN Link

2009-06-16 19:17
by Jarrod
Slight problem with TextRenderer is that it uses integers for the return value, which can cause problems if you need sub-pixel precision. Otherwise, it's a good alternative - Raymond Martineau 2010-09-01 03:45
How is this the solution? The example in the link shows TextRenderer.DrawText(e.Graphics,jp2code 2012-10-03 19:01
@jp2code - Did you even read my answer? He asked how to measure a string without using the Graphics class. I suggested the MeasureText method on the TextRenderer class. It answers his question and I even provided the link to the MSDN documentation for this class. Take another look - Jarrod 2012-10-05 04:02
But, the TextRenderer's DrawText method's first parameter is Graphics. Am I missing something - jp2code 2012-10-05 13:31
Hmmm... on closer inspection of the MeasureText example, I see that static call you talk about. I wonder if that method is a wrapper for NerdFury's answer below - jp2code 2012-10-05 13:38
@jp2code don't know if you still care but MeaureText uses an internal class WindowsGraphicsCacheManager. The manager has a static MeasurementGraphics property which gets inited on its first use. It does this by creating a graphics from the device context with a PInvoke to the gdi32.dll using this constructor. NerdFury's answer eventually uses this constructor and of course the Graphics isn't cached but otherwise they're the sam - Conrad Frix 2013-02-26 20:58
This works splendidly and doesnt even require an object to be created - bwoogie 2015-12-19 03:29
I like this except that it requires a dependency on System.Windows.Forms just to measure a string, which might have nothing to do with forms - Dave Cousineau 2018-08-23 21:36
@DaveCousineau That wasn't much of a problem in 2011 when I wrote this answer. Nearly all scenarios where this question might come up, a person would have had a reference to Forms - Jarrod 2018-10-26 19:26
@Jarrod well, I like to keep my forms related code separate from my drawing related code, and separate from code that has no dependencies except C#, etc. maybe I'm rendering text to an image or something in a command-line program or service or something. no reason that I should need a reference to windows forms, except that TextRenderer is there for some reason instead of in System.Drawing - Dave Cousineau 2018-10-26 23:29


23

You don't need to use the graphics object that you are using to render to do the measuring. You could create a static utility class:

public static class GraphicsHelper
{
    public static SizeF MeasureString(string s, Font font)
    {
        SizeF result;
        using (var image = new Bitmap(1, 1))
        {
            using (var g = Graphics.FromImage(image))
            {
                result = g.MeasureString(s, font);
            }
        }

        return result;
    }
}

It might be worthwile, depending on your situation to set the dpi of the bitmap as well.

2009-06-16 19:26
by NerdFury
Looks good, but I dont think new Bitmap(0, 0) is allowed, maybe (1,1 - Alex Nolasco 2010-05-18 04:42
@AlexanderN I just confirmed that Bitmap(0, 0) is invalid. Your solution of Bitmap(1, 1) is correct - Mark Bouchard 2011-11-03 14:40
image object could be an static to avoid recreated the object each tim - dlopezgonzalez 2016-01-22 10:47
Use Graphics.FromHwnd(IntPtr.Zero) to avoid creating bitmap - Raj 2017-05-25 19:51


10

MeasureString method in @NerdFury answer will give a higher string width than expected.Additional info you can find here. If you want to measure only the physical length please add this two lines :

g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
result = 
  g.MeasureString(measuredString, font, int.MaxValue, StringFormat.GenericTypographic);
2012-11-08 10:51
by Anton Putov


1

This example does great job of illustrating the use of FormattedText. FormattedText provides low-level control for drawing text in Windows Presentation Foundation (WPF) applications. You can use it to measure the Width of a string with a particular Font without using a Graphics object.

public static float Measure(string text, string fontFamily, float emSize)
{
    FormattedText formatted = new FormattedText(
        item, 
        CultureInfo.CurrentCulture, 
        System.Windows.FlowDirection.LeftToRight, 
        new Typeface(fontFamily), 
        emSize, 
        Brushes.Black);

    return formatted.Width;
}

Include WindowsBase and PresentationCore libraries.

2013-06-17 20:17
by Jacob Raines


0

This may not be but a repeat of others, but my problem was the unwanted Graphics object as well. After listening to the above frustrations I simply tried:

Size proposedSize = new Size(int.MaxValue, int.MaxValue);
TextFormatFlags flags = TextFormatFlags.NoPadding;
Size ressize = TextRenderer.MeasureText(content, cardfont, proposedSize, flags);

(where 'content' is the string to measure and cardfont the font it is in)

... and was in luck. I could use the result to set the width of my column in VSTO.

2015-09-26 18:13
by Peter Høgstedt
Ads