1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4 
5 using System.ComponentModel;
6 using System.Diagnostics;
7 using System.Drawing.Internal;
8 using System.Globalization;
9 using System.Runtime.InteropServices;
10 using System.Runtime.Serialization;
11 
12 namespace System.Drawing
13 {
14     /// <summary>
15     /// Defines a particular format for text, including font face, size, and style attributes.
16     /// </summary>
17     public sealed partial class Font : MarshalByRefObject, ICloneable, IDisposable, ISerializable
18     {
19         private const int LogFontCharSetOffset = 23;
20         private const int LogFontNameOffset = 28;
21 
22         private IntPtr _nativeFont;
23         private float _fontSize;
24         private FontStyle _fontStyle;
25         private FontFamily _fontFamily;
26         private GraphicsUnit _fontUnit;
27         private byte _gdiCharSet = SafeNativeMethods.DEFAULT_CHARSET;
28         private bool _gdiVerticalFont;
29         private string _systemFontName = "";
30         private string _originalFontName;
31 
32         ///<summary>
33         /// Creates the GDI+ native font object.
34         ///</summary>
CreateNativeFont()35         private void CreateNativeFont()
36         {
37             Debug.Assert(_nativeFont == IntPtr.Zero, "nativeFont already initialized, this will generate a handle leak.");
38             Debug.Assert(_fontFamily != null, "fontFamily not initialized.");
39 
40             // Note: GDI+ creates singleton font family objects (from the corresponding font file) and reference count them so
41             // if creating the font object from an external FontFamily, this object's FontFamily will share the same native object.
42             int status = SafeNativeMethods.Gdip.GdipCreateFont(
43                                     new HandleRef(this, _fontFamily.NativeFamily),
44                                     _fontSize,
45                                     _fontStyle,
46                                     _fontUnit,
47                                     out _nativeFont);
48 
49             // Special case this common error message to give more information
50             if (status == SafeNativeMethods.Gdip.FontStyleNotFound)
51             {
52                 throw new ArgumentException(SR.Format(SR.GdiplusFontStyleNotFound, _fontFamily.Name, _fontStyle.ToString()));
53             }
54             else if (status != SafeNativeMethods.Gdip.Ok)
55             {
56                 throw SafeNativeMethods.Gdip.StatusException(status);
57             }
58         }
59 
60         /// <summary>
61         /// Initializes a new instance of the <see cref='Font'/> class from the specified existing <see cref='Font'/>
62         /// and <see cref='FontStyle'/>.
63         /// </summary>
Font(Font prototype, FontStyle newStyle)64         public Font(Font prototype, FontStyle newStyle)
65         {
66             // Copy over the originalFontName because it won't get initialized
67             _originalFontName = prototype.OriginalFontName;
68             Initialize(prototype.FontFamily, prototype.Size, newStyle, prototype.Unit, SafeNativeMethods.DEFAULT_CHARSET, false);
69         }
70 
71         /// <summary>
72         /// Initializes a new instance of the <see cref='Font'/> class with the specified attributes.
73         /// </summary>
Font(FontFamily family, float emSize, FontStyle style, GraphicsUnit unit)74         public Font(FontFamily family, float emSize, FontStyle style, GraphicsUnit unit)
75         {
76             Initialize(family, emSize, style, unit, SafeNativeMethods.DEFAULT_CHARSET, false);
77         }
78 
79         /// <summary>
80         /// Initializes a new instance of the <see cref='Font'/> class with the specified attributes.
81         /// </summary>
Font(FontFamily family, float emSize, FontStyle style, GraphicsUnit unit, byte gdiCharSet)82         public Font(FontFamily family, float emSize, FontStyle style, GraphicsUnit unit, byte gdiCharSet)
83         {
84             Initialize(family, emSize, style, unit, gdiCharSet, false);
85         }
86 
87         /// <summary>
88         /// Initializes a new instance of the <see cref='Font'/> class with the specified attributes.
89         /// </summary>
Font(FontFamily family, float emSize, FontStyle style, GraphicsUnit unit, byte gdiCharSet, bool gdiVerticalFont)90         public Font(FontFamily family, float emSize, FontStyle style, GraphicsUnit unit, byte gdiCharSet, bool gdiVerticalFont)
91         {
92             Initialize(family, emSize, style, unit, gdiCharSet, gdiVerticalFont);
93         }
94 
95         /// <summary>
96         /// Initializes a new instance of the <see cref='Font'/> class with the specified attributes.
97         /// </summary>
Font(string familyName, float emSize, FontStyle style, GraphicsUnit unit, byte gdiCharSet)98         public Font(string familyName, float emSize, FontStyle style, GraphicsUnit unit, byte gdiCharSet)
99         {
100             Initialize(familyName, emSize, style, unit, gdiCharSet, IsVerticalName(familyName));
101         }
102 
103         /// <summary>
104         /// Initializes a new instance of the <see cref='Font'/> class with the specified attributes.
105         /// </summary>
Font(string familyName, float emSize, FontStyle style, GraphicsUnit unit, byte gdiCharSet, bool gdiVerticalFont)106         public Font(string familyName, float emSize, FontStyle style, GraphicsUnit unit, byte gdiCharSet, bool gdiVerticalFont)
107         {
108             if (float.IsNaN(emSize) || float.IsInfinity(emSize) || emSize <= 0)
109             {
110                 throw new ArgumentException(SR.Format(SR.InvalidBoundArgument, "emSize", emSize, 0, "System.Single.MaxValue"), "emSize");
111             }
112 
113             Initialize(familyName, emSize, style, unit, gdiCharSet, gdiVerticalFont);
114         }
115 
116         /// <summary>
117         /// Initializes a new instance of the <see cref='Font'/> class with the specified attributes.
118         /// </summary>
Font(FontFamily family, float emSize, FontStyle style)119         public Font(FontFamily family, float emSize, FontStyle style)
120         {
121             Initialize(family, emSize, style, GraphicsUnit.Point, SafeNativeMethods.DEFAULT_CHARSET, false);
122         }
123 
124         /// <summary>
125         /// Initializes a new instance of the <see cref='Font'/> class with the specified attributes.
126         /// </summary>
Font(FontFamily family, float emSize, GraphicsUnit unit)127         public Font(FontFamily family, float emSize, GraphicsUnit unit)
128         {
129             Initialize(family, emSize, FontStyle.Regular, unit, SafeNativeMethods.DEFAULT_CHARSET, false);
130         }
131 
132         /// <summary>
133         /// Initializes a new instance of the <see cref='Font'/> class with the specified attributes.
134         /// </summary>
Font(FontFamily family, float emSize)135         public Font(FontFamily family, float emSize)
136         {
137             Initialize(family, emSize, FontStyle.Regular, GraphicsUnit.Point, SafeNativeMethods.DEFAULT_CHARSET, false);
138         }
139 
140         /// <summary>
141         /// Initializes a new instance of the <see cref='Font'/> class with the specified attributes.
142         /// </summary>
Font(string familyName, float emSize, FontStyle style, GraphicsUnit unit)143         public Font(string familyName, float emSize, FontStyle style, GraphicsUnit unit)
144         {
145             Initialize(familyName, emSize, style, unit, SafeNativeMethods.DEFAULT_CHARSET, IsVerticalName(familyName));
146         }
147 
148         /// <summary>
149         /// Initializes a new instance of the <see cref='Font'/> class with the specified attributes.
150         /// </summary>
Font(string familyName, float emSize, FontStyle style)151         public Font(string familyName, float emSize, FontStyle style)
152         {
153             Initialize(familyName, emSize, style, GraphicsUnit.Point, SafeNativeMethods.DEFAULT_CHARSET, IsVerticalName(familyName));
154         }
155 
156         /// <summary>
157         /// Initializes a new instance of the <see cref='Font'/> class with the specified attributes.
158         /// </summary>
Font(string familyName, float emSize, GraphicsUnit unit)159         public Font(string familyName, float emSize, GraphicsUnit unit)
160         {
161             Initialize(familyName, emSize, FontStyle.Regular, unit, SafeNativeMethods.DEFAULT_CHARSET, IsVerticalName(familyName));
162         }
163 
164         /// <summary>
165         /// Initializes a new instance of the <see cref='Font'/> class with the specified attributes.
166         /// </summary>
Font(string familyName, float emSize)167         public Font(string familyName, float emSize)
168         {
169             Initialize(familyName, emSize, FontStyle.Regular, GraphicsUnit.Point, SafeNativeMethods.DEFAULT_CHARSET, IsVerticalName(familyName));
170         }
171 
172         /// <summary>
173         /// Constructor to initialize fields from an existing native GDI+ object reference. Used by ToLogFont.
174         /// </summary>
Font(IntPtr nativeFont, byte gdiCharSet, bool gdiVerticalFont)175         private Font(IntPtr nativeFont, byte gdiCharSet, bool gdiVerticalFont)
176         {
177             Debug.Assert(_nativeFont == IntPtr.Zero, "GDI+ native font already initialized, this will generate a handle leak");
178             Debug.Assert(nativeFont != IntPtr.Zero, "nativeFont is null");
179 
180             int status = 0;
181             float size = 0;
182             GraphicsUnit unit = GraphicsUnit.Point;
183             FontStyle style = FontStyle.Regular;
184             IntPtr nativeFamily = IntPtr.Zero;
185 
186             _nativeFont = nativeFont;
187 
188             status = SafeNativeMethods.Gdip.GdipGetFontUnit(new HandleRef(this, nativeFont), out unit);
189             SafeNativeMethods.Gdip.CheckStatus(status);
190 
191             status = SafeNativeMethods.Gdip.GdipGetFontSize(new HandleRef(this, nativeFont), out size);
192             SafeNativeMethods.Gdip.CheckStatus(status);
193 
194             status = SafeNativeMethods.Gdip.GdipGetFontStyle(new HandleRef(this, nativeFont), out style);
195             SafeNativeMethods.Gdip.CheckStatus(status);
196 
197             status = SafeNativeMethods.Gdip.GdipGetFamily(new HandleRef(this, nativeFont), out nativeFamily);
198             SafeNativeMethods.Gdip.CheckStatus(status);
199 
200             SetFontFamily(new FontFamily(nativeFamily));
201             Initialize(_fontFamily, size, style, unit, gdiCharSet, gdiVerticalFont);
202         }
203 
204         /// <summary>
205         /// Initializes this object's fields.
206         /// </summary>
Initialize(string familyName, float emSize, FontStyle style, GraphicsUnit unit, byte gdiCharSet, bool gdiVerticalFont)207         private void Initialize(string familyName, float emSize, FontStyle style, GraphicsUnit unit, byte gdiCharSet, bool gdiVerticalFont)
208         {
209             _originalFontName = familyName;
210 
211             SetFontFamily(new FontFamily(StripVerticalName(familyName), createDefaultOnFail: true));
212             Initialize(_fontFamily, emSize, style, unit, gdiCharSet, gdiVerticalFont);
213         }
214 
215         /// <summary>
216         /// Initializes this object's fields.
217         /// </summary>
Initialize(FontFamily family, float emSize, FontStyle style, GraphicsUnit unit, byte gdiCharSet, bool gdiVerticalFont)218         private void Initialize(FontFamily family, float emSize, FontStyle style, GraphicsUnit unit, byte gdiCharSet, bool gdiVerticalFont)
219         {
220             if (family == null)
221             {
222                 throw new ArgumentNullException(nameof(family));
223             }
224 
225             if (float.IsNaN(emSize) || float.IsInfinity(emSize) || emSize <= 0)
226             {
227                 throw new ArgumentException(SR.Format(SR.InvalidBoundArgument, nameof(emSize), emSize, 0, "System.Single.MaxValue"), nameof(emSize));
228             }
229 
230             int status;
231 
232             _fontSize = emSize;
233             _fontStyle = style;
234             _fontUnit = unit;
235             _gdiCharSet = gdiCharSet;
236             _gdiVerticalFont = gdiVerticalFont;
237 
238             if (_fontFamily == null)
239             {
240                 // GDI+ FontFamily is a singleton object.
241                 SetFontFamily(new FontFamily(family.NativeFamily));
242             }
243 
244             if (_nativeFont == IntPtr.Zero)
245             {
246                 CreateNativeFont();
247             }
248 
249             // Get actual size.
250             status = SafeNativeMethods.Gdip.GdipGetFontSize(new HandleRef(this, _nativeFont), out _fontSize);
251             SafeNativeMethods.Gdip.CheckStatus(status);
252         }
253 
254         /// <summary>
255         /// Creates a <see cref='System.Drawing.Font'/> from the specified Windows handle.
256         /// </summary>
FromHfont(IntPtr hfont)257         public static Font FromHfont(IntPtr hfont)
258         {
259             var lf = new SafeNativeMethods.LOGFONT();
260             SafeNativeMethods.GetObject(new HandleRef(null, hfont), lf);
261 
262             IntPtr screenDC = UnsafeNativeMethods.GetDC(NativeMethods.NullHandleRef);
263             try
264             {
265                 return FromLogFont(lf, screenDC);
266             }
267             finally
268             {
269                 UnsafeNativeMethods.ReleaseDC(NativeMethods.NullHandleRef, new HandleRef(null, screenDC));
270             }
271         }
272 
FromLogFont(object lf)273         public static Font FromLogFont(object lf)
274         {
275             IntPtr screenDC = UnsafeNativeMethods.GetDC(NativeMethods.NullHandleRef);
276             try
277             {
278                 return FromLogFont(lf, screenDC);
279             }
280             finally
281             {
282                 UnsafeNativeMethods.ReleaseDC(NativeMethods.NullHandleRef, new HandleRef(null, screenDC));
283             }
284         }
285 
FromLogFont(object lf, IntPtr hdc)286         public static Font FromLogFont(object lf, IntPtr hdc)
287         {
288             IntPtr font = IntPtr.Zero;
289             int status = SafeNativeMethods.Gdip.GdipCreateFontFromLogfontW(new HandleRef(null, hdc), lf, out font);
290 
291             // Special case this incredibly common error message to give more information
292             if (status == SafeNativeMethods.Gdip.NotTrueTypeFont)
293             {
294                 throw new ArgumentException(SR.Format(SR.GdiplusNotTrueTypeFont_NoName));
295             }
296             else if (status != SafeNativeMethods.Gdip.Ok)
297             {
298                 throw SafeNativeMethods.Gdip.StatusException(status);
299             }
300 
301             // GDI+ returns font = 0 even though the status is Ok.
302             if (font == IntPtr.Zero)
303             {
304                 throw new ArgumentException(SR.Format(SR.GdiplusNotTrueTypeFont, lf.ToString()));
305             }
306 
307 #pragma warning disable 0618
308             bool gdiVerticalFont = (Marshal.ReadInt16(lf, LogFontNameOffset) == (short)'@');
309             return new Font(font, Marshal.ReadByte(lf, LogFontCharSetOffset), gdiVerticalFont);
310 #pragma warning restore 0618
311         }
312 
313         /// <summary>
314         /// Creates a Font from the specified Windows handle to a device context.
315         /// </summary>
FromHdc(IntPtr hdc)316         public static Font FromHdc(IntPtr hdc)
317         {
318             IntPtr font = IntPtr.Zero;
319             int status = SafeNativeMethods.Gdip.GdipCreateFontFromDC(new HandleRef(null, hdc), ref font);
320 
321             // Special case this incredibly common error message to give more information
322             if (status == SafeNativeMethods.Gdip.NotTrueTypeFont)
323             {
324                 throw new ArgumentException(SR.Format(SR.GdiplusNotTrueTypeFont_NoName));
325             }
326             else if (status != SafeNativeMethods.Gdip.Ok)
327             {
328                 throw SafeNativeMethods.Gdip.StatusException(status);
329             }
330 
331             return new Font(font, 0, false);
332         }
333 
334         /// <summary>
335         /// Creates an exact copy of this <see cref='Font'/>.
336         /// </summary>
Clone()337         public object Clone()
338         {
339             IntPtr clonedFont = IntPtr.Zero;
340             int status = SafeNativeMethods.Gdip.GdipCloneFont(new HandleRef(this, _nativeFont), out clonedFont);
341             SafeNativeMethods.Gdip.CheckStatus(status);
342 
343             return new Font(clonedFont, _gdiCharSet, _gdiVerticalFont);
344         }
345 
346         /// <summary>
347         /// Get native GDI+ object pointer. This property triggers the creation of the GDI+ native object if not initialized yet.
348         /// </summary>
349         internal IntPtr NativeFont => _nativeFont;
350 
351         /// <summary>
352         /// Gets the <see cref='Drawing.FontFamily'/> of this <see cref='Font'/>.
353         /// </summary>
354         [Browsable(false)]
355         public FontFamily FontFamily
356         {
357             get
358             {
359                 Debug.Assert(_fontFamily != null, "fontFamily should never be null");
360                 return _fontFamily;
361             }
362         }
363 
SetFontFamily(FontFamily family)364         private void SetFontFamily(FontFamily family)
365         {
366             _fontFamily = family;
367 
368             // GDI+ creates ref-counted singleton FontFamily objects based on the family name so all managed
369             // objects with same family name share the underlying GDI+ native pointer.  The unmanged object is
370             // destroyed when its ref-count gets to zero.
371             // Make sure this.fontFamily is not finalized so the underlying singleton object is kept alive.
372             GC.SuppressFinalize(_fontFamily);
373         }
374 
375         /// <summary>
376         /// Cleans up Windows resources for this <see cref='Font'/>.
377         /// </summary>
~Font()378         ~Font() => Dispose(false);
379 
380         /// <summary>
381         /// Cleans up Windows resources for this <see cref='Font'/>.
382         /// </summary>
Dispose()383         public void Dispose()
384         {
385             Dispose(true);
386             GC.SuppressFinalize(this);
387         }
388 
Dispose(bool disposing)389         private void Dispose(bool disposing)
390         {
391             if (_nativeFont != IntPtr.Zero)
392             {
393                 try
394                 {
395 #if DEBUG
396                     int status =
397 #endif
398                     SafeNativeMethods.Gdip.GdipDeleteFont(new HandleRef(this, _nativeFont));
399 #if DEBUG
400                     Debug.Assert(status == SafeNativeMethods.Gdip.Ok, "GDI+ returned an error status: " + status.ToString(CultureInfo.InvariantCulture));
401 #endif
402                 }
403                 catch (Exception ex) when (!ClientUtils.IsCriticalException(ex))
404                 {
405                 }
406                 finally
407                 {
408                     _nativeFont = IntPtr.Zero;
409                 }
410             }
411         }
412 
IsVerticalName(string familyName)413         private static bool IsVerticalName(string familyName) => familyName?.Length > 0 && familyName[0] == '@';
414 
415         /// <summary>
416         /// Gets a value indicating whether this <see cref='System.Drawing.Font'/> is bold.
417         /// </summary>
418         [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
419         public bool Bold => (Style & FontStyle.Bold) != 0;
420 
421         /// <summary>
422         /// Returns the GDI char set for this instance of a font. This will only
423         /// be valid if this font was created from a classic GDI font definition,
424         /// like a LOGFONT or HFONT, or it was passed into the constructor.
425         ///
426         /// This is here for compatibility with native Win32 intrinsic controls
427         /// on non-Unicode platforms.
428         /// </summary>
429         [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
430         public byte GdiCharSet => _gdiCharSet;
431 
432         /// <summary>
433         /// Determines if this font was created to represent a GDI vertical font. This will only be valid if this font
434         /// was created from a classic GDIfont definition, like a LOGFONT or HFONT, or it was passed into the constructor.
435         ///
436         /// This is here for compatibility with native Win32 intrinsic controls on non-Unicode platforms.
437         /// </summary>
438         [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
439         public bool GdiVerticalFont => _gdiVerticalFont;
440 
441         /// <summary>
442         /// Gets a value indicating whether this <see cref='Font'/> is Italic.
443         /// </summary>
444         [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
445         public bool Italic => (Style & FontStyle.Italic) != 0;
446 
447         /// <summary>
448         /// Gets the face name of this <see cref='Font'/> .
449         /// </summary>
450         [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
451         public string Name => FontFamily.Name;
452 
453         /// <summary>
454         /// This property is required by the framework and not intended to be used directly.
455         /// </summary>
456         [Browsable(false)]
457         public string OriginalFontName => _originalFontName;
458 
459         /// <summary>
460         /// Gets a value indicating whether this <see cref='Font'/> is strikeout (has a line through it).
461         /// </summary>
462         [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
463         public bool Strikeout => (Style & FontStyle.Strikeout) != 0;
464 
465         /// <summary>
466         /// Gets a value indicating whether this <see cref='Font'/> is underlined.
467         /// </summary>
468         [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
469         public bool Underline => (Style & FontStyle.Underline) != 0;
470 
471         /// <summary>
472         /// Returns a value indicating whether the specified object is a <see cref='Font'/> equivalent to this
473         /// <see cref='Font'/>.
474         /// </summary>
Equals(object obj)475         public override bool Equals(object obj)
476         {
477             if (obj == this)
478             {
479                 return true;
480             }
481 
482             if (!(obj is Font font))
483             {
484                 return false;
485             }
486 
487             // Note: If this and/or the passed-in font are disposed, this method can still return true since we check for cached properties
488             // here.
489             // We need to call properties on the passed-in object since it could be a proxy in a remoting scenario and proxies don't
490             // have access to private/internal fields.
491             return font.FontFamily.Equals(FontFamily) &&
492                 font.GdiVerticalFont == GdiVerticalFont &&
493                 font.GdiCharSet == GdiCharSet &&
494                 font.Style == Style &&
495                 font.Size == Size &&
496                 font.Unit == Unit;
497         }
498 
499         /// <summary>
500         /// Gets the hash code for this <see cref='Font'/>.
501         /// </summary>
GetHashCode()502         public override int GetHashCode()
503         {
504             return unchecked((int)((((uint)_fontStyle << 13) | ((uint)_fontStyle >> 19)) ^
505                          (((uint)_fontUnit << 26) | ((uint)_fontUnit >> 6)) ^
506                          (((uint)_fontSize << 7) | ((uint)_fontSize >> 25))));
507         }
508 
StripVerticalName(string familyName)509         private static string StripVerticalName(string familyName)
510         {
511             if (familyName?.Length > 1 && familyName[0] == '@')
512             {
513                 return familyName.Substring(1);
514             }
515 
516             return familyName;
517         }
518 
519         /// <summary>
520         /// Returns a human-readable string representation of this <see cref='Font'/>.
521         /// </summary>
ToString()522         public override string ToString()
523         {
524             return string.Format(CultureInfo.CurrentCulture, "[{0}: Name={1}, Size={2}, Units={3}, GdiCharSet={4}, GdiVerticalFont={5}]",
525                                     GetType().Name,
526                                     FontFamily.Name,
527                                     _fontSize,
528                                     (int)_fontUnit,
529                                     _gdiCharSet,
530                                     _gdiVerticalFont);
531         }
532 
ToLogFont(object logFont)533         public void ToLogFont(object logFont)
534         {
535             IntPtr screenDC = UnsafeNativeMethods.GetDC(NativeMethods.NullHandleRef);
536             try
537             {
538                 Graphics graphics = Graphics.FromHdcInternal(screenDC);
539                 try
540                 {
541                     ToLogFont(logFont, graphics);
542                 }
543                 finally
544                 {
545                     graphics.Dispose();
546                 }
547             }
548             finally
549             {
550                 UnsafeNativeMethods.ReleaseDC(NativeMethods.NullHandleRef, new HandleRef(null, screenDC));
551             }
552         }
553 
ToLogFont(object logFont, Graphics graphics)554         public unsafe void ToLogFont(object logFont, Graphics graphics)
555         {
556             if (graphics == null)
557             {
558                 throw new ArgumentNullException(nameof(graphics));
559             }
560 
561             int status = SafeNativeMethods.Gdip.GdipGetLogFontW(new HandleRef(this, NativeFont), new HandleRef(graphics, graphics.NativeGraphics), logFont);
562 
563             // Prefix the string with '@' this is a gdiVerticalFont.
564 #pragma warning disable 0618
565             if (_gdiVerticalFont)
566             {
567                 // Copy the Unicode contents of the name.
568                 for (int i = 60; i >= 0; i -= 2)
569                 {
570                     Marshal.WriteInt16(logFont,
571                                         LogFontNameOffset + i + 2,
572                                         Marshal.ReadInt16(logFont, LogFontNameOffset + i));
573                 }
574 
575                 // Prefix the name with an '@' sign.
576                 Marshal.WriteInt16(logFont, LogFontNameOffset, (short)'@');
577             }
578             if (Marshal.ReadByte(logFont, LogFontCharSetOffset) == 0)
579             {
580                 Marshal.WriteByte(logFont, LogFontCharSetOffset, _gdiCharSet);
581             }
582 #pragma warning restore 0618
583 
584             SafeNativeMethods.Gdip.CheckStatus(status);
585         }
586 
587         /// <summary>
588         /// Returns a handle to this <see cref='Font'/>.
589         /// </summary>
ToHfont()590         public IntPtr ToHfont()
591         {
592             var lf = new SafeNativeMethods.LOGFONT();
593             ToLogFont(lf);
594 
595             IntPtr handle = IntUnsafeNativeMethods.IntCreateFontIndirect(lf);
596             if (handle == IntPtr.Zero)
597             {
598                 throw new Win32Exception();
599             }
600 
601             return handle;
602         }
603 
604         /// <summary>
605         /// Returns the height of this Font in the specified graphics context.
606         /// </summary>
GetHeight(Graphics graphics)607         public float GetHeight(Graphics graphics)
608         {
609             if (graphics == null)
610             {
611                 throw new ArgumentNullException(nameof(graphics));
612             }
613 
614             float height;
615             int status = SafeNativeMethods.Gdip.GdipGetFontHeight(new HandleRef(this, NativeFont), new HandleRef(graphics, graphics.NativeGraphics), out height);
616             SafeNativeMethods.Gdip.CheckStatus(status);
617 
618             return height;
619         }
620 
GetHeight()621         public float GetHeight()
622         {
623             IntPtr screenDC = UnsafeNativeMethods.GetDC(NativeMethods.NullHandleRef);
624             try
625             {
626                 using (Graphics graphics = Graphics.FromHdcInternal(screenDC))
627                 {
628                     return GetHeight(graphics);
629                 }
630             }
631             finally
632             {
633                 UnsafeNativeMethods.ReleaseDC(NativeMethods.NullHandleRef, new HandleRef(null, screenDC));
634             }
635         }
636 
GetHeight(float dpi)637         public float GetHeight(float dpi)
638         {
639             float height;
640             int status = SafeNativeMethods.Gdip.GdipGetFontHeightGivenDPI(new HandleRef(this, NativeFont), dpi, out height);
641             SafeNativeMethods.Gdip.CheckStatus(status);
642 
643             return height;
644         }
645 
646         /// <summary>
647         /// Gets style information for this <see cref='Font'/>.
648         /// </summary>
649         [Browsable(false)]
650         public FontStyle Style => _fontStyle;
651 
652         // Return value is in Unit (the unit the font was created in)
653         /// <summary>
654         /// Gets the size of this <see cref='Font'/>.
655         /// </summary>
656         public float Size => _fontSize;
657 
658         /// <summary>
659         /// Gets the size, in points, of this <see cref='Font'/>.
660         /// </summary>
661         [Browsable(false)]
662         public float SizeInPoints
663         {
664             get
665             {
666                 if (Unit == GraphicsUnit.Point)
667                 {
668                     return Size;
669                 }
670 
671                 IntPtr screenDC = UnsafeNativeMethods.GetDC(NativeMethods.NullHandleRef);
672                 try
673                 {
674                     using (Graphics graphics = Graphics.FromHdcInternal(screenDC))
675                     {
676                         float pixelsPerPoint = (float)(graphics.DpiY / 72.0);
677                         float lineSpacingInPixels = GetHeight(graphics);
678                         float emHeightInPixels = lineSpacingInPixels * FontFamily.GetEmHeight(Style) / FontFamily.GetLineSpacing(Style);
679 
680                         return emHeightInPixels / pixelsPerPoint;
681                     }
682                 }
683                 finally
684                 {
685                     UnsafeNativeMethods.ReleaseDC(NativeMethods.NullHandleRef, new HandleRef(null, screenDC));
686                 }
687             }
688         }
689 
690         /// <summary>
691         /// Gets the unit of measure for this <see cref='Font'/>.
692         /// </summary>
693         public GraphicsUnit Unit => _fontUnit;
694 
695         /// <summary>
696         /// Gets the height of this <see cref='Font'/>.
697         /// </summary>
698         [Browsable(false)]
699         public int Height => (int)Math.Ceiling(GetHeight());
700 
701         /// <summary>
702         /// Returns true if this <see cref='Font'/> is a SystemFont.
703         /// </summary>
704         [Browsable(false)]
705         public bool IsSystemFont => !string.IsNullOrEmpty(_systemFontName);
706 
707         /// <summary>
708         /// Gets the name of this <see cref='Drawing.SystemFont'/>.
709         /// </summary>
710         [Browsable(false)]
711         public string SystemFontName => _systemFontName;
712 
713         // This is used by SystemFonts when constructing a system Font objects.
SetSystemFontName(string systemFontName)714         internal void SetSystemFontName(string systemFontName) => _systemFontName = systemFontName;
715     }
716 }
717