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