Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

GDI+, Remove white space from Graphics DrawString()

Tags:

c#

gdi+

I have a problem with C# GDI+ draw string functions -- I can not get the exact size of my drawn string -- without internal leading and external leading, just the em height. The font/string Measuring APIs provided by Microsoft seems to always output a bounding box that is a few pixels bigger than the text drawing, even after I removed the internal leading.

    private void pictureBox1_Paint(object sender, PaintEventArgs e)
    {
        Graphics g = e.Graphics;
        g.Clear(Color.Black);
        String text = "The quick brown fox jumps over the lazy dog";
        Font font = new System.Drawing.Font(FontFamily.GenericSerif, 24);
        float internalLeading = font.Size * (font.FontFamily.GetCellAscent(font.Style) + font.FontFamily.GetCellDescent(font.Style) - font.FontFamily.GetEmHeight(font.Style)) / font.FontFamily.GetEmHeight(font.Style);
        StringFormat format = StringFormat.GenericTypographic;
        format.Trimming = StringTrimming.None;
        format.FormatFlags = StringFormatFlags.NoWrap;
        System.Drawing.RectangleF rect = new System.Drawing.RectangleF(0, 0, pictureBox1.Width,pictureBox1.Height);
        System.Drawing.CharacterRange[] ranges = { new System.Drawing.CharacterRange(0, text.Length) };
        System.Drawing.Region[] boundings = new System.Drawing.Region[1];
        format.SetMeasurableCharacterRanges(ranges);
        boundings = g.MeasureCharacterRanges(text, font, rect, format);
        rect = boundings[0].GetBounds(g);
        g.DrawString(text, font, new SolidBrush(Color.Red), new RectangleF(0,0,rect.Width,rect.Height), format);
        g.DrawRectangle(Pens.Yellow, rect.X, rect.Y + internalLeading, rect.Width, rect.Height - internalLeading);
    }

And the output is shown below. We can see there is still a few pixels between the text and top edge of the bounding box.

enter image description here

I can get the exact size of drawn string by reading the pixels in image and calculate bounding box by myself. But it is slow. Could I know if there is a simple way to just work around this?

like image 343
ncite Avatar asked May 19 '15 05:05

ncite


1 Answers

GDI+ handles more than just english, and line heights are fixed (imagine the horror if every line was the height of the text on that line).

The bounding box also accounts for various diacritics that can adorn those characters, including similar things in english, like in COÖPERATE.

See:

Diaresis and diacritis

Note how the diaresis (the ¨) barely fits within the bounding box, and the ˇ is quite obviously outside - and yet, at the bottom, there's enough space to fit q. What gives? Well, MeasureString is meant for text formatting, not for getting the smallest possible rectangle the text could be rendered in - which would of course further be complicated by things that are not supposed to change the way the text aligns, like using ClearType for example.

The function is built around the assumption that you quite like to have lines the same height - that's how all text is usually rendered.

To quote MSDN directly:

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.

like image 109
Luaan Avatar answered Nov 17 '22 09:11

Luaan