using System; using System.Drawing; using System.Reflection; using System.Runtime.InteropServices; using System.Windows.Forms; public class Form1 : Form { static void Main() { Application.Run(new Form1()); } public Form1() { Paint += Form1_Paint; SetBounds(0, 0, 1400, 800, BoundsSpecified.Size); } private void Form1_Paint(object sender, PaintEventArgs e) { int count = 0; const int width = 100, height = 100; for (int rtl = 0; rtl <= 1; rtl++) { for (int boxed = 0; boxed <= 1; boxed++) { for (int vertical = 0; vertical <= 1; vertical++) { for (StringAlignment h = StringAlignment.Near; h <= StringAlignment.Far; h++) { for (StringAlignment v = StringAlignment.Near; v <= StringAlignment.Far; v++) { DrawString(e.Graphics, h, v, vertical == 1, rtl == 1, new RectangleF(100 + width * (int)h + 6 * width * boxed + 3 * width * rtl, 100 + height * (int)v + 3 * width * vertical, width * boxed, height * boxed), ref count); } } } } } } void DrawString(Graphics g, StringAlignment horizAlign, StringAlignment vertAlign, bool vertical, bool rtl, RectangleF rect, ref int count) { using (var sf = new StringFormat(StringFormat.GenericTypographic)) { sf.Alignment = horizAlign; sf.LineAlignment = vertAlign; if (vertical) sf.FormatFlags |= StringFormatFlags.DirectionVertical; if (rtl) sf.FormatFlags |= StringFormatFlags.DirectionRightToLeft; if (rect.Width > 1) { g.DrawRectangle(Pens.Green, Rectangle.Round(rect)); } else { g.FillRectangle(Brushes.Fuchsia, rect.X - 2, rect.Y - 2, 5, 5); } string text = $"{count}: {horizAlign}\n{vertAlign}{(vertical ? " / vertical" : "")}{(rtl ? " / rtl" : "")}"; sf.SetMeasurableCharacterRanges(new[] { new CharacterRange(0, text.Length) }); //var measuredRegion = g.MeasureCharacterRanges(text, Font, rect, sf)[0]; //g.FillRegion(Brushes.LightYellow, measuredRegion); //using (var gp = new GraphicsPath()) { // gp.AddString(text, Font.FontFamily, (int)Font.Style, Font.SizeInPoints / 72 * g.DpiX, rect, sf); // g.FillPath(Brushes.Black, gp); // Console.WriteLine(gp.GetBounds()); //} g.DrawString(text, base.Font, Brushes.Black, rect, sf); //var measuredRect = measuredRegion.GetBounds(g); //g.DrawRectangle(Pens.Orange, Rectangle.Round(measuredRect)); var measuredRect = GdipMeasureString(g, text, Font, ref rect, sf); g.FillRectangle(Brushes.Orange, measuredRect.X - 1, measuredRect.Y - 1, 3, 3); g.DrawRectangle(Pens.Aqua, Rectangle.Round(measuredRect)); Console.WriteLine($"Measured {count} text as {Rectangle.Round(measuredRect)}, rect is {rect}"); count++; } } // Lightly modified from Mono's System.Drawing gdipFunctions.cs [DllImport("gdiplus", CharSet = CharSet.Unicode)] internal unsafe static extern int GdipMeasureString(IntPtr graphics, string str, int length, IntPtr font, ref RectangleF layoutRect, IntPtr stringFormat, out RectangleF boundingBox, int* codepointsFitted, int* linesFilled); // Lightly modified from Mono's System.Drawing Graphics.cs private unsafe RectangleF GdipMeasureString(Graphics graphics, string text, Font font, ref RectangleF layoutRect, StringFormat stringFormat) { if ((text == null) || (text.Length == 0)) return RectangleF.Empty; if (font == null) throw new ArgumentNullException("font"); RectangleF boundingBox = new RectangleF (); var status = GdipMeasureString (GetPropertyValue(graphics, "NativeGraphics"), text, text.Length, GetNativeFont(font), ref layoutRect, GetNativeStringFormat(stringFormat), out boundingBox, null, null); if (status != 0) throw new Exception($"Status was an error ({status})"); return boundingBox; } private IntPtr GetNativeFont(Font font) { if (Environment.OSVersion.Platform == PlatformID.Unix) return GetPropertyValue(font, "NativeObject"); return GetPropertyValue(font, "NativeFont"); } private IntPtr GetNativeStringFormat(StringFormat format) { if (Environment.OSVersion.Platform == PlatformID.Unix) return GetPropertyValue(format, "NativeObject"); var field = typeof(StringFormat).GetField("nativeFormat", BindingFlags.Instance | BindingFlags.NonPublic); return (IntPtr)field.GetValue(format); } private T GetPropertyValue(object from, string propertyName) { var t = from.GetType(); var prop = t.GetProperty(propertyName, BindingFlags.Instance | BindingFlags.NonPublic, null, typeof(T), new Type[0], null); return (T)prop?.GetValue(from); } }