4

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.

MusiGenesis
  • 74,184
  • 40
  • 190
  • 334
Martin Marconcini
  • 26,875
  • 19
  • 106
  • 144

7 Answers7

6

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);
}
MusiGenesis
  • 74,184
  • 40
  • 190
  • 334
  • Thanks for the info, I ended up using something very close to this, although with Consolas font, the "numberOfCharacters" returned, depending upon the char you use. Eg: with an "a" will return 50, if I use " ", it will return a different number. I thought Consolas was Fixed Font - Monospaced ? – Martin Marconcini Oct 15 '09 at 11:42
  • Finally I Get equal values of width for different characters with monospaced font BUT, `" " - space` give me negative value `-3.813766E-7` which is strange. Any advice? – Gondil Feb 21 '17 at 16:03
  • @Gondil: that is a weird outcome, but does it matter since you're using a monospaced font? You could just measure the width of "m" (for example) and use that as the width for all characters (including spaces). – MusiGenesis Feb 21 '17 at 18:38
  • @MusiGenesis Yes of course but it is just one case, what If I get text and not monospaced font. But I already figured it out for spaces here http://stackoverflow.com/a/42373508/3625461. Problem is still with tabs for which I get something very close to zero. – Gondil Feb 21 '17 at 19:13
3

There is also TextRenderer.MeasureText(), which produces a different result (this is what is used when drawing windows controls, so it is generally more accurate).

There is a discussion on SO somewhere about it, but I cannot find it at the moment.

Edit: This MSDN Article goes a little more in depth.

Pondidum
  • 11,457
  • 8
  • 50
  • 69
1

I read few sites and come up with solution as,

using (System.Drawing.Graphics graphics = CreateGraphics())
{
    System.Drawing.Size size = TextRenderer.MeasureText(graphics, id, e.Appearance.Font);
    if (size.Width > e.Column.Width)
    {
        int charFit = (int)(((double)e.Column.Width / (double)size.Width) * (double)id.Length);
        if (id.Length - charFit + 2 < id.Length)
        {
            e.DisplayText = string.Format("{0}{1}","...", id.Substring(id.Length - charFit + 2));
        }
    }
}

I did this changes in CustomDrawCell event of the DevExpress grid view. Please let me know if you see any flaw in this solution.

Rahul Techie
  • 363
  • 2
  • 8
0

You could use Drawing.Graphics.MeasureString() to get the size of one of your glyphs.
Then just check how many of them fit into your drawing area.

Thomas Zoechling
  • 34,177
  • 3
  • 81
  • 112
0

If it's fixed width why not just use floor(pixelcount/fontwidth)?

dutt
  • 7,909
  • 11
  • 52
  • 85
0

I think System.Drawing.Graphics.MeasureString() can help you. You can combine it with MeasureCharacterRanges or measure a Size of one character and then divide you number of pixels on this value. Something like this, you can play with results to get it work. I am sure that this will be working solution, so please ask if something is unclear =)

Restuta
  • 5,855
  • 33
  • 44
0

Although this is a late reply, I came across the same issue.

I think the answer to your question is inside your question. The function DrawString function you mention has facilities to automatically trim long strings. Have a look at the docs http://msdn.microsoft.com/en-us/library/19sb1bw6.aspx.

The function will trim the string so that it fits into the layout rectangle. You have to set the trimming property of the stringformat object you pass as parameter to something other than none.

Jason
  • 413
  • 5
  • 11