C# – How to determine maximum number of characters given a fixed width font and a maximum width in pixels

cgdi+net

Given a number of pixels (say: 300) and a Font (fixed type, fixed size, like Consolas), how could I determine the maximum number of characters that I could draw with GDI+?

The text won't be written to a Label or such, but drawn using GDI+:

public void DrawString( string s, Font font, Brush brush, 
    RectangleF layoutRectangle, StringFormat format );

But I want to perform certain text operations before drawing, hence why I'd like to figure out the maximum number of chars I can safely output.

Best Answer

The System.Drawing.Graphics method MeasureString pads string widths with a few extra pixels (I do not know why), so measuring the width of one fixed-length character and then dividing that into the maximum width available would give a too-low estimate of the number of characters that could fit into the maximum width.

To get the maximum number of characters, you'd have to do something iterative like this:

using (Graphics g = this.CreateGraphics())
{
    string s = "";
    SizeF size;
    int numberOfCharacters = 0;
    float maxWidth = 50;
    while (true)
    {
        s += "a";
        size = g.MeasureString(s, this.Font);
        if (size.Width > maxWidth)
        {
            break;
        }
        numberOfCharacters++;
    }
    // numberOfCharacters will now contain your max string length
}

Update: you learn something new every day. Graphics.MeasureString (and TextRenderer) pad the bounds with a few extra pixels to accomodate overhanging glyphs. Makes sense, but it can be annoying. See:

http://msdn.microsoft.com/en-us/library/6xe5hazb.aspx

Looks like a better way to do this is:

using (Graphics g = this.CreateGraphics())
{
    g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
    SizeF size = g.MeasureString("a", this.Font, new PointF(0, 0), 
        StringFormat.GenericTypographic);
    float maxWidth = 50; // or whatever
    int numberOfCharacters = (int)(maxWidth / size.Width);
}