Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to measure width of a string precisely?

Tags:

c#

winforms

gdi+

First of all, question How to measure width of character precisely? which is answered, doesn't really help for this case, so this isn't a duplicate of that.

I have a string. I draw using graphics.DrawString, however when I need to put another one after it, I need to know the precise width of previous string.

For this I use graphics.MeasureString with:

StringFormat format = new StringFormat(StringFormat.GenericTypographic);
format.Alignment = StringAlignment.Center;
format.Trimming = StringTrimming.None;
format.FormatFlags = StringFormatFlags.MeasureTrailingSpaces;

I have tried many other functions, just as TextRendered.MeasureText however all of them fail, with all possible combinations of parameters.

the mentioned combination of MeasureString is most close to what I need (it works in most cases, except for special characters), however using characters like # break it. The width is either shorter or longer.

Is there a way to get a precise size of text produced by DrawString function? How does the DrawString calculate the size of drawing area? It must be clearly some other function because the size always differ.

The source code of whole application is here https://gitorious.org/pidgeon/pidgeon-main/ (File where I work with this, is https://gitorious.org/pidgeon/pidgeon-main/blobs/master/scrollback/SBABox.cs)

like image 753
Petr Avatar asked Jul 29 '12 10:07

Petr


3 Answers

You just need to eliminate extra width. You can do this by using string format:

GdipStringFormatGetGenericTypographic()

You could also use:

float doubleWidth = g.MeasureString(text+text,...).Width;
float singleWidth = g.MeasureString(text).Width;
float textWidth = doubleWidth-singleWidth;

This will allow you to work with other languages such as Japanese.

On codeproject, Pierre Anaud's solution was to use MeasureCharacterRanges, which returns a region matching exactly the bounding box of the specified string:

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);
    var ranges  = 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);
}
like image 91
Chibueze Opata Avatar answered Nov 13 '22 19:11

Chibueze Opata


I'm a little late to the party here, but I was trying to do something similar and stumbled on this question. You've probably already seen the following remark from the documentation for the Graphics.MeasureString method on MSDN:

The MeasureString method is designed for use with individual strings and includes a small amount of extra space before and after the string to allow for overhanging glyphs. Also, the DrawString method adjusts glyph points to optimize display quality and might display a string narrower than reported by MeasureString. To obtain metrics suitable for adjacent strings in layout (for example, when implementing formatted text), use the MeasureCharacterRanges method or one of the MeasureString methods that takes a StringFormat, and pass GenericTypographic. Also, ensure the TextRenderingHint for the Graphics is AntiAlias.

It seems that you were trying to follow this advice because you're using StringFormat.GenericTypographic as a starting point for your custom StringFormat object. However, the line

format.FormatFlags = StringFormatFlags.MeasureTrailingSpaces;

effectively negates the fact that you started with StringFormat.GenericTypographic because it clears any previously set flags. What you probably meant to do is set the StringFormatFlags.MeasureTrailingSpaces flag while preserving the other flags, like so:

format.FormatFlags |= StringFormatFlags.MeasureTrailingSpaces;
like image 35
chess007 Avatar answered Nov 13 '22 21:11

chess007


Try to use this methods:

GDI+ (graphics.MeasureString and graphics.DrawString) >> System.Drawing.Graphics
GDI (TextRenderer.MeasureText and TextRenderer.DrawText)

It also may help you:

Write a custom measure method:

  • Split entry string on special characters
  • Use above .net methods
  • Calculate width of special characters and sum ...

Read Ian Boyd answer

like image 36
Ramin Bateni Avatar answered Nov 13 '22 21:11

Ramin Bateni