locked
System.Drawing: Graphics.DrawString does not Draw at 0,0 RRS feed

  • Question

  • Hi

    It appears that the DrawString method does not draw text with the upper-left corner at (0, 0) when this is specified - it seems to leave an offset of about 0.2 * font size (in pixels) from the top. This also means that when (0, Bitmap.Height - font size [again in pixels) is specified, where one would expect the line to fit at the bottom of the bitmap, some descenders are partially cut-off. We also make use of a separate third party graphics library for different purposes and it places the text almost exactly at the top of a graphic when specified.

    Please see the sample code below. If run, black strips can clearly be seen above the text in the two output bitmap files. The second example contains code that was recommended for a more accurate match between MeasureString and DrawString, although it does not seem to impact the text position.

    Sample1:

                int bitmapWidth = 1024;
                int bitmapHeight = 50;
                float fontSize = 20.0f;
                string text = "THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG the quick brown fox jumps over the lazy dog";
    
                Bitmap bmp = new Bitmap(bitmapWidth, bitmapHeight);
                Graphics g = Graphics.FromImage(bmp);
                g.FillRectangle(Brushes.Black, 0.0f, 0.0f, bitmapWidth, bitmapHeight);    // Start with black background.
                Font font = new Font("Arial", fontSize, GraphicsUnit.Pixel);
                // Draw white text. NB: Notice 0,0 specified for upper left corner of text.
                g.DrawString(text, font, Brushes.White, new PointF(0.0f, 0.0f));
                string filename = Application.StartupPath + System.IO.Path.DirectorySeparatorChar + "test1.bmp";
                bmp.Save(filename, System.Drawing.Imaging.ImageFormat.Bmp);
                font.Dispose();
                g.Dispose();
                bmp.Dispose();


    Sample2:

                int bitmapWidth = 1024;
                int bitmapHeight = 50;
                float fontSize = 20.0f;
                string text = "THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG the quick brown fox jumps over the lazy dog";
    
                Bitmap bmp = new Bitmap(bitmapWidth, bitmapHeight);
                Graphics g = Graphics.FromImage(bmp);
    
                g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
                StringFormat format = new StringFormat(StringFormat.GenericTypographic);
                format.FormatFlags = format.FormatFlags | StringFormatFlags.MeasureTrailingSpaces | StringFormatFlags.NoWrap;
                format.Trimming = StringTrimming.None;
    
                g.FillRectangle(Brushes.Black, 0.0f, 0.0f, bitmapWidth, bitmapHeight);    // Start with black background.
                Font font = new Font("Arial", fontSize, GraphicsUnit.Pixel);
                // Draw white text. NB: Notice 0,0 specified for upper left corner of text.
                g.DrawString(text, font, Brushes.White, new PointF(0.0f, 0.0f), format);
                string filename = Application.StartupPath + System.IO.Path.DirectorySeparatorChar + "test2.bmp";
                bmp.Save(filename, System.Drawing.Imaging.ImageFormat.Bmp);
                font.Dispose();
                g.Dispose();
                bmp.Dispose();


    Thanks.
    Thursday, July 9, 2009 3:09 PM

Answers

  • This is by design, it is called "external leading" in typography.  It ensures that lines of text are properly spaced apart.  Getting rid of it is not easy to do in .NET.  The tricky part is knowing how much leading is used for the particular font and point size you are using.  Google GetTextMetric(), that's the native API function that is used to retrieve TEXTMETRIC.tmExternalLeading.  The reason it is not exposed in .NET is because it is only an approximation.  TrueType hinting subtly adjusts the shape and size of letters to make them more readable.  It is definitely best to avoid having to use this adjustment in the first place.

    Hans Passant.
    • Marked as answer by Brendon123 Wednesday, July 15, 2009 1:02 PM
    Thursday, July 9, 2009 4:14 PM
  • Internal leading is the space left for accents and ascenders.  Leading is on top to leave enough space between the first line and the top margin.  Not really, you'd need to P/Invoke API functions.
    Hans Passant.
    • Marked as answer by Brendon123 Wednesday, July 15, 2009 1:03 PM
    Friday, July 10, 2009 9:17 AM
  • Here is a link to the TEXTMETRIC doc page:
    http://msdn.microsoft.com/en-us/library/dd145132(VS.85).aspx

    If you need per-pixel control of your text, the only viable option you have is to use bitmap fonts instead of True Type. True Type fonts have a lot of "slack" in them intentionally, so that they are readable regardless of the display (video or paper, high DPI or low, etc.). I think bitmap fonts have gone the way of the Dodo. If I remember correctly, GDI+ does not support bitmap/raster fonts, but TextRenderer uses GDI and can.
    • Marked as answer by Brendon123 Wednesday, July 15, 2009 1:03 PM
    Friday, July 10, 2009 3:56 PM
  • Nope, the Font class doesn't support device fonts.

    Hans Passant.
    • Marked as answer by Brendon123 Wednesday, July 15, 2009 1:07 PM
    Friday, July 10, 2009 4:15 PM

All replies

  • This is by design, it is called "external leading" in typography.  It ensures that lines of text are properly spaced apart.  Getting rid of it is not easy to do in .NET.  The tricky part is knowing how much leading is used for the particular font and point size you are using.  Google GetTextMetric(), that's the native API function that is used to retrieve TEXTMETRIC.tmExternalLeading.  The reason it is not exposed in .NET is because it is only an approximation.  TrueType hinting subtly adjusts the shape and size of letters to make them more readable.  It is definitely best to avoid having to use this adjustment in the first place.

    Hans Passant.
    • Marked as answer by Brendon123 Wednesday, July 15, 2009 1:02 PM
    Thursday, July 9, 2009 4:14 PM
  • Thanks for the reply, I did not get anywhere near to this by searching for my initial problem! I found the GetTextMetic function on http://pinvoke.net/default.aspx/gdi32/GetTextMetrics.html and it looks fairly straightforward to work with barring the approximation issue that you mentioned. I have not found a good explanation of the TEXTMETRIC members yet though. I've been looking over some typography information (leading, tracking, kerning etc.) as well.
    1. I assume that internal leading is the spacing between lines and external leading is the additional space above the first line. Is that correct?
    2. I can understand spacing between lines for the readability/space trade-off; however I can't understand the design reasons behind having additional space above a block of text?
    3. Is it possible to control tracking and kerning with .NET code, as an interest has been shown in this?
    Thanks.
    Friday, July 10, 2009 8:01 AM
  • Internal leading is the space left for accents and ascenders.  Leading is on top to leave enough space between the first line and the top margin.  Not really, you'd need to P/Invoke API functions.
    Hans Passant.
    • Marked as answer by Brendon123 Wednesday, July 15, 2009 1:03 PM
    Friday, July 10, 2009 9:17 AM
  • Here is a link to the TEXTMETRIC doc page:
    http://msdn.microsoft.com/en-us/library/dd145132(VS.85).aspx

    If you need per-pixel control of your text, the only viable option you have is to use bitmap fonts instead of True Type. True Type fonts have a lot of "slack" in them intentionally, so that they are readable regardless of the display (video or paper, high DPI or low, etc.). I think bitmap fonts have gone the way of the Dodo. If I remember correctly, GDI+ does not support bitmap/raster fonts, but TextRenderer uses GDI and can.
    • Marked as answer by Brendon123 Wednesday, July 15, 2009 1:03 PM
    Friday, July 10, 2009 3:56 PM
  • Nope, the Font class doesn't support device fonts.

    Hans Passant.
    • Marked as answer by Brendon123 Wednesday, July 15, 2009 1:07 PM
    Friday, July 10, 2009 4:15 PM
  • This seems like a good starting point: Microsoft Typography
    • Marked as answer by Zhi-Xin Ye Wednesday, July 15, 2009 12:55 PM
    • Unmarked as answer by Brendon123 Wednesday, July 15, 2009 1:03 PM
    • Marked as answer by Brendon123 Wednesday, July 15, 2009 1:04 PM
    • Unmarked as answer by Brendon123 Wednesday, July 15, 2009 1:09 PM
    Friday, July 10, 2009 5:55 PM