public static void DrawText( this DeviceContextHdcScope hdc, string?text, FontCache.Scope font, Rectangle bounds, Color foreColor, User32.DT flags, Color backColor = default, TextPaddingOptions padding = default) => DrawText(hdc.HDC, text, font, bounds, foreColor, flags, backColor, padding);
/// <summary> /// Draws the text in the given bounds, using the given Font, foreColor and backColor, and according to the specified /// TextFormatFlags flags. /// /// If font is null, the font currently selected in the hdc is used. /// /// If foreColor and/or backColor are Color.Empty, the hdc current text and/or background color are used. /// </summary> public static void DrawText( this Gdi32.HDC hdc, ReadOnlySpan <char> text, FontCache.Scope font, Rectangle bounds, Color foreColor, User32.DT flags, Color backColor = default, TextPaddingOptions padding = default) { if (text.IsEmpty || foreColor == Color.Transparent) { return; } ValidateFlags(flags); // DrawText requires default text alignment. using var alignment = new Gdi32.SetTextAlignmentScope(hdc, default); // Color empty means use the one currently selected in the dc. using var textColor = foreColor.IsEmpty ? default : new Gdi32.SetTextColorScope(hdc, foreColor); using var fontSelection = new Gdi32.SelectObjectScope(hdc, (Gdi32.HFONT)font); Gdi32.BKMODE newBackGroundMode = (backColor.IsEmpty || backColor == Color.Transparent) ? Gdi32.BKMODE.TRANSPARENT : Gdi32.BKMODE.OPAQUE; using var backgroundMode = new Gdi32.SetBkModeScope(hdc, newBackGroundMode); using var backgroundColor = newBackGroundMode != Gdi32.BKMODE.TRANSPARENT ? new Gdi32.SetBackgroundColorScope(hdc, backColor) : default; User32.DRAWTEXTPARAMS dtparams = GetTextMargins(font, padding); bounds = AdjustForVerticalAlignment(hdc, text, bounds, flags, ref dtparams); // Adjust unbounded rect to avoid overflow since Rectangle ctr does not do param validation. if (bounds.Width == TextRenderer.MaxSize.Width) { bounds.Width -= bounds.X; } if (bounds.Height == TextRenderer.MaxSize.Height) { bounds.Height -= bounds.Y; } RECT rect = bounds; User32.DrawTextExW(hdc, text, ref rect, flags, ref dtparams); }
private static (User32.DT Flags, TextPaddingOptions Padding) SplitTextFormatFlags(TextFormatFlags flags) { if (((uint)flags & GdiUnsupportedFlagMask) == 0) { return((User32.DT)flags, TextPaddingOptions.GlyphOverhangPadding); } // Clear TextRenderer custom flags. User32.DT windowsGraphicsSupportedFlags = (User32.DT)((uint)flags & ~GdiUnsupportedFlagMask); TextPaddingOptions padding = flags.HasFlag(TextFormatFlags.LeftAndRightPadding) ? TextPaddingOptions.LeftAndRightPadding : flags.HasFlag(TextFormatFlags.NoPadding) ? TextPaddingOptions.NoPadding : TextPaddingOptions.GlyphOverhangPadding; return(windowsGraphicsSupportedFlags, padding); }
/// <summary> /// Get the bounding box internal text padding to be used when drawing text. /// </summary> public static User32.DRAWTEXTPARAMS GetTextMargins( this FontCache.Scope font, TextPaddingOptions padding = default) { // DrawText(Ex) adds a small space at the beginning of the text bounding box but not at the end, // this is more noticeable when the font has the italic style. We compensate with this factor. int leftMargin = 0; int rightMargin = 0; float overhangPadding; switch (padding) { case TextPaddingOptions.GlyphOverhangPadding: // [overhang padding][Text][overhang padding][italic padding] overhangPadding = font.Data.Height / 6f; leftMargin = (int)Math.Ceiling(overhangPadding); rightMargin = (int)Math.Ceiling(overhangPadding * (1 + ItalicPaddingFactor)); break; case TextPaddingOptions.LeftAndRightPadding: // [2 * overhang padding][Text][2 * overhang padding][italic padding] overhangPadding = font.Data.Height / 6f; leftMargin = (int)Math.Ceiling(2 * overhangPadding); rightMargin = (int)Math.Ceiling(overhangPadding * (2 + ItalicPaddingFactor)); break; case TextPaddingOptions.NoPadding: default: break; } return(new User32.DRAWTEXTPARAMS { iLeftMargin = leftMargin, iRightMargin = rightMargin }); }