| File: System\Windows\Forms\Rendering\FontCache.Data.cs | Web Access |
| Project: src\winforms\src\System.Windows.Forms\System.Windows.Forms.csproj (System.Windows.Forms) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Drawing; namespace System.Windows.Forms; internal sealed partial class FontCache { internal struct Data : IDisposable { // Note: These defaults are according to the ones in GDI+ but those are not necessarily the same as the system // default font. The GetSystemDefaultHFont() method should be used if needed. private const string DefaultFaceName = "Microsoft Sans Serif"; private const byte True = 1; private const byte False = 0; public WeakReference<Font> Font { get; } public HFONT HFONT { get; private set; } public FONT_QUALITY Quality { get; } private int? _tmHeight; public Data(Font font, FONT_QUALITY quality) { Font = new WeakReference<Font>(font); Quality = quality; HFONT = FromFont(font, quality); _tmHeight = null; } public unsafe int Height { get { if (!_tmHeight.HasValue) { using var screenDC = GdiCache.GetScreenHdc(); HDC hdc = screenDC.HDC; using SelectObjectScope fontSelection = new(hdc, HFONT); Debug.Assert(PInvoke.GetMapMode(hdc) == HDC_MAP_MODE.MM_TEXT); TEXTMETRICW tm = default; PInvoke.GetTextMetrics(hdc, &tm); _tmHeight = tm.tmHeight; } return _tmHeight.Value; } } public void Dispose() { if (!HFONT.IsNull) { PInvokeCore.DeleteObject(HFONT); } HFONT = default; } /// <summary> /// Constructs a WindowsFont object from an existing System.Drawing.Font object (GDI+), based on the screen dc /// MapMode and resolution (normally: MM_TEXT and 96 dpi). /// </summary> private static unsafe HFONT FromFont(Font font, FONT_QUALITY quality = FONT_QUALITY.DEFAULT_QUALITY) { string familyName = font.FontFamily.Name; // Strip vertical-font mark from the name if needed. if (familyName is not null && familyName.Length > 1 && familyName[0] == '@') { familyName = familyName[1..]; } // Now, creating it using the Font.SizeInPoints makes it GraphicsUnit-independent. Debug.Assert(font.SizeInPoints > 0.0f, "size has a negative value."); // Get the font height from the specified size. The size is in point units and height in logical units // (pixels when using MM_TEXT) so we need to make the conversion using the number of pixels per logical // inch along the screen height. (1 point = 1/72 inch.) int pixelsY = (int)Math.Ceiling(ScaleHelper.InitialSystemDpi * font.SizeInPoints / 72); // The lfHeight represents the font cell height (line spacing) which includes the internal leading; we // specify a negative size value (in pixels) for the height so the font mapper provides the closest match // for the character height rather than the cell height. LOGFONTW logFont = new() { lfHeight = -pixelsY, lfCharSet = (FONT_CHARSET)font.GdiCharSet, lfQuality = quality, lfWeight = (int)((font.Style & FontStyle.Bold) == FontStyle.Bold ? FW.BOLD : FW.NORMAL), lfItalic = (font.Style & FontStyle.Italic) == FontStyle.Italic ? True : False, lfUnderline = (font.Style & FontStyle.Underline) == FontStyle.Underline ? True : False, lfStrikeOut = (font.Style & FontStyle.Strikeout) == FontStyle.Strikeout ? True : False, FaceName = familyName, // This is the only difference in what `Font.ToHfont()` ultimately does. GDI+ uses OUT_DEFAULT_PRECIS // which appears to be the same thing as OUT_TT_PRECIS. GDI+ also applies the transforms from the // screen device context lfOutPrecision = FONT_OUTPUT_PRECISION.OUT_TT_PRECIS, }; if (logFont.FaceName.IsEmpty) { logFont.FaceName = DefaultFaceName; } HFONT hfont = PInvokeCore.CreateFontIndirect(&logFont); if (hfont.IsNull) { // Get the default font if we couldn't get what we requested. logFont.FaceName = DefaultFaceName; logFont.lfOutPrecision = FONT_OUTPUT_PRECISION.OUT_TT_ONLY_PRECIS; hfont = PInvokeCore.CreateFontIndirect(&logFont); Debug.Assert(!hfont.IsNull); } return hfont; } } }