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);
80 static void DrawText (IDeviceContext dc, string text, Font font, Point pt, Color foreColor, TextFormatFlags flags)
82 DrawTextInternal (dc, text, font, pt, foreColor, Color.Transparent, flags, false);
86 public static void DrawText (IDeviceContext dc, string text, Font font, Rectangle bounds, Color foreColor, Color backColor)
88 DrawTextInternal (dc, text, font, bounds, foreColor, backColor, TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter, false);
92 public static void DrawText (IDeviceContext dc, string text, Font font, Rectangle bounds, Color foreColor, TextFormatFlags flags)
94 DrawTextInternal (dc, text, font, bounds, foreColor, Color.Transparent, flags, false);
98 public static void DrawText (IDeviceContext dc, string text, Font font, Point pt, Color foreColor, Color backColor, TextFormatFlags flags)
100 DrawTextInternal (dc, text, font, pt, foreColor, backColor, flags, false);
103 public static void DrawText (IDeviceContext dc, string text, Font font, Rectangle bounds, Color foreColor, Color backColor, TextFormatFlags flags)
105 DrawTextInternal (dc, text, font, bounds, foreColor, backColor, flags, false);
114 static Size MeasureText (string text, Font font)
116 return MeasureTextInternal (Hwnd.GraphicsContext, text, font, Size.Empty, TextFormatFlags.Default, false);
120 public static Size MeasureText (IDeviceContext dc, string text, Font font)
122 return MeasureTextInternal (dc, text, font, Size.Empty, TextFormatFlags.Default, false);
125 public static Size MeasureText (string text, Font font, Size proposedSize)
127 return MeasureTextInternal (Hwnd.GraphicsContext, text, font, proposedSize, TextFormatFlags.Default, false);
130 public static Size MeasureText (IDeviceContext dc, string text, Font font, Size proposedSize)
132 return MeasureTextInternal (dc, text, font, proposedSize, TextFormatFlags.Default, false);
135 public static Size MeasureText (string text, Font font, Size proposedSize, TextFormatFlags flags)
137 return MeasureTextInternal (Hwnd.GraphicsContext, text, font, proposedSize, flags, false);
140 public static Size MeasureText (IDeviceContext dc, string text, Font font, Size proposedSize, TextFormatFlags flags)
142 return MeasureTextInternal (dc, text, font, proposedSize, flags, false);
147 #region Internal Methods That Do Stuff
149 internal static void DrawTextInternal (IDeviceContext dc, string text, Font font, Rectangle bounds, Color foreColor, Color backColor, TextFormatFlags flags, bool useDrawString)
151 internal static void DrawTextInternal (Graphics dc, string text, Font font, Rectangle bounds, Color foreColor, Color backColor, TextFormatFlags flags, bool useDrawString)
155 throw new ArgumentNullException ("dc");
157 if (text == null || text.Length == 0)
160 // We use MS GDI API's unless told not to, or we aren't on Windows
161 if (!useDrawString && !XplatUI.RunningOnUnix) {
162 if ((flags & TextFormatFlags.VerticalCenter) == TextFormatFlags.VerticalCenter || (flags & TextFormatFlags.Bottom) == TextFormatFlags.Bottom)
163 flags |= TextFormatFlags.SingleLine;
165 // Calculate the text bounds (there is often padding added)
166 Rectangle new_bounds = PadRectangle (bounds, flags);
167 new_bounds.Offset ((int)(dc as Graphics).Transform.OffsetX, (int)(dc as Graphics).Transform.OffsetY);
169 IntPtr hdc = IntPtr.Zero;
170 bool clear_clip_region = false;
172 // If we need to use the graphics clipping region, add it to our hdc
173 if ((flags & TextFormatFlags.PreserveGraphicsClipping) == TextFormatFlags.PreserveGraphicsClipping) {
174 Graphics graphics = (Graphics)dc;
175 Region clip_region = graphics.Clip;
177 if (!clip_region.IsInfinite (graphics)) {
178 IntPtr hrgn = clip_region.GetHrgn (graphics);
180 SelectClipRgn (hdc, hrgn);
183 clear_clip_region = true;
187 if (hdc == IntPtr.Zero)
190 // Set the fore color
191 if (foreColor != Color.Empty)
192 SetTextColor (hdc, ColorTranslator.ToWin32 (foreColor));
194 // Set the back color
195 if (backColor != Color.Transparent && backColor != Color.Empty) {
196 SetBkMode (hdc, 2); //1-Transparent, 2-Opaque
197 SetBkColor (hdc, ColorTranslator.ToWin32 (backColor));
200 SetBkMode (hdc, 1); //1-Transparent, 2-Opaque
203 XplatUIWin32.RECT r = XplatUIWin32.RECT.FromRectangle (new_bounds);
208 prevobj = SelectObject (hdc, font.ToHfont ());
209 Win32DrawText (hdc, text, text.Length, ref r, (int)flags);
210 prevobj = SelectObject (hdc, prevobj);
211 DeleteObject (prevobj);
214 Win32DrawText (hdc, text, text.Length, ref r, (int)flags);
217 if (clear_clip_region)
218 SelectClipRgn (hdc, IntPtr.Zero);
226 // Use Graphics.DrawString as a fallback method
229 IntPtr hdc = IntPtr.Zero;
235 g = Graphics.FromHdc (hdc);
238 StringFormat sf = FlagsToStringFormat (flags);
240 Rectangle new_bounds = PadDrawStringRectangle (bounds, flags);
242 g.DrawString (text, font, ThemeEngine.Current.ResPool.GetSolidBrush (foreColor), new_bounds, sf);
244 if (!(dc is Graphics)) {
249 if (hdc != IntPtr.Zero) dc.ReleaseHdc (hdc);
256 internal static Size MeasureTextInternal (IDeviceContext dc, string text, Font font, Size proposedSize, TextFormatFlags flags, bool useMeasureString)
258 internal static Size MeasureTextInternal (Graphics dc, string text, Font font, Size proposedSize, TextFormatFlags flags, bool useMeasureString)
261 if (!useMeasureString && !XplatUI.RunningOnUnix) {
262 // Tell DrawText to calculate size instead of draw
263 flags |= (TextFormatFlags)1024; // DT_CALCRECT
265 IntPtr hdc = dc.GetHdc ();
267 XplatUIWin32.RECT r = XplatUIWin32.RECT.FromRectangle (new Rectangle (Point.Empty, proposedSize));
272 prevobj = SelectObject (hdc, font.ToHfont ());
273 Win32DrawText (hdc, text, text.Length, ref r, (int)flags);
274 prevobj = SelectObject (hdc, prevobj);
275 DeleteObject (prevobj);
278 Win32DrawText (hdc, text, text.Length, ref r, (int)flags);
287 // Really, I am just making something up here, which as far as I can tell, MS
288 // just makes something up as well. This will require lots of tweaking to match MS. :(
289 Size retval = r.ToRectangle ().Size;
291 if (retval.Width > 0 && (flags & TextFormatFlags.NoPadding) == 0) {
293 retval.Width += (int)retval.Height / 8;
299 StringFormat sf = FlagsToStringFormat (flags);
304 retval = (dc as Graphics).MeasureString (text, font, proposedSize.Width == 0 ? Int32.MaxValue : proposedSize.Width, sf).ToSize ();
306 retval = TextRenderer.MeasureString (text, font, proposedSize.Width == 0 ? Int32.MaxValue : proposedSize.Width, sf).ToSize ();
308 if (retval.Width > 0 && (flags & TextFormatFlags.NoPadding) == 0)
316 #region Internal Methods That Are Just Overloads
318 internal static void DrawTextInternal (IDeviceContext dc, string text, Font font, Point pt, Color foreColor, bool useDrawString)
320 DrawTextInternal (dc, text, font, pt, foreColor, Color.Transparent, TextFormatFlags.Default, useDrawString);
323 internal static void DrawTextInternal (IDeviceContext dc, string text, Font font, Rectangle bounds, Color foreColor, bool useDrawString)
325 DrawTextInternal (dc, text, font, bounds, foreColor, Color.Transparent, TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter, useDrawString);
328 internal static void DrawTextInternal (IDeviceContext dc, string text, Font font, Point pt, Color foreColor, Color backColor, bool useDrawString)
330 DrawTextInternal (dc, text, font, pt, foreColor, backColor, TextFormatFlags.Default, useDrawString);
333 internal static void DrawTextInternal (IDeviceContext dc, string text, Font font, Point pt, Color foreColor, TextFormatFlags flags, bool useDrawString)
335 DrawTextInternal (dc, text, font, pt, foreColor, Color.Transparent, flags, useDrawString);
338 internal static void DrawTextInternal (IDeviceContext dc, string text, Font font, Rectangle bounds, Color foreColor, Color backColor, bool useDrawString)
340 DrawTextInternal (dc, text, font, bounds, foreColor, backColor, TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter, useDrawString);
343 internal static void DrawTextInternal (IDeviceContext dc, string text, Font font, Rectangle bounds, Color foreColor, TextFormatFlags flags, bool useDrawString)
345 internal static void DrawTextInternal (Graphics dc, string text, Font font, Rectangle bounds, Color foreColor, TextFormatFlags flags, bool useDrawString)
348 DrawTextInternal (dc, text, font, bounds, foreColor, Color.Transparent, flags, useDrawString);
351 internal static Size MeasureTextInternal (string text, Font font, bool useMeasureString)
353 return MeasureTextInternal (Hwnd.GraphicsContext, text, font, Size.Empty, TextFormatFlags.Default, useMeasureString);
356 internal static void DrawTextInternal (IDeviceContext dc, string text, Font font, Point pt, Color foreColor, Color backColor, TextFormatFlags flags, bool useDrawString)
358 Size sz = MeasureTextInternal (dc, text, font, useDrawString);
359 DrawTextInternal (dc, text, font, new Rectangle (pt, sz), foreColor, backColor, flags, useDrawString);
362 internal static Size MeasureTextInternal (IDeviceContext dc, string text, Font font, bool useMeasureString)
364 return MeasureTextInternal (dc, text, font, Size.Empty, TextFormatFlags.Default, useMeasureString);
368 internal static Size MeasureTextInternal (string text, Font font, Size proposedSize, bool useMeasureString)
370 return MeasureTextInternal (Hwnd.GraphicsContext, text, font, proposedSize, TextFormatFlags.Default, useMeasureString);
373 internal static Size MeasureTextInternal (IDeviceContext dc, string text, Font font, Size proposedSize, bool useMeasureString)
375 return MeasureTextInternal (dc, text, font, proposedSize, TextFormatFlags.Default, useMeasureString);
379 internal static Size MeasureTextInternal (string text, Font font, Size proposedSize, TextFormatFlags flags, bool useMeasureString)
381 return MeasureTextInternal (Hwnd.GraphicsContext, text, font, proposedSize, flags, useMeasureString);
385 #region Thread-Safe Static Graphics Methods
386 internal static SizeF MeasureString (string text, Font font)
388 return Hwnd.GraphicsContext.MeasureString (text, font);
391 internal static SizeF MeasureString (string text, Font font, int width)
393 return Hwnd.GraphicsContext.MeasureString (text, font, width);
396 internal static SizeF MeasureString (string text, Font font, SizeF layoutArea)
398 return Hwnd.GraphicsContext.MeasureString (text, font, layoutArea);
401 internal static SizeF MeasureString (string text, Font font, int width, StringFormat format)
403 return Hwnd.GraphicsContext.MeasureString (text, font, width, format);
406 internal static SizeF MeasureString (string text, Font font, PointF origin, StringFormat stringFormat)
408 return Hwnd.GraphicsContext.MeasureString (text, font, origin, stringFormat);
411 internal static SizeF MeasureString (string text, Font font, SizeF layoutArea, StringFormat stringFormat)
413 return Hwnd.GraphicsContext.MeasureString (text, font, layoutArea, stringFormat);
416 internal static SizeF MeasureString (string text, Font font, SizeF layoutArea, StringFormat stringFormat, out int charactersFitted, out int linesFilled)
418 return Hwnd.GraphicsContext.MeasureString (text, font, layoutArea, stringFormat, out charactersFitted, out linesFilled);
421 internal static Region[] MeasureCharacterRanges (string text, Font font, RectangleF layoutRect, StringFormat stringFormat)
423 return Hwnd.GraphicsContext.MeasureCharacterRanges (text, font, layoutRect, stringFormat);
426 internal static SizeF GetDpi ()
428 return new SizeF (Hwnd.GraphicsContext.DpiX, Hwnd.GraphicsContext.DpiY);
432 #region Private Methods
433 private static StringFormat FlagsToStringFormat (TextFormatFlags flags)
435 StringFormat sf = new StringFormat ();
437 // Translation table: http://msdn.microsoft.com/msdnmag/issues/06/03/TextRendering/default.aspx?fig=true#fig4
439 // Horizontal Alignment
440 if ((flags & TextFormatFlags.HorizontalCenter) == TextFormatFlags.HorizontalCenter)
441 sf.Alignment = StringAlignment.Center;
442 else if ((flags & TextFormatFlags.Right) == TextFormatFlags.Right)
443 sf.Alignment = StringAlignment.Far;
445 sf.Alignment = StringAlignment.Near;
447 // Vertical Alignment
448 if ((flags & TextFormatFlags.Bottom) == TextFormatFlags.Bottom)
449 sf.LineAlignment = StringAlignment.Far;
450 else if ((flags & TextFormatFlags.VerticalCenter) == TextFormatFlags.VerticalCenter)
451 sf.LineAlignment = StringAlignment.Center;
453 sf.LineAlignment = StringAlignment.Near;
456 if ((flags & TextFormatFlags.EndEllipsis) == TextFormatFlags.EndEllipsis)
457 sf.Trimming = StringTrimming.EllipsisCharacter;
458 else if ((flags & TextFormatFlags.PathEllipsis) == TextFormatFlags.PathEllipsis)
459 sf.Trimming = StringTrimming.EllipsisPath;
460 else if ((flags & TextFormatFlags.WordEllipsis) == TextFormatFlags.WordEllipsis)
461 sf.Trimming = StringTrimming.EllipsisWord;
463 sf.Trimming = StringTrimming.Character;
466 if ((flags & TextFormatFlags.NoPrefix) == TextFormatFlags.NoPrefix)
467 sf.HotkeyPrefix = HotkeyPrefix.None;
468 else if ((flags & TextFormatFlags.HidePrefix) == TextFormatFlags.HidePrefix)
469 sf.HotkeyPrefix = HotkeyPrefix.Hide;
471 sf.HotkeyPrefix = HotkeyPrefix.Show;
474 if ((flags & TextFormatFlags.NoPadding) == TextFormatFlags.NoPadding)
475 sf.FormatFlags |= StringFormatFlags.FitBlackBox;
478 if ((flags & TextFormatFlags.SingleLine) == TextFormatFlags.SingleLine)
479 sf.FormatFlags |= StringFormatFlags.NoWrap;
480 else if ((flags & TextFormatFlags.TextBoxControl) == TextFormatFlags.TextBoxControl)
481 sf.FormatFlags |= StringFormatFlags.LineLimit;
484 //if ((flags & TextFormatFlags.RightToLeft) == TextFormatFlags.RightToLeft)
485 // sf.FormatFlags |= StringFormatFlags.DirectionRightToLeft;
486 if ((flags & TextFormatFlags.NoClipping) == TextFormatFlags.NoClipping)
487 sf.FormatFlags |= StringFormatFlags.NoClip;
492 private static Rectangle PadRectangle (Rectangle r, TextFormatFlags flags)
494 if ((flags & TextFormatFlags.NoPadding) == 0 && (flags & TextFormatFlags.Right) == 0 && (flags & TextFormatFlags.HorizontalCenter) == 0) {
498 if ((flags & TextFormatFlags.NoPadding) == 0 && (flags & TextFormatFlags.Right) == TextFormatFlags.Right) {
501 if ((flags & TextFormatFlags.LeftAndRightPadding) == TextFormatFlags.LeftAndRightPadding) {
505 if ((flags & TextFormatFlags.WordEllipsis) == TextFormatFlags.WordEllipsis || (flags & TextFormatFlags.EndEllipsis) == TextFormatFlags.EndEllipsis || (flags & TextFormatFlags.WordBreak) == TextFormatFlags.WordBreak) {
508 if ((flags & TextFormatFlags.VerticalCenter) == TextFormatFlags.VerticalCenter) {
515 private static Rectangle PadDrawStringRectangle (Rectangle r, TextFormatFlags flags)
517 if ((flags & TextFormatFlags.NoPadding) == 0 && (flags & TextFormatFlags.Right) == 0 && (flags & TextFormatFlags.HorizontalCenter) == 0) {
521 if ((flags & TextFormatFlags.NoPadding) == 0 && (flags & TextFormatFlags.Right) == TextFormatFlags.Right) {
524 if ((flags & TextFormatFlags.NoPadding) == TextFormatFlags.NoPadding) {
527 if ((flags & TextFormatFlags.NoPadding) == 0 && (flags & TextFormatFlags.Bottom) == TextFormatFlags.Bottom) {
530 if ((flags & TextFormatFlags.LeftAndRightPadding) == TextFormatFlags.LeftAndRightPadding) {
534 if ((flags & TextFormatFlags.WordEllipsis) == TextFormatFlags.WordEllipsis || (flags & TextFormatFlags.EndEllipsis) == TextFormatFlags.EndEllipsis || (flags & TextFormatFlags.WordBreak) == TextFormatFlags.WordBreak) {
537 if ((flags & TextFormatFlags.VerticalCenter) == TextFormatFlags.VerticalCenter) {
545 #region DllImports (Windows)
546 [DllImport ("user32", CharSet = CharSet.Unicode, EntryPoint = "DrawText")]
547 static extern int Win32DrawText (IntPtr hdc, string lpStr, int nCount, ref XplatUIWin32.RECT lpRect, int wFormat);
549 [DllImport ("gdi32")]
550 static extern int SetTextColor (IntPtr hdc, int crColor);
552 [DllImport ("gdi32")]
553 static extern IntPtr SelectObject (IntPtr hDC, IntPtr hObject);
555 [DllImport ("gdi32")]
556 static extern int SetBkColor (IntPtr hdc, int crColor);
558 [DllImport ("gdi32")]
559 static extern int SetBkMode (IntPtr hdc, int iBkMode);
561 [DllImport ("gdi32")]
562 static extern bool DeleteObject (IntPtr objectHandle);
565 static extern bool SelectClipRgn(IntPtr hdc, IntPtr hrgn);