Difference in MeasureString and DrawString

why do I need to increase the width of the result of MeasureString() by 21% size.Width = size.Width * 1.21f; to evade Word Wrap in DrawString() ?

I need a solution to get the exact result.

The same font, the same string format, the same text used in both functions.


From the OP answer:

  SizeF size = graphics.MeasureString(element.Currency, Currencyfont, new PointF(0, 0), strFormatLeft); size.Width = size.Width * 1.21f; int freespace = rect.Width - (int)size.Width; if (freespace < ImageSize) { if (freespace > 0) ImageSize = freespace; else ImageSize = 0; } int FlagY = y + (CurrencySize - ImageSize) / 2; int FlagX = (freespace - ImageSize) / 2; graphics.DrawImage(GetResourseImage(@"Flags." + element.Flag.ToUpper() + ".png"), new Rectangle(FlagX, FlagY, ImageSize, ImageSize)); graphics.DrawString(element.Currency, Currencyfont, Brushes.Black, new Rectangle(FlagX + ImageSize, rect.Y, (int)(size.Width), CurrencySize), strFormatLeft); 

My code.

+7
string c # graphics
source share
6 answers

The MeasureString () method had some problems, especially when drawing non-ASCII characters. Try using TextRenderer.MeasureText () instead.

+5
source share

Graphics.MeasureString, TextRenderer.MeasureText, and Graphics.MeasureCharacterRanges all return a size that includes empty pixels around the glyph to accommodate ascenders and descenders.

In other words, they return the height "a" in the same way as the height "d" (ascending) or "y" (descender). If you need true glyph size, the only way is to draw a line and count the pixels:

 Public Shared Function MeasureStringSize(ByVal graphics As Graphics, ByVal text As String, ByVal font As Font) As SizeF ' Get initial estimate with MeasureText Dim flags As TextFormatFlags = TextFormatFlags.Left + TextFormatFlags.NoClipping Dim proposedSize As Size = New Size(Integer.MaxValue, Integer.MaxValue) Dim size As Size = TextRenderer.MeasureText(graphics, text, font, proposedSize, flags) ' Create a bitmap Dim image As New Bitmap(size.Width, size.Height) image.SetResolution(graphics.DpiX, graphics.DpiY) Dim strFormat As New StringFormat strFormat.Alignment = StringAlignment.Near strFormat.LineAlignment = StringAlignment.Near ' Draw the actual text Dim g As Graphics = graphics.FromImage(image) g.SmoothingMode = SmoothingMode.HighQuality g.TextRenderingHint = Drawing.Text.TextRenderingHint.AntiAliasGridFit g.Clear(Color.White) g.DrawString(text, font, Brushes.Black, New PointF(0, 0), strFormat) ' Find the true boundaries of the glyph Dim xs As Integer = 0 Dim xf As Integer = size.Width - 1 Dim ys As Integer = 0 Dim yf As Integer = size.Height - 1 ' Find left margin Do While xs < xf For y As Integer = ys To yf If image.GetPixel(xs, y).ToArgb <> Color.White.ToArgb Then Exit Do End If Next xs += 1 Loop ' Find right margin Do While xf > xs For y As Integer = ys To yf If image.GetPixel(xf, y).ToArgb <> Color.White.ToArgb Then Exit Do End If Next xf -= 1 Loop ' Find top margin Do While ys < yf For x As Integer = xs To xf If image.GetPixel(x, ys).ToArgb <> Color.White.ToArgb Then Exit Do End If Next ys += 1 Loop ' Find bottom margin Do While yf > ys For x As Integer = xs To xf If image.GetPixel(x, yf).ToArgb <> Color.White.ToArgb Then Exit Do End If Next yf -= 1 Loop Return New SizeF(xf - xs + 1, yf - ys + 1) End Function 
+3
source share

If this helps someone, I converted the response from smirkingman to C #, fixing memory errors (using - Dispose) and external loop interrupts (without TODO). I also used scaling graphics (and fonts), so I added that too (didn't work otherwise). And it returns RectangleF because I wanted to accurately position the text (using Graphics.DrawText).

Not perfect, but good enough for my source code:

 static class StringMeasurer { private static SizeF GetScaleTransform(Matrix m) { /* 3x3 matrix, affine transformation (skew - used by rotation) [ X scale, Y skew, 0 ] [ X skew, Y scale, 0 ] [ X translate, Y translate, 1 ] indices (0, ...): X scale, Y skew, Y skew, X scale, X translate, Y translate */ return new SizeF(m.Elements[0], m.Elements[3]); } public static RectangleF MeasureString(Graphics graphics, Font f, string s) { //copy only scale, not rotate or transform var scale = GetScaleTransform(graphics.Transform); // Get initial estimate with MeasureText //TextFormatFlags flags = TextFormatFlags.Left | TextFormatFlags.NoClipping; //Size proposedSize = new Size(int.MaxValue, int.MaxValue); //Size size = TextRenderer.MeasureText(graphics, s, f, proposedSize, flags); SizeF sizef = graphics.MeasureString(s, f); sizef.Width *= scale.Width; sizef.Height *= scale.Height; Size size = sizef.ToSize(); int xLeft = 0; int xRight = size.Width - 1; int yTop = 0; int yBottom = size.Height - 1; // Create a bitmap using (Bitmap image = new Bitmap(size.Width, size.Height)) { image.SetResolution(graphics.DpiX, graphics.DpiY); StringFormat strFormat = new StringFormat(); strFormat.Alignment = StringAlignment.Near; strFormat.LineAlignment = StringAlignment.Near; // Draw the actual text using (Graphics g = Graphics.FromImage(image)) { g.SmoothingMode = graphics.SmoothingMode; g.TextRenderingHint = graphics.TextRenderingHint; g.Clear(Color.White); g.ScaleTransform(scale.Width, scale.Height); g.DrawString(s, f, Brushes.Black, new PointF(0, 0), strFormat); } // Find the true boundaries of the glyph // Find left margin for (; xLeft < xRight; xLeft++) for (int y = yTop; y <= yBottom; y++) if (image.GetPixel(xLeft, y).ToArgb() != Color.White.ToArgb()) goto OUTER_BREAK_LEFT; OUTER_BREAK_LEFT: ; // Find right margin for (; xRight > xLeft; xRight--) for (int y = yTop; y <= yBottom; y++) if (image.GetPixel(xRight, y).ToArgb() != Color.White.ToArgb()) goto OUTER_BREAK_RIGHT; OUTER_BREAK_RIGHT: ; // Find top margin for (; yTop < yBottom; yTop++) for (int x = xLeft; x <= xRight; x++) if (image.GetPixel(x, yTop).ToArgb() != Color.White.ToArgb()) goto OUTER_BREAK_TOP; OUTER_BREAK_TOP: ; // Find bottom margin for (; yBottom > yTop; yBottom-- ) for (int x = xLeft; x <= xRight; x++) if (image.GetPixel(x, yBottom).ToArgb() != Color.White.ToArgb()) goto OUTER_BREAK_BOTTOM; OUTER_BREAK_BOTTOM: ; } var pt = new PointF(xLeft, yTop); var sz = new SizeF(xRight - xLeft + 1, yBottom - yTop + 1); return new RectangleF(pt.X / scale.Width, pt.Y / scale.Height, sz.Width / scale.Width, sz.Height / scale.Height); } } 
+2
source share

This codeproject article provides two ways to get the exact size of characters as they are rendered by DrawString.

0
source share

You probably need to add the following StringFormat flags:

 StringFormatFlags.FitBlackBox 
-one
source share

try this solution: http://www.codeproject.com/Articles/2118/Bypass-Graphics-MeasureString-limitation (found it at https://stackoverflow.com/a/115279/ )

the code:

 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 (new[] {ranges}); regions = graphics.MeasureCharacterRanges (text, font, rect, format); rect = regions[0].GetBounds (graphics); return (int)(rect.Right + 1.0f); } 
-one
source share

All Articles