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
53 private TextRenderer ()
57 #region Public Methods
59 public static void DrawText (IDeviceContext dc, string text, Font font, Point pt, Color foreColor)
61 DrawTextInternal (dc, text, font, pt, foreColor, Color.Transparent, TextFormatFlags.Default, false);
64 public static void DrawText (IDeviceContext dc, string text, Font font, Rectangle bounds, Color foreColor)
66 DrawTextInternal (dc, text, font, bounds, foreColor, Color.Transparent, TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter, false);
69 public static void DrawText (IDeviceContext dc, string text, Font font, Point pt, Color foreColor, Color backColor)
71 DrawTextInternal (dc, text, font, pt, foreColor, backColor, TextFormatFlags.Default, false);
74 public static void DrawText (IDeviceContext dc, string text, Font font, Point pt, Color foreColor, TextFormatFlags flags)
76 DrawTextInternal (dc, text, font, pt, foreColor, Color.Transparent, flags, false);
79 public static void DrawText (IDeviceContext dc, string text, Font font, Rectangle bounds, Color foreColor, Color backColor)
81 DrawTextInternal (dc, text, font, bounds, foreColor, backColor, TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter, false);
84 public static void DrawText (IDeviceContext dc, string text, Font font, Rectangle bounds, Color foreColor, TextFormatFlags flags)
86 DrawTextInternal (dc, text, font, bounds, foreColor, Color.Transparent, flags, false);
89 public static void DrawText (IDeviceContext dc, string text, Font font, Point pt, Color foreColor, Color backColor, TextFormatFlags flags)
91 DrawTextInternal (dc, text, font, pt, foreColor, backColor, flags, false);
94 public static void DrawText (IDeviceContext dc, string text, Font font, Rectangle bounds, Color foreColor, Color backColor, TextFormatFlags flags)
96 DrawTextInternal (dc, text, font, bounds, foreColor, backColor, flags, false);
99 public static Size MeasureText (string text, Font font)
101 return MeasureTextInternal (Hwnd.GraphicsContext, text, font, Size.Empty, TextFormatFlags.Default, false);
104 public static Size MeasureText (IDeviceContext dc, string text, Font font)
106 return MeasureTextInternal (dc, text, font, Size.Empty, TextFormatFlags.Default, false);
109 public static Size MeasureText (string text, Font font, Size proposedSize)
111 return MeasureTextInternal (Hwnd.GraphicsContext, text, font, proposedSize, TextFormatFlags.Default, false);
114 public static Size MeasureText (IDeviceContext dc, string text, Font font, Size proposedSize)
116 return MeasureTextInternal (dc, text, font, proposedSize, TextFormatFlags.Default, false);
119 public static Size MeasureText (string text, Font font, Size proposedSize, TextFormatFlags flags)
121 return MeasureTextInternal (Hwnd.GraphicsContext, text, font, proposedSize, flags, false);
124 public static Size MeasureText (IDeviceContext dc, string text, Font font, Size proposedSize, TextFormatFlags flags)
126 return MeasureTextInternal (dc, text, font, proposedSize, flags, false);
131 #region Internal Methods That Do Stuff
133 internal static void DrawTextInternal (IDeviceContext dc, string text, Font font, Rectangle bounds, Color foreColor, Color backColor, TextFormatFlags flags, bool useDrawString)
135 internal static void DrawTextInternal (Graphics dc, string text, Font font, Rectangle bounds, Color foreColor, Color backColor, TextFormatFlags flags, bool useDrawString)
139 throw new ArgumentNullException ("dc");
141 if (text == null || text.Length == 0)
144 // We use MS GDI API's unless told not to, or we aren't on Windows
145 if (!useDrawString && !XplatUI.RunningOnUnix) {
146 if ((flags & TextFormatFlags.VerticalCenter) == TextFormatFlags.VerticalCenter || (flags & TextFormatFlags.Bottom) == TextFormatFlags.Bottom)
147 flags |= TextFormatFlags.SingleLine;
149 // Calculate the text bounds (there is often padding added)
150 Rectangle new_bounds = PadRectangle (bounds, flags);
151 new_bounds.Offset ((int)(dc as Graphics).Transform.OffsetX, (int)(dc as Graphics).Transform.OffsetY);
153 IntPtr hdc = IntPtr.Zero;
154 bool clear_clip_region = false;
156 // If we need to use the graphics clipping region, add it to our hdc
157 if ((flags & TextFormatFlags.PreserveGraphicsClipping) == TextFormatFlags.PreserveGraphicsClipping) {
158 Graphics graphics = (Graphics)dc;
159 Region clip_region = graphics.Clip;
161 if (!clip_region.IsInfinite (graphics)) {
162 IntPtr hrgn = clip_region.GetHrgn (graphics);
164 SelectClipRgn (hdc, hrgn);
167 clear_clip_region = true;
171 if (hdc == IntPtr.Zero)
174 // Set the fore color
175 if (foreColor != Color.Empty)
176 SetTextColor (hdc, ColorTranslator.ToWin32 (foreColor));
178 // Set the back color
179 if (backColor != Color.Transparent && backColor != Color.Empty) {
180 SetBkMode (hdc, 2); //1-Transparent, 2-Opaque
181 SetBkColor (hdc, ColorTranslator.ToWin32 (backColor));
184 SetBkMode (hdc, 1); //1-Transparent, 2-Opaque
187 XplatUIWin32.RECT r = XplatUIWin32.RECT.FromRectangle (new_bounds);
192 prevobj = SelectObject (hdc, font.ToHfont ());
193 Win32DrawText (hdc, text, text.Length, ref r, (int)flags);
194 prevobj = SelectObject (hdc, prevobj);
195 DeleteObject (prevobj);
198 Win32DrawText (hdc, text, text.Length, ref r, (int)flags);
201 if (clear_clip_region)
202 SelectClipRgn (hdc, IntPtr.Zero);
210 // Use Graphics.DrawString as a fallback method
213 IntPtr hdc = IntPtr.Zero;
219 g = Graphics.FromHdc (hdc);
222 StringFormat sf = FlagsToStringFormat (flags);
224 Rectangle new_bounds = PadDrawStringRectangle (bounds, flags);
226 g.DrawString (text, font, ThemeEngine.Current.ResPool.GetSolidBrush (foreColor), new_bounds, sf);
228 if (!(dc is Graphics)) {
233 if (hdc != IntPtr.Zero) dc.ReleaseHdc (hdc);
240 internal static Size MeasureTextInternal (IDeviceContext dc, string text, Font font, Size proposedSize, TextFormatFlags flags, bool useMeasureString)
242 internal static Size MeasureTextInternal (Graphics dc, string text, Font font, Size proposedSize, TextFormatFlags flags, bool useMeasureString)
245 if (!useMeasureString && !XplatUI.RunningOnUnix) {
246 // Tell DrawText to calculate size instead of draw
247 flags |= (TextFormatFlags)1024; // DT_CALCRECT
249 IntPtr hdc = dc.GetHdc ();
251 XplatUIWin32.RECT r = XplatUIWin32.RECT.FromRectangle (new Rectangle (Point.Empty, proposedSize));
256 prevobj = SelectObject (hdc, font.ToHfont ());
257 Win32DrawText (hdc, text, text.Length, ref r, (int)flags);
258 prevobj = SelectObject (hdc, prevobj);
259 DeleteObject (prevobj);
262 Win32DrawText (hdc, text, text.Length, ref r, (int)flags);
271 // Really, I am just making something up here, which as far as I can tell, MS
272 // just makes something up as well. This will require lots of tweaking to match MS. :(
273 Size retval = r.ToRectangle ().Size;
275 if (retval.Width > 0 && (flags & TextFormatFlags.NoPadding) == 0) {
277 retval.Width += (int)retval.Height / 8;
283 StringFormat sf = FlagsToStringFormat (flags);
288 retval = (dc as Graphics).MeasureString (text, font, proposedSize.Width == 0 ? Int32.MaxValue : proposedSize.Width, sf).ToSize ();
290 retval = TextRenderer.MeasureString (text, font, proposedSize.Width == 0 ? Int32.MaxValue : proposedSize.Width, sf).ToSize ();
292 if (retval.Width > 0 && (flags & TextFormatFlags.NoPadding) == 0)
300 #region Internal Methods That Are Just Overloads
302 internal static void DrawTextInternal (IDeviceContext dc, string text, Font font, Point pt, Color foreColor, bool useDrawString)
304 DrawTextInternal (dc, text, font, pt, foreColor, Color.Transparent, TextFormatFlags.Default, useDrawString);
307 internal static void DrawTextInternal (IDeviceContext dc, string text, Font font, Rectangle bounds, Color foreColor, bool useDrawString)
309 DrawTextInternal (dc, text, font, bounds, foreColor, Color.Transparent, TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter, useDrawString);
312 internal static void DrawTextInternal (IDeviceContext dc, string text, Font font, Point pt, Color foreColor, Color backColor, bool useDrawString)
314 DrawTextInternal (dc, text, font, pt, foreColor, backColor, TextFormatFlags.Default, useDrawString);
317 internal static void DrawTextInternal (IDeviceContext dc, string text, Font font, Point pt, Color foreColor, TextFormatFlags flags, bool useDrawString)
319 DrawTextInternal (dc, text, font, pt, foreColor, Color.Transparent, flags, useDrawString);
322 internal static void DrawTextInternal (IDeviceContext dc, string text, Font font, Rectangle bounds, Color foreColor, Color backColor, bool useDrawString)
324 DrawTextInternal (dc, text, font, bounds, foreColor, backColor, TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter, useDrawString);
327 internal static void DrawTextInternal (IDeviceContext dc, string text, Font font, Rectangle bounds, Color foreColor, TextFormatFlags flags, bool useDrawString)
329 internal static void DrawTextInternal (Graphics dc, string text, Font font, Rectangle bounds, Color foreColor, TextFormatFlags flags, bool useDrawString)
332 DrawTextInternal (dc, text, font, bounds, foreColor, Color.Transparent, flags, useDrawString);
335 internal static Size MeasureTextInternal (string text, Font font, bool useMeasureString)
337 return MeasureTextInternal (Hwnd.GraphicsContext, text, font, Size.Empty, TextFormatFlags.Default, useMeasureString);
341 internal static void DrawTextInternal (IDeviceContext dc, string text, Font font, Point pt, Color foreColor, Color backColor, TextFormatFlags flags, bool useDrawString)
343 Size sz = MeasureTextInternal (dc, text, font, useDrawString);
344 DrawTextInternal (dc, text, font, new Rectangle (pt, sz), foreColor, backColor, flags, useDrawString);
347 internal static Size MeasureTextInternal (IDeviceContext dc, string text, Font font, bool useMeasureString)
349 return MeasureTextInternal (dc, text, font, Size.Empty, TextFormatFlags.Default, useMeasureString);
352 internal static Size MeasureTextInternal (string text, Font font, Size proposedSize, bool useMeasureString)
354 return MeasureTextInternal (Hwnd.GraphicsContext, text, font, proposedSize, TextFormatFlags.Default, useMeasureString);
357 internal static Size MeasureTextInternal (IDeviceContext dc, string text, Font font, Size proposedSize, bool useMeasureString)
359 return MeasureTextInternal (dc, text, font, proposedSize, TextFormatFlags.Default, useMeasureString);
363 internal static Size MeasureTextInternal (string text, Font font, Size proposedSize, TextFormatFlags flags, bool useMeasureString)
365 return MeasureTextInternal (Hwnd.GraphicsContext, text, font, proposedSize, flags, useMeasureString);
369 #region Thread-Safe Static Graphics Methods
370 internal static SizeF MeasureString (string text, Font font)
372 return Hwnd.GraphicsContext.MeasureString (text, font);
375 internal static SizeF MeasureString (string text, Font font, int width)
377 return Hwnd.GraphicsContext.MeasureString (text, font, width);
380 internal static SizeF MeasureString (string text, Font font, SizeF layoutArea)
382 return Hwnd.GraphicsContext.MeasureString (text, font, layoutArea);
385 internal static SizeF MeasureString (string text, Font font, int width, StringFormat format)
387 return Hwnd.GraphicsContext.MeasureString (text, font, width, format);
390 internal static SizeF MeasureString (string text, Font font, PointF origin, StringFormat stringFormat)
392 return Hwnd.GraphicsContext.MeasureString (text, font, origin, stringFormat);
395 internal static SizeF MeasureString (string text, Font font, SizeF layoutArea, StringFormat stringFormat)
397 return Hwnd.GraphicsContext.MeasureString (text, font, layoutArea, stringFormat);
400 internal static SizeF MeasureString (string text, Font font, SizeF layoutArea, StringFormat stringFormat, out int charactersFitted, out int linesFilled)
402 return Hwnd.GraphicsContext.MeasureString (text, font, layoutArea, stringFormat, out charactersFitted, out linesFilled);
405 internal static Region[] MeasureCharacterRanges (string text, Font font, RectangleF layoutRect, StringFormat stringFormat)
407 return Hwnd.GraphicsContext.MeasureCharacterRanges (text, font, layoutRect, stringFormat);
410 internal static SizeF GetDpi ()
412 return new SizeF (Hwnd.GraphicsContext.DpiX, Hwnd.GraphicsContext.DpiY);
416 #region Private Methods
417 private static StringFormat FlagsToStringFormat (TextFormatFlags flags)
419 StringFormat sf = new StringFormat ();
421 // Translation table: http://msdn.microsoft.com/msdnmag/issues/06/03/TextRendering/default.aspx?fig=true#fig4
423 // Horizontal Alignment
424 if ((flags & TextFormatFlags.HorizontalCenter) == TextFormatFlags.HorizontalCenter)
425 sf.Alignment = StringAlignment.Center;
426 else if ((flags & TextFormatFlags.Right) == TextFormatFlags.Right)
427 sf.Alignment = StringAlignment.Far;
429 sf.Alignment = StringAlignment.Near;
431 // Vertical Alignment
432 if ((flags & TextFormatFlags.Bottom) == TextFormatFlags.Bottom)
433 sf.LineAlignment = StringAlignment.Far;
434 else if ((flags & TextFormatFlags.VerticalCenter) == TextFormatFlags.VerticalCenter)
435 sf.LineAlignment = StringAlignment.Center;
437 sf.LineAlignment = StringAlignment.Near;
440 if ((flags & TextFormatFlags.EndEllipsis) == TextFormatFlags.EndEllipsis)
441 sf.Trimming = StringTrimming.EllipsisCharacter;
442 else if ((flags & TextFormatFlags.PathEllipsis) == TextFormatFlags.PathEllipsis)
443 sf.Trimming = StringTrimming.EllipsisPath;
444 else if ((flags & TextFormatFlags.WordEllipsis) == TextFormatFlags.WordEllipsis)
445 sf.Trimming = StringTrimming.EllipsisWord;
447 sf.Trimming = StringTrimming.Character;
450 if ((flags & TextFormatFlags.NoPrefix) == TextFormatFlags.NoPrefix)
451 sf.HotkeyPrefix = HotkeyPrefix.None;
452 else if ((flags & TextFormatFlags.HidePrefix) == TextFormatFlags.HidePrefix)
453 sf.HotkeyPrefix = HotkeyPrefix.Hide;
455 sf.HotkeyPrefix = HotkeyPrefix.Show;
458 if ((flags & TextFormatFlags.NoPadding) == TextFormatFlags.NoPadding)
459 sf.FormatFlags |= StringFormatFlags.FitBlackBox;
462 if ((flags & TextFormatFlags.SingleLine) == TextFormatFlags.SingleLine)
463 sf.FormatFlags |= StringFormatFlags.NoWrap;
464 else if ((flags & TextFormatFlags.TextBoxControl) == TextFormatFlags.TextBoxControl)
465 sf.FormatFlags |= StringFormatFlags.LineLimit;
468 //if ((flags & TextFormatFlags.RightToLeft) == TextFormatFlags.RightToLeft)
469 // sf.FormatFlags |= StringFormatFlags.DirectionRightToLeft;
470 if ((flags & TextFormatFlags.NoClipping) == TextFormatFlags.NoClipping)
471 sf.FormatFlags |= StringFormatFlags.NoClip;
476 private static Rectangle PadRectangle (Rectangle r, TextFormatFlags flags)
478 if ((flags & TextFormatFlags.NoPadding) == 0 && (flags & TextFormatFlags.Right) == 0 && (flags & TextFormatFlags.HorizontalCenter) == 0) {
482 if ((flags & TextFormatFlags.NoPadding) == 0 && (flags & TextFormatFlags.Right) == TextFormatFlags.Right) {
485 if ((flags & TextFormatFlags.LeftAndRightPadding) == TextFormatFlags.LeftAndRightPadding) {
489 if ((flags & TextFormatFlags.WordEllipsis) == TextFormatFlags.WordEllipsis || (flags & TextFormatFlags.EndEllipsis) == TextFormatFlags.EndEllipsis || (flags & TextFormatFlags.WordBreak) == TextFormatFlags.WordBreak) {
492 if ((flags & TextFormatFlags.VerticalCenter) == TextFormatFlags.VerticalCenter) {
499 private static Rectangle PadDrawStringRectangle (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.NoPadding) == TextFormatFlags.NoPadding) {
511 if ((flags & TextFormatFlags.NoPadding) == 0 && (flags & TextFormatFlags.Bottom) == TextFormatFlags.Bottom) {
514 if ((flags & TextFormatFlags.LeftAndRightPadding) == TextFormatFlags.LeftAndRightPadding) {
518 if ((flags & TextFormatFlags.WordEllipsis) == TextFormatFlags.WordEllipsis || (flags & TextFormatFlags.EndEllipsis) == TextFormatFlags.EndEllipsis || (flags & TextFormatFlags.WordBreak) == TextFormatFlags.WordBreak) {
521 if ((flags & TextFormatFlags.VerticalCenter) == TextFormatFlags.VerticalCenter) {
529 #region DllImports (Windows)
530 [DllImport ("user32", CharSet = CharSet.Unicode, EntryPoint = "DrawText")]
531 static extern int Win32DrawText (IntPtr hdc, string lpStr, int nCount, ref XplatUIWin32.RECT lpRect, int wFormat);
533 [DllImport ("gdi32")]
534 static extern int SetTextColor (IntPtr hdc, int crColor);
536 [DllImport ("gdi32")]
537 static extern IntPtr SelectObject (IntPtr hDC, IntPtr hObject);
539 [DllImport ("gdi32")]
540 static extern int SetBkColor (IntPtr hdc, int crColor);
542 [DllImport ("gdi32")]
543 static extern int SetBkMode (IntPtr hdc, int iBkMode);
545 [DllImport ("gdi32")]
546 static extern bool DeleteObject (IntPtr objectHandle);
549 static extern bool SelectClipRgn(IntPtr hdc, IntPtr hrgn);