MeasureString和DrawString的区别

为什么我必须将MeasureString()结果宽度增加21% size.Width = size.Width * 1.21f; 逃避DrawString() Word Wrap?

我需要一个解决方案来获得确切的结果。

相同的字体,相同的字符串格式,两个函数中使用的文本相同。


从OP回答:

  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  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); 

我的代码。

MeasureString()方法存在一些问题,尤其是在绘制非ASCII字符时。 请尝试使用TextRenderer.MeasureText()。

Graphics.MeasureString,TextRenderer.MeasureText和Graphics.MeasureCharacterRanges都返回一个包含字形周围空白像素的大小,以容纳上升和下降。

换句话说,它们返回“a”的高度与“d”(上升)或“y”(下降)的高度相同。 如果您需要字形的真实大小,唯一的方法是绘制字符串并计算像素:

 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 

如果它对任何人都有帮助,我将回答从smirkingman转换为C#,修复了内存错误(使用 – Dispose)和外部循环中断(没有TODO)。 我也使用缩放图形(和字体),所以我也添加了(否则不起作用)。 它返回RectangleF,因为我想精确定位文本(使用Graphics.DrawText)。

不完美但足够我的目的源代码:

 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); } } 

关于codeproject的这篇文章提供了两种方法来获取DrawString呈现的字符的确切大小。

您可能需要添加以下StringFormat标志:

 StringFormatFlags.FitBlackBox 

试试这个解决方案: http : //www.codeproject.com/Articles/2118/Bypass-Graphics-MeasureString-limitation (发现于https://stackoverflow.com/a/11708952/908936 )

码:

 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); }