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)
30 using System.Runtime.InteropServices;
32 using System.Drawing.Text;
34 namespace System.Windows.Forms
41 private static Bitmap measure_bitmap = new Bitmap (1, 1);
43 private TextRenderer ()
47 #region Public Methods
49 public static void DrawText (IDeviceContext dc, string text, Font font, Point pt, Color foreColor)
51 DrawTextInternal (dc, text, font, pt, foreColor, Color.Transparent, TextFormatFlags.Default, false);
54 public static void DrawText (IDeviceContext dc, string text, Font font, Rectangle bounds, Color foreColor)
56 DrawTextInternal (dc, text, font, bounds, foreColor, Color.Transparent, TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter, false);
59 public static void DrawText (IDeviceContext dc, string text, Font font, Point pt, Color foreColor, Color backColor)
61 DrawTextInternal (dc, text, font, pt, foreColor, backColor, TextFormatFlags.Default, false);
64 public static void DrawText (IDeviceContext dc, string text, Font font, Point pt, Color foreColor, TextFormatFlags flags)
66 DrawTextInternal (dc, text, font, pt, foreColor, Color.Transparent, flags, false);
69 public static void DrawText (IDeviceContext dc, string text, Font font, Rectangle bounds, Color foreColor, Color backColor)
71 DrawTextInternal (dc, text, font, bounds, foreColor, backColor, TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter, false);
74 public static void DrawText (IDeviceContext dc, string text, Font font, Rectangle bounds, Color foreColor, TextFormatFlags flags)
76 DrawTextInternal (dc, text, font, bounds, foreColor, Color.Transparent, flags, false);
79 public static void DrawText (IDeviceContext dc, string text, Font font, Point pt, Color foreColor, Color backColor, TextFormatFlags flags)
81 DrawTextInternal (dc, text, font, pt, foreColor, backColor, flags, false);
84 public static void DrawText (IDeviceContext dc, string text, Font font, Rectangle bounds, Color foreColor, Color backColor, TextFormatFlags flags)
86 DrawTextInternal (dc, text, font, bounds, foreColor, backColor, flags, false);
89 public static Size MeasureText (string text, Font font)
91 return MeasureTextInternal (Graphics.FromImage (measure_bitmap), text, font, Size.Empty, TextFormatFlags.Default, false);
94 public static Size MeasureText (IDeviceContext dc, string text, Font font)
96 return MeasureTextInternal (dc, text, font, Size.Empty, TextFormatFlags.Default, false);
99 public static Size MeasureText (string text, Font font, Size proposedSize)
101 return MeasureTextInternal (Graphics.FromImage (measure_bitmap), text, font, proposedSize, TextFormatFlags.Default, false);
104 public static Size MeasureText (IDeviceContext dc, string text, Font font, Size proposedSize)
106 return MeasureTextInternal (dc, text, font, proposedSize, TextFormatFlags.Default, false);
109 public static Size MeasureText (string text, Font font, Size proposedSize, TextFormatFlags flags)
111 return MeasureTextInternal (Graphics.FromImage (measure_bitmap), text, font, proposedSize, flags, false);
114 public static Size MeasureText (IDeviceContext dc, string text, Font font, Size proposedSize, TextFormatFlags flags)
116 return MeasureTextInternal (dc, text, font, proposedSize, flags, false);
121 #region Internal Methods That Do Stuff
123 internal static void DrawTextInternal (IDeviceContext dc, string text, Font font, Rectangle bounds, Color foreColor, Color backColor, TextFormatFlags flags, bool useDrawString)
125 internal static void DrawTextInternal (Graphics dc, string text, Font font, Rectangle bounds, Color foreColor, Color backColor, TextFormatFlags flags, bool useDrawString)
130 throw new ArgumentNullException ("dc");
132 if (string.IsNullOrEmpty (text))
135 // We use MS GDI API's unless told not to, or we aren't on Windows
136 if (!useDrawString && (Environment.OSVersion.Platform == PlatformID.Win32NT || Environment.OSVersion.Platform == PlatformID.Win32Windows)) {
137 if ((flags & TextFormatFlags.VerticalCenter) == TextFormatFlags.VerticalCenter || (flags & TextFormatFlags.Bottom) == TextFormatFlags.Bottom)
138 flags |= TextFormatFlags.SingleLine;
140 // Calculate the text bounds (there is often padding added)
141 Rectangle new_bounds = PadRectangle (bounds, flags);
142 new_bounds.Offset ((int)(dc as Graphics).Transform.OffsetX, (int)(dc as Graphics).Transform.OffsetY);
144 IntPtr hdc = IntPtr.Zero;
145 bool clear_clip_region = false;
147 // If we need to use the graphics clipping region, add it to our hdc
148 if ((flags & TextFormatFlags.PreserveGraphicsClipping) == TextFormatFlags.PreserveGraphicsClipping) {
149 Graphics graphics = (Graphics)dc;
150 Region clip_region = graphics.Clip;
152 if (!clip_region.IsInfinite (graphics)) {
153 IntPtr hrgn = clip_region.GetHrgn (graphics);
155 SelectClipRgn (hdc, hrgn);
156 clip_region.ReleaseHrgn (hrgn);
157 clear_clip_region = true;
161 if (hdc == IntPtr.Zero)
164 // Set the fore color
165 if (foreColor != Color.Empty)
166 SetTextColor (hdc, ColorTranslator.ToWin32 (foreColor));
168 // Set the back color
169 if (backColor != Color.Transparent && backColor != Color.Empty) {
170 SetBkMode (hdc, 2); //1-Transparent, 2-Opaque
171 SetBkColor (hdc, ColorTranslator.ToWin32 (backColor));
174 SetBkMode (hdc, 1); //1-Transparent, 2-Opaque
177 XplatUIWin32.RECT r = XplatUIWin32.RECT.FromRectangle (new_bounds);
182 prevobj = SelectObject (hdc, font.ToHfont ());
183 Win32DrawText (hdc, text, text.Length, ref r, (int)flags);
184 prevobj = SelectObject (hdc, prevobj);
185 DeleteObject (prevobj);
188 Win32DrawText (hdc, text, text.Length, ref r, (int)flags);
191 if (clear_clip_region)
192 SelectClipRgn (hdc, IntPtr.Zero);
196 // Use Graphics.DrawString as a fallback method
205 g = Graphics.FromHdc (dc.GetHdc ());
211 StringFormat sf = FlagsToStringFormat (flags);
213 Rectangle new_bounds = PadDrawStringRectangle (bounds, flags);
215 g.DrawString (text, font, ThemeEngine.Current.ResPool.GetSolidBrush (foreColor), new_bounds, sf);
218 if (!(dc is Graphics)) {
227 internal static Size MeasureTextInternal (IDeviceContext dc, string text, Font font, Size proposedSize, TextFormatFlags flags, bool useMeasureString)
229 internal static Size MeasureTextInternal (Graphics dc, string text, Font font, Size proposedSize, TextFormatFlags flags, bool useMeasureString)
233 if (!useMeasureString && (Environment.OSVersion.Platform == PlatformID.Win32NT || Environment.OSVersion.Platform == PlatformID.Win32Windows)) {
234 // Tell DrawText to calculate size instead of draw
235 flags |= (TextFormatFlags)1024; // DT_CALCRECT
237 IntPtr hdc = dc.GetHdc ();
239 XplatUIWin32.RECT r = XplatUIWin32.RECT.FromRectangle (new Rectangle (Point.Empty, proposedSize));
244 prevobj = SelectObject (hdc, font.ToHfont ());
245 Win32DrawText (hdc, text, text.Length, ref r, (int)flags);
246 prevobj = SelectObject (hdc, prevobj);
247 DeleteObject (prevobj);
250 Win32DrawText (hdc, text, text.Length, ref r, (int)flags);
255 // Really, I am just making something up here, which as far as I can tell, MS
256 // just makes something up as well. This will require lots of tweaking to match MS. :(
257 Size retval = r.ToRectangle ().Size;
259 if (retval.Width > 0 && (flags & TextFormatFlags.NoPadding) == 0) {
261 retval.Width += (int)retval.Height / 8;
268 Graphics g = Graphics.FromImage (measure_bitmap);
270 Size retval = g.MeasureString (text, font).ToSize ();
272 if (retval.Width > 0 && (flags & TextFormatFlags.NoPadding) == 0)
282 #region Internal Methods That Are Just Overloads
284 internal static void DrawTextInternal (IDeviceContext dc, string text, Font font, Point pt, Color foreColor, bool useDrawString)
286 DrawTextInternal (dc, text, font, pt, foreColor, Color.Transparent, TextFormatFlags.Default, useDrawString);
289 internal static void DrawTextInternal (IDeviceContext dc, string text, Font font, Rectangle bounds, Color foreColor, bool useDrawString)
291 DrawTextInternal (dc, text, font, bounds, foreColor, Color.Transparent, TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter, useDrawString);
294 internal static void DrawTextInternal (IDeviceContext dc, string text, Font font, Point pt, Color foreColor, Color backColor, bool useDrawString)
296 DrawTextInternal (dc, text, font, pt, foreColor, backColor, TextFormatFlags.Default, useDrawString);
299 internal static void DrawTextInternal (IDeviceContext dc, string text, Font font, Point pt, Color foreColor, TextFormatFlags flags, bool useDrawString)
301 DrawTextInternal (dc, text, font, pt, foreColor, Color.Transparent, flags, useDrawString);
304 internal static void DrawTextInternal (IDeviceContext dc, string text, Font font, Rectangle bounds, Color foreColor, Color backColor, bool useDrawString)
306 DrawTextInternal (dc, text, font, bounds, foreColor, backColor, TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter, useDrawString);
309 internal static void DrawTextInternal (IDeviceContext dc, string text, Font font, Rectangle bounds, Color foreColor, TextFormatFlags flags, bool useDrawString)
311 internal static void DrawTextInternal (Graphics dc, string text, Font font, Rectangle bounds, Color foreColor, TextFormatFlags flags, bool useDrawString)
314 DrawTextInternal (dc, text, font, bounds, foreColor, Color.Transparent, flags, useDrawString);
318 internal static void DrawTextInternal (IDeviceContext dc, string text, Font font, Point pt, Color foreColor, Color backColor, TextFormatFlags flags, bool useDrawString)
320 Size sz = MeasureTextInternal (dc, text, font, useDrawString);
321 DrawTextInternal (dc, text, font, new Rectangle (pt, sz), foreColor, backColor, flags, useDrawString);
324 internal static Size MeasureTextInternal (string text, Font font, bool useMeasureString)
326 return MeasureTextInternal (Graphics.FromImage (measure_bitmap), text, font, Size.Empty, TextFormatFlags.Default, useMeasureString);
329 internal static Size MeasureTextInternal (IDeviceContext dc, string text, Font font, bool useMeasureString)
331 return MeasureTextInternal (dc, text, font, Size.Empty, TextFormatFlags.Default, useMeasureString);
334 internal static Size MeasureTextInternal (string text, Font font, Size proposedSize, bool useMeasureString)
336 return MeasureTextInternal (Graphics.FromImage (measure_bitmap), text, font, proposedSize, TextFormatFlags.Default, useMeasureString);
339 internal static Size MeasureTextInternal (IDeviceContext dc, string text, Font font, Size proposedSize, bool useMeasureString)
341 return MeasureTextInternal (dc, text, font, proposedSize, TextFormatFlags.Default, useMeasureString);
345 internal static Size MeasureTextInternal (string text, Font font, Size proposedSize, TextFormatFlags flags, bool useMeasureString)
347 return MeasureTextInternal (Graphics.FromImage (measure_bitmap), text, font, proposedSize, flags, useMeasureString);
351 #region Private Methods
352 private static StringFormat FlagsToStringFormat (TextFormatFlags flags)
354 StringFormat sf = new StringFormat ();
356 // Translation table: http://msdn.microsoft.com/msdnmag/issues/06/03/TextRendering/default.aspx?fig=true#fig4
358 // Horizontal Alignment
359 if ((flags & TextFormatFlags.HorizontalCenter) == TextFormatFlags.HorizontalCenter)
360 sf.Alignment = StringAlignment.Center;
361 else if ((flags & TextFormatFlags.Right) == TextFormatFlags.Right)
362 sf.Alignment = StringAlignment.Far;
364 sf.Alignment = StringAlignment.Near;
366 // Vertical Alignment
367 if ((flags & TextFormatFlags.Bottom) == TextFormatFlags.Bottom)
368 sf.LineAlignment = StringAlignment.Far;
369 else if ((flags & TextFormatFlags.VerticalCenter) == TextFormatFlags.VerticalCenter)
370 sf.LineAlignment = StringAlignment.Center;
372 sf.LineAlignment = StringAlignment.Near;
375 if ((flags & TextFormatFlags.EndEllipsis) == TextFormatFlags.EndEllipsis)
376 sf.Trimming = StringTrimming.EllipsisCharacter;
377 else if ((flags & TextFormatFlags.PathEllipsis) == TextFormatFlags.PathEllipsis)
378 sf.Trimming = StringTrimming.EllipsisPath;
379 else if ((flags & TextFormatFlags.WordEllipsis) == TextFormatFlags.WordEllipsis)
380 sf.Trimming = StringTrimming.EllipsisWord;
382 sf.Trimming = StringTrimming.Character;
385 if ((flags & TextFormatFlags.NoPrefix) == TextFormatFlags.NoPrefix)
386 sf.HotkeyPrefix = HotkeyPrefix.None;
387 else if ((flags & TextFormatFlags.HidePrefix) == TextFormatFlags.HidePrefix)
388 sf.HotkeyPrefix = HotkeyPrefix.Hide;
390 sf.HotkeyPrefix = HotkeyPrefix.Show;
393 if ((flags & TextFormatFlags.NoPadding) == TextFormatFlags.NoPadding)
394 sf.FormatFlags |= StringFormatFlags.FitBlackBox;
397 if ((flags & TextFormatFlags.SingleLine) == TextFormatFlags.SingleLine)
398 sf.FormatFlags |= StringFormatFlags.NoWrap;
399 else if ((flags & TextFormatFlags.TextBoxControl) == TextFormatFlags.TextBoxControl)
400 sf.FormatFlags |= StringFormatFlags.LineLimit;
403 //if ((flags & TextFormatFlags.RightToLeft) == TextFormatFlags.RightToLeft)
404 // sf.FormatFlags |= StringFormatFlags.DirectionRightToLeft;
405 if ((flags & TextFormatFlags.NoClipping) == TextFormatFlags.NoClipping)
406 sf.FormatFlags |= StringFormatFlags.NoClip;
407 //sf.FormatFlags |= StringFormatFlags.NoClip;
409 if ((flags & TextFormatFlags.WordBreak) == 0)
410 sf.FormatFlags |= StringFormatFlags.LineLimit;
415 private static Rectangle PadRectangle (Rectangle r, TextFormatFlags flags)
417 if ((flags & TextFormatFlags.NoPadding) == 0 && (flags & TextFormatFlags.Right) == 0 && (flags & TextFormatFlags.HorizontalCenter) == 0) {
421 if ((flags & TextFormatFlags.NoPadding) == 0 && (flags & TextFormatFlags.Right) == TextFormatFlags.Right) {
424 if ((flags & TextFormatFlags.LeftAndRightPadding) == TextFormatFlags.LeftAndRightPadding) {
428 if ((flags & TextFormatFlags.WordEllipsis) == TextFormatFlags.WordEllipsis || (flags & TextFormatFlags.EndEllipsis) == TextFormatFlags.EndEllipsis || (flags & TextFormatFlags.WordBreak) == TextFormatFlags.WordBreak) {
431 if ((flags & TextFormatFlags.VerticalCenter) == TextFormatFlags.VerticalCenter) {
438 private static Rectangle PadDrawStringRectangle (Rectangle r, TextFormatFlags flags)
440 if ((flags & TextFormatFlags.NoPadding) == 0 && (flags & TextFormatFlags.Right) == 0 && (flags & TextFormatFlags.HorizontalCenter) == 0) {
444 if ((flags & TextFormatFlags.NoPadding) == 0 && (flags & TextFormatFlags.Right) == TextFormatFlags.Right) {
447 if ((flags & TextFormatFlags.NoPadding) == TextFormatFlags.NoPadding) {
450 if ((flags & TextFormatFlags.NoPadding) == 0 && (flags & TextFormatFlags.Bottom) == TextFormatFlags.Bottom) {
453 if ((flags & TextFormatFlags.LeftAndRightPadding) == TextFormatFlags.LeftAndRightPadding) {
457 if ((flags & TextFormatFlags.WordEllipsis) == TextFormatFlags.WordEllipsis || (flags & TextFormatFlags.EndEllipsis) == TextFormatFlags.EndEllipsis || (flags & TextFormatFlags.WordBreak) == TextFormatFlags.WordBreak) {
460 if ((flags & TextFormatFlags.VerticalCenter) == TextFormatFlags.VerticalCenter) {
468 #region DllImports (Windows)
470 [DllImport ("user32", CharSet = CharSet.Unicode, EntryPoint = "DrawText")]
471 static extern int Win32DrawText (IntPtr hdc, string lpStr, int nCount, ref XplatUIWin32.RECT lpRect, int wFormat);
473 [DllImport ("gdi32")]
474 static extern int SetTextColor (IntPtr hdc, int crColor);
476 [DllImport ("gdi32")]
477 static extern IntPtr SelectObject (IntPtr hDC, IntPtr hObject);
479 [DllImport ("gdi32")]
480 static extern int SetBkColor (IntPtr hdc, int crColor);
482 [DllImport ("gdi32")]
483 static extern int SetBkMode (IntPtr hdc, int iBkMode);
485 [DllImport ("gdi32")]
486 static extern bool DeleteObject (IntPtr objectHandle);
489 static extern bool SelectClipRgn(IntPtr hdc, IntPtr hrgn);