4 // Permission is hereby granted, free of charge, to any person obtaining
5 // a copy of this software and associated documentation files (the
6 // "Software"), to deal in the Software without restriction, including
7 // without limitation the rights to use, copy, modify, merge, publish,
8 // distribute, sublicense, and/or sell copies of the Software, and to
9 // permit persons to whom the Software is furnished to do so, subject to
10 // the following conditions:
12 // The above copyright notice and this permission notice shall be
13 // included in all copies or substantial portions of the Software.
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 // Copyright (c) 2006 Novell, Inc.
26 // Jonathan Pobst (monkey@jpobst.com)
29 // This has become a monster class for all things text measuring and drawing.
31 // The public API is MeasureText/DrawText, which uses GDI on Win32, and
32 // GDI+ on other platforms.
34 // There is an internal API MeasureTextInternal/DrawTextInternal, which allows
35 // you to pass a flag of whether to use GDI or GDI+. This is used mainly for
36 // controls that have the UseCompatibleTextRendering flag.
38 // There are also thread-safe versions of MeasureString/MeasureCharacterRanges
39 // for things that want to measure strings without having a Graphics object.
42 using System.Runtime.InteropServices;
44 using System.Drawing.Text;
46 namespace System.Windows.Forms
54 private static Bitmap static_bitmap;
56 private static Graphics static_graphics;
58 private TextRenderer ()
62 #region Public Methods
64 public static void DrawText (IDeviceContext dc, string text, Font font, Point pt, Color foreColor)
66 DrawTextInternal (dc, text, font, pt, foreColor, Color.Transparent, TextFormatFlags.Default, false);
69 public static void DrawText (IDeviceContext dc, string text, Font font, Rectangle bounds, Color foreColor)
71 DrawTextInternal (dc, text, font, bounds, foreColor, Color.Transparent, TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter, false);
74 public static void DrawText (IDeviceContext dc, string text, Font font, Point pt, Color foreColor, Color backColor)
76 DrawTextInternal (dc, text, font, pt, foreColor, backColor, TextFormatFlags.Default, false);
79 public static void DrawText (IDeviceContext dc, string text, Font font, Point pt, Color foreColor, TextFormatFlags flags)
81 DrawTextInternal (dc, text, font, pt, foreColor, Color.Transparent, flags, false);
84 public static void DrawText (IDeviceContext dc, string text, Font font, Rectangle bounds, Color foreColor, Color backColor)
86 DrawTextInternal (dc, text, font, bounds, foreColor, backColor, TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter, false);
89 public static void DrawText (IDeviceContext dc, string text, Font font, Rectangle bounds, Color foreColor, TextFormatFlags flags)
91 DrawTextInternal (dc, text, font, bounds, foreColor, Color.Transparent, flags, false);
94 public static void DrawText (IDeviceContext dc, string text, Font font, Point pt, Color foreColor, Color backColor, TextFormatFlags flags)
96 DrawTextInternal (dc, text, font, pt, foreColor, backColor, flags, false);
99 public static void DrawText (IDeviceContext dc, string text, Font font, Rectangle bounds, Color foreColor, Color backColor, TextFormatFlags flags)
101 DrawTextInternal (dc, text, font, bounds, foreColor, backColor, flags, false);
104 public static Size MeasureText (string text, Font font)
106 return MeasureTextInternal (StaticGraphics, text, font, Size.Empty, TextFormatFlags.Default, false);
109 public static Size MeasureText (IDeviceContext dc, string text, Font font)
111 return MeasureTextInternal (dc, text, font, Size.Empty, TextFormatFlags.Default, false);
114 public static Size MeasureText (string text, Font font, Size proposedSize)
116 return MeasureTextInternal (StaticGraphics, text, font, proposedSize, TextFormatFlags.Default, false);
119 public static Size MeasureText (IDeviceContext dc, string text, Font font, Size proposedSize)
121 return MeasureTextInternal (dc, text, font, proposedSize, TextFormatFlags.Default, false);
124 public static Size MeasureText (string text, Font font, Size proposedSize, TextFormatFlags flags)
126 return MeasureTextInternal (StaticGraphics, text, font, proposedSize, flags, false);
129 public static Size MeasureText (IDeviceContext dc, string text, Font font, Size proposedSize, TextFormatFlags flags)
131 return MeasureTextInternal (dc, text, font, proposedSize, flags, false);
136 #region Internal Methods That Do Stuff
138 internal static void DrawTextInternal (IDeviceContext dc, string text, Font font, Rectangle bounds, Color foreColor, Color backColor, TextFormatFlags flags, bool useDrawString)
140 internal static void DrawTextInternal (Graphics dc, string text, Font font, Rectangle bounds, Color foreColor, Color backColor, TextFormatFlags flags, bool useDrawString)
144 throw new ArgumentNullException ("dc");
146 if (text == null || text.Length == 0)
149 // We use MS GDI API's unless told not to, or we aren't on Windows
150 if (!useDrawString && !XplatUI.RunningOnUnix) {
151 if ((flags & TextFormatFlags.VerticalCenter) == TextFormatFlags.VerticalCenter || (flags & TextFormatFlags.Bottom) == TextFormatFlags.Bottom)
152 flags |= TextFormatFlags.SingleLine;
154 // Calculate the text bounds (there is often padding added)
155 Rectangle new_bounds = PadRectangle (bounds, flags);
156 new_bounds.Offset ((int)(dc as Graphics).Transform.OffsetX, (int)(dc as Graphics).Transform.OffsetY);
158 IntPtr hdc = IntPtr.Zero;
159 bool clear_clip_region = false;
161 // If we need to use the graphics clipping region, add it to our hdc
162 if ((flags & TextFormatFlags.PreserveGraphicsClipping) == TextFormatFlags.PreserveGraphicsClipping) {
163 Graphics graphics = (Graphics)dc;
164 Region clip_region = graphics.Clip;
166 if (!clip_region.IsInfinite (graphics)) {
167 IntPtr hrgn = clip_region.GetHrgn (graphics);
169 SelectClipRgn (hdc, hrgn);
172 clear_clip_region = true;
176 if (hdc == IntPtr.Zero)
179 // Set the fore color
180 if (foreColor != Color.Empty)
181 SetTextColor (hdc, ColorTranslator.ToWin32 (foreColor));
183 // Set the back color
184 if (backColor != Color.Transparent && backColor != Color.Empty) {
185 SetBkMode (hdc, 2); //1-Transparent, 2-Opaque
186 SetBkColor (hdc, ColorTranslator.ToWin32 (backColor));
189 SetBkMode (hdc, 1); //1-Transparent, 2-Opaque
192 XplatUIWin32.RECT r = XplatUIWin32.RECT.FromRectangle (new_bounds);
197 prevobj = SelectObject (hdc, font.ToHfont ());
198 Win32DrawText (hdc, text, text.Length, ref r, (int)flags);
199 prevobj = SelectObject (hdc, prevobj);
200 DeleteObject (prevobj);
203 Win32DrawText (hdc, text, text.Length, ref r, (int)flags);
206 if (clear_clip_region)
207 SelectClipRgn (hdc, IntPtr.Zero);
215 // Use Graphics.DrawString as a fallback method
218 IntPtr hdc = IntPtr.Zero;
224 g = Graphics.FromHdc (hdc);
227 StringFormat sf = FlagsToStringFormat (flags);
229 Rectangle new_bounds = PadDrawStringRectangle (bounds, flags);
231 g.DrawString (text, font, ThemeEngine.Current.ResPool.GetSolidBrush (foreColor), new_bounds, sf);
233 if (!(dc is Graphics)) {
238 if (hdc != IntPtr.Zero) dc.ReleaseHdc (hdc);
245 internal static Size MeasureTextInternal (IDeviceContext dc, string text, Font font, Size proposedSize, TextFormatFlags flags, bool useMeasureString)
247 internal static Size MeasureTextInternal (Graphics dc, string text, Font font, Size proposedSize, TextFormatFlags flags, bool useMeasureString)
250 if (!useMeasureString && !XplatUI.RunningOnUnix) {
251 // Tell DrawText to calculate size instead of draw
252 flags |= (TextFormatFlags)1024; // DT_CALCRECT
254 IntPtr hdc = dc.GetHdc ();
256 XplatUIWin32.RECT r = XplatUIWin32.RECT.FromRectangle (new Rectangle (Point.Empty, proposedSize));
261 prevobj = SelectObject (hdc, font.ToHfont ());
262 Win32DrawText (hdc, text, text.Length, ref r, (int)flags);
263 prevobj = SelectObject (hdc, prevobj);
264 DeleteObject (prevobj);
267 Win32DrawText (hdc, text, text.Length, ref r, (int)flags);
276 // Really, I am just making something up here, which as far as I can tell, MS
277 // just makes something up as well. This will require lots of tweaking to match MS. :(
278 Size retval = r.ToRectangle ().Size;
280 if (retval.Width > 0 && (flags & TextFormatFlags.NoPadding) == 0) {
282 retval.Width += (int)retval.Height / 8;
288 StringFormat sf = FlagsToStringFormat (flags);
293 retval = (dc as Graphics).MeasureString (text, font, Int32.MaxValue, sf).ToSize ();
295 retval = TextRenderer.MeasureString (text, font, Int32.MaxValue, sf).ToSize ();
297 if (retval.Width > 0 && (flags & TextFormatFlags.NoPadding) == 0)
305 #region Internal Methods That Are Just Overloads
307 internal static void DrawTextInternal (IDeviceContext dc, string text, Font font, Point pt, Color foreColor, bool useDrawString)
309 DrawTextInternal (dc, text, font, pt, foreColor, Color.Transparent, TextFormatFlags.Default, useDrawString);
312 internal static void DrawTextInternal (IDeviceContext dc, string text, Font font, Rectangle bounds, Color foreColor, bool useDrawString)
314 DrawTextInternal (dc, text, font, bounds, foreColor, Color.Transparent, TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter, useDrawString);
317 internal static void DrawTextInternal (IDeviceContext dc, string text, Font font, Point pt, Color foreColor, Color backColor, bool useDrawString)
319 DrawTextInternal (dc, text, font, pt, foreColor, backColor, TextFormatFlags.Default, useDrawString);
322 internal static void DrawTextInternal (IDeviceContext dc, string text, Font font, Point pt, Color foreColor, TextFormatFlags flags, bool useDrawString)
324 DrawTextInternal (dc, text, font, pt, foreColor, Color.Transparent, flags, useDrawString);
327 internal static void DrawTextInternal (IDeviceContext dc, string text, Font font, Rectangle bounds, Color foreColor, Color backColor, bool useDrawString)
329 DrawTextInternal (dc, text, font, bounds, foreColor, backColor, TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter, useDrawString);
332 internal static void DrawTextInternal (IDeviceContext dc, string text, Font font, Rectangle bounds, Color foreColor, TextFormatFlags flags, bool useDrawString)
334 internal static void DrawTextInternal (Graphics dc, string text, Font font, Rectangle bounds, Color foreColor, TextFormatFlags flags, bool useDrawString)
337 DrawTextInternal (dc, text, font, bounds, foreColor, Color.Transparent, flags, useDrawString);
340 internal static Size MeasureTextInternal (string text, Font font, bool useMeasureString)
342 return MeasureTextInternal (StaticGraphics, text, font, Size.Empty, TextFormatFlags.Default, useMeasureString);
346 internal static void DrawTextInternal (IDeviceContext dc, string text, Font font, Point pt, Color foreColor, Color backColor, TextFormatFlags flags, bool useDrawString)
348 Size sz = MeasureTextInternal (dc, text, font, useDrawString);
349 DrawTextInternal (dc, text, font, new Rectangle (pt, sz), foreColor, backColor, flags, useDrawString);
352 internal static Size MeasureTextInternal (IDeviceContext dc, string text, Font font, bool useMeasureString)
354 return MeasureTextInternal (dc, text, font, Size.Empty, TextFormatFlags.Default, useMeasureString);
357 internal static Size MeasureTextInternal (string text, Font font, Size proposedSize, bool useMeasureString)
359 return MeasureTextInternal (StaticGraphics, text, font, proposedSize, TextFormatFlags.Default, useMeasureString);
362 internal static Size MeasureTextInternal (IDeviceContext dc, string text, Font font, Size proposedSize, bool useMeasureString)
364 return MeasureTextInternal (dc, text, font, proposedSize, TextFormatFlags.Default, useMeasureString);
368 internal static Size MeasureTextInternal (string text, Font font, Size proposedSize, TextFormatFlags flags, bool useMeasureString)
370 return MeasureTextInternal (StaticGraphics, text, font, proposedSize, flags, useMeasureString);
374 #region Thread-Safe Static Graphics Methods
375 internal static SizeF MeasureString (string text, Font font)
377 return StaticGraphics.MeasureString (text, font);
380 internal static SizeF MeasureString (string text, Font font, int width)
382 return StaticGraphics.MeasureString (text, font, width);
385 internal static SizeF MeasureString (string text, Font font, SizeF layoutArea)
387 return StaticGraphics.MeasureString (text, font, layoutArea);
390 internal static SizeF MeasureString (string text, Font font, int width, StringFormat format)
392 return StaticGraphics.MeasureString (text, font, width, format);
395 internal static SizeF MeasureString (string text, Font font, PointF origin, StringFormat stringFormat)
397 return StaticGraphics.MeasureString (text, font, origin, stringFormat);
400 internal static SizeF MeasureString (string text, Font font, SizeF layoutArea, StringFormat stringFormat)
402 return StaticGraphics.MeasureString (text, font, layoutArea, stringFormat);
405 internal static SizeF MeasureString (string text, Font font, SizeF layoutArea, StringFormat stringFormat, out int charactersFitted, out int linesFilled)
407 return StaticGraphics.MeasureString (text, font, layoutArea, stringFormat, out charactersFitted, out linesFilled);
410 internal static Region[] MeasureCharacterRanges (string text, Font font, RectangleF layoutRect, StringFormat stringFormat)
412 return StaticGraphics.MeasureCharacterRanges (text, font, layoutRect, stringFormat);
415 internal static SizeF GetDpi ()
417 return new SizeF (StaticGraphics.DpiX, StaticGraphics.DpiY);
420 internal static Bitmap StaticBitmap {
422 if (static_bitmap == null)
423 static_bitmap = new Bitmap (1, 1, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
425 return static_bitmap;
429 internal static Graphics StaticGraphics {
431 if (static_graphics == null)
432 static_graphics = Graphics.FromImage (StaticBitmap);
434 return static_graphics;
439 #region Private Methods
440 private static StringFormat FlagsToStringFormat (TextFormatFlags flags)
442 StringFormat sf = new StringFormat ();
444 // Translation table: http://msdn.microsoft.com/msdnmag/issues/06/03/TextRendering/default.aspx?fig=true#fig4
446 // Horizontal Alignment
447 if ((flags & TextFormatFlags.HorizontalCenter) == TextFormatFlags.HorizontalCenter)
448 sf.Alignment = StringAlignment.Center;
449 else if ((flags & TextFormatFlags.Right) == TextFormatFlags.Right)
450 sf.Alignment = StringAlignment.Far;
452 sf.Alignment = StringAlignment.Near;
454 // Vertical Alignment
455 if ((flags & TextFormatFlags.Bottom) == TextFormatFlags.Bottom)
456 sf.LineAlignment = StringAlignment.Far;
457 else if ((flags & TextFormatFlags.VerticalCenter) == TextFormatFlags.VerticalCenter)
458 sf.LineAlignment = StringAlignment.Center;
460 sf.LineAlignment = StringAlignment.Near;
463 if ((flags & TextFormatFlags.EndEllipsis) == TextFormatFlags.EndEllipsis)
464 sf.Trimming = StringTrimming.EllipsisCharacter;
465 else if ((flags & TextFormatFlags.PathEllipsis) == TextFormatFlags.PathEllipsis)
466 sf.Trimming = StringTrimming.EllipsisPath;
467 else if ((flags & TextFormatFlags.WordEllipsis) == TextFormatFlags.WordEllipsis)
468 sf.Trimming = StringTrimming.EllipsisWord;
470 sf.Trimming = StringTrimming.Character;
473 if ((flags & TextFormatFlags.NoPrefix) == TextFormatFlags.NoPrefix)
474 sf.HotkeyPrefix = HotkeyPrefix.None;
475 else if ((flags & TextFormatFlags.HidePrefix) == TextFormatFlags.HidePrefix)
476 sf.HotkeyPrefix = HotkeyPrefix.Hide;
478 sf.HotkeyPrefix = HotkeyPrefix.Show;
481 if ((flags & TextFormatFlags.NoPadding) == TextFormatFlags.NoPadding)
482 sf.FormatFlags |= StringFormatFlags.FitBlackBox;
485 if ((flags & TextFormatFlags.SingleLine) == TextFormatFlags.SingleLine)
486 sf.FormatFlags |= StringFormatFlags.NoWrap;
487 else if ((flags & TextFormatFlags.TextBoxControl) == TextFormatFlags.TextBoxControl)
488 sf.FormatFlags |= StringFormatFlags.LineLimit;
491 //if ((flags & TextFormatFlags.RightToLeft) == TextFormatFlags.RightToLeft)
492 // sf.FormatFlags |= StringFormatFlags.DirectionRightToLeft;
493 if ((flags & TextFormatFlags.NoClipping) == TextFormatFlags.NoClipping)
494 sf.FormatFlags |= StringFormatFlags.NoClip;
499 private static Rectangle PadRectangle (Rectangle r, TextFormatFlags flags)
501 if ((flags & TextFormatFlags.NoPadding) == 0 && (flags & TextFormatFlags.Right) == 0 && (flags & TextFormatFlags.HorizontalCenter) == 0) {
505 if ((flags & TextFormatFlags.NoPadding) == 0 && (flags & TextFormatFlags.Right) == TextFormatFlags.Right) {
508 if ((flags & TextFormatFlags.LeftAndRightPadding) == TextFormatFlags.LeftAndRightPadding) {
512 if ((flags & TextFormatFlags.WordEllipsis) == TextFormatFlags.WordEllipsis || (flags & TextFormatFlags.EndEllipsis) == TextFormatFlags.EndEllipsis || (flags & TextFormatFlags.WordBreak) == TextFormatFlags.WordBreak) {
515 if ((flags & TextFormatFlags.VerticalCenter) == TextFormatFlags.VerticalCenter) {
522 private static Rectangle PadDrawStringRectangle (Rectangle r, TextFormatFlags flags)
524 if ((flags & TextFormatFlags.NoPadding) == 0 && (flags & TextFormatFlags.Right) == 0 && (flags & TextFormatFlags.HorizontalCenter) == 0) {
528 if ((flags & TextFormatFlags.NoPadding) == 0 && (flags & TextFormatFlags.Right) == TextFormatFlags.Right) {
531 if ((flags & TextFormatFlags.NoPadding) == TextFormatFlags.NoPadding) {
534 if ((flags & TextFormatFlags.NoPadding) == 0 && (flags & TextFormatFlags.Bottom) == TextFormatFlags.Bottom) {
537 if ((flags & TextFormatFlags.LeftAndRightPadding) == TextFormatFlags.LeftAndRightPadding) {
541 if ((flags & TextFormatFlags.WordEllipsis) == TextFormatFlags.WordEllipsis || (flags & TextFormatFlags.EndEllipsis) == TextFormatFlags.EndEllipsis || (flags & TextFormatFlags.WordBreak) == TextFormatFlags.WordBreak) {
544 if ((flags & TextFormatFlags.VerticalCenter) == TextFormatFlags.VerticalCenter) {
552 #region DllImports (Windows)
553 [DllImport ("user32", CharSet = CharSet.Unicode, EntryPoint = "DrawText")]
554 static extern int Win32DrawText (IntPtr hdc, string lpStr, int nCount, ref XplatUIWin32.RECT lpRect, int wFormat);
556 [DllImport ("gdi32")]
557 static extern int SetTextColor (IntPtr hdc, int crColor);
559 [DllImport ("gdi32")]
560 static extern IntPtr SelectObject (IntPtr hDC, IntPtr hObject);
562 [DllImport ("gdi32")]
563 static extern int SetBkColor (IntPtr hdc, int crColor);
565 [DllImport ("gdi32")]
566 static extern int SetBkMode (IntPtr hdc, int iBkMode);
568 [DllImport ("gdi32")]
569 static extern bool DeleteObject (IntPtr objectHandle);
572 static extern bool SelectClipRgn(IntPtr hdc, IntPtr hrgn);