使用 GraphicsPath 正确绘制文本

Ter*_*Dog 4 .net c# graphics gdi+ winforms

如下图所示,PictureBox 上的文本与 TextBox 中的文本不同。如果我使用它可以正常工作,Graphics.DrawString()但是当我使用图形路径时,它会截断并且不显示整个文本。你认为我的代码有什么问题?

在此处输入图片说明

这是我的代码:

public LayerClass DrawString(LayerClass.Type _text, string text, RectangleF rect, Font _fontStyle, PaintEventArgs e)
{
    using (StringFormat string_format = new StringFormat())
    {
        rect.Size = e.Graphics.MeasureString(text, _fontStyle);
        rect.Location = new PointF(Shape.center.X - (rect.Width / 2), Shape.center.Y - (rect.Height / 2));
        if(isOutlinedOrSolid)
        {
            using (GraphicsPath path = new GraphicsPath())
            {
                path.AddString(text, _fontStyle.FontFamily, (int)_fontStyle.Style, rect.Size.Height, rect, string_format);
                e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
                e.Graphics.CompositingQuality = CompositingQuality.HighQuality;
                e.Graphics.CompositingMode = CompositingMode.SourceOver;
                e.Graphics.DrawPath(new Pen(Color.Red, 1), path);
            }
        }
        else
        {
            e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
            e.Graphics.CompositingQuality = CompositingQuality.HighQuality;
            e.Graphics.CompositingMode = CompositingMode.SourceOver;
            e.Graphics.DrawString(text,_fontStyle,Brushes.Red, rect.Location);
        }
    }

    this._Text = text;
    this._TextRect = rect;
    return new LayerClass(_text, this, text, rect);
}
Run Code Online (Sandbox Code Playgroud)

Jim*_*imi 6

GraphicsPath的类计算文本对象的方式不同大小(在评论正如已经指出)。文本大小是使用Ems边界矩形大小计算的。
AnEm是独立于目标设备上下文的印刷度量。
它指的是字体最宽的字母所占据的矩形,通常是字母“M”(发音为em)。

目标Em大小可以通过两种不同的方式计算:一种包括 a Font descent,另一种不包括它。

float EMSize = (Font.SizeInPoints * [FontFamily].GetCellAscent([FontStyle]) 
                                  / [FontFamily].GetEmHeight([FontStyle])
Run Code Online (Sandbox Code Playgroud)

或者

float EMSize = (Font.SizeInPoints * 
               ([FontFamily].GetCellAscent([FontStyle] + 
                [FontFamily.GetCellDescent([FontStyle])) / 
                [FontFamily].GetEmHeight([FontStyle])
Run Code Online (Sandbox Code Playgroud)

请参阅有关文档:
FontFamily.GetEmHeightFontFamily.GetCellAscentFontFamily.GetCellDescent

我在这里插入您可以在文档中找到的数字。

字体上升下降

请参阅此处包含的一般信息:
使用字体和文本 (MSDN)

本文档报告了有关如何翻译 Points、Pixels 和 Ems 的详细信息:
如何:获取字体度量 (MSDN)


我假设您已经有一个包含/引用来自 UI 控件和所需调整的字体设置的类对象。
我在这里添加了一些属性,其中包含与问题相关的这些设置的子集。

此类根据用户选择的字体大小执行一些计算。
字体大小通常以 Points 为单位。然后使用当前的屏幕DPI分辨率(或从像素维度转换为点),以像素为单位转换该度量。每个度量也转换为Ems,如果您必须使用它GraphicsPath来绘制文本,这会派上用场。

Ems大小的计算同时考虑AscentDescent字体的。
GraphicsPath类的工作更好地与这个措施,因为混合文本可以有两个部分,如果没有,那部分= 0

为了计算与特定的字体和字体尺寸绘制的文本的容器中,使用GraphicsPath.GetBounds()方法:
[Canvas]是提供控制Paint事件的e.Graphics对象)

using (var path = new GraphicsPath())
using (var format = new StringFormat(StringFormatFlags.NoClip | StringFormatFlags.NoWrap))
{
    format.Alignment = [StringAlignment.Center/Near/Far]; //As selected
    format.LineAlignment = [StringAlignment.Center/Near/Far]; //As selected
    //Add the Text to the GraphicsPath
    path.AddString(fontObject.Text, fontObject.FontFamily, 
                   (int)fontObject.FontStyle, fontObject.SizeInEms, 
                   [Canvas].ClientRectangle, format);
    //Ems size (bounding rectangle)
    RectangleF TextBounds = path.GetBounds(null, fontObject.Outline);
    //Location of the Text
    fontObject.Location = TextBounds.Location;
}
Run Code Online (Sandbox Code Playgroud)

[Canvas]设备上下文中绘制文本:

private void Canvas_Paint(object sender, PaintEventArgs e)
{
    using (var path = new GraphicsPath())
    using (var format = new StringFormat(StringFormatFlags.NoClip | StringFormatFlags.NoWrap))
    {
        format.Alignment = [StringAlignment.Center/Near/Far]; //As selected
        format.LineAlignment = [StringAlignment.Center/Near/Far]; //As selected

        path.AddString(fontObject.Text, fontObject.FontFamily, (int)fontObject.FontStyle, fontObject.SizeInEms, Canvas.ClientRectangle, format);

        e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
        // When text is rendered directly
        e.Graphics.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;
        // The composition properties are useful when drawing on a composited surface
        // when we simply draw on a Control's surface, these are useless
        e.Graphics.CompositingMode = CompositingMode.SourceOver;
        e.Graphics.CompositingQuality = CompositingQuality.HighQuality;

        if (fontObject.Outlined) { 
            e.Graphics.DrawPath(fontObject.Outline, path);
        }
        using(var brush = new SolidBrush(fontObject.FillColor)) {
            e.Graphics.FillPath(brush, path);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

使用此类和相关方法的视觉效果:

字体轮廓示例

用作参考的类对象:

public class FontObject
{
    private float currentScreenDPI = 0.0F;
    private float m_SizeInPoints = 0.0F;
    private float m_SizeInPixels = 0.0F;
    public FontObject() 
        : this(string.Empty, FontFamily.GenericSansSerif, FontStyle.Regular, 18F) { }
    public FontObject(string text, Font font) 
        : this(text, font.FontFamily, font.Style, font.SizeInPoints) { }
    public FontObject(string text, FontFamily fontFamily, FontStyle fontStyle, float FontSize)
    {
        if (FontSize < 3) FontSize = 3;
        using (Graphics g = Graphics.FromHwndInternal(IntPtr.Zero)) {
            currentScreenDPI = g.DpiY; 
        }
        Text = text;
        FontFamily = fontFamily;
        SizeInPoints = FontSize;
        FillColor = Color.Black;
        Outline = new Pen(Color.Black, 1);
        Outlined = false;
    }

    public string Text { get; set; }
    public FontStyle FontStyle { get; set; }
    public FontFamily FontFamily { get; set; }
    public Color FillColor { get; set; }
    public Pen Outline { get; set; }
    public bool Outlined { get; set; }
    public float SizeInPoints {
        get => m_SizeInPoints;
        set {  m_SizeInPoints = value;
               m_SizeInPixels = (value * 72F) / currentScreenDPI;
               SizeInEms = GetEmSize();
        }
    }
    public float SizeInPixels {
        get => m_SizeInPixels;
        set {  m_SizeInPixels = value;
               m_SizeInPoints = (value * currentScreenDPI) / 72F;
               SizeInEms = GetEmSize();
        }
    }

    public float SizeInEms { get; private set; }
    public PointF Location { get; set; }
    public RectangleF DrawingBox { get; set; }

    private float GetEmSize()
    {
        return (m_SizeInPoints * 
               (FontFamily.GetCellAscent(FontStyle) +
                FontFamily.GetCellDescent(FontStyle))) /
                FontFamily.GetEmHeight(FontStyle);
    }
}
Run Code Online (Sandbox Code Playgroud)

带有字体系列
的 ComboBox 创建一个自定义 ComboBox 并设置其DrawMode = OwnerDrawVariable

string[] FontList = FontFamily.Families.Where(f => f.IsStyleAvailable(FontStyle.Regular)).Select(f => f.Name).ToArray();

cboFontFamily.DrawMode = DrawMode.OwnerDrawVariable;
cboFontFamily.AutoCompleteMode = AutoCompleteMode.SuggestAppend;
cboFontFamily.AutoCompleteSource = AutoCompleteSource.CustomSource;
cboFontFamily.AutoCompleteCustomSource.AddRange(FontList);
cboFontFamily.DisplayMember = "Name";
cboFontFamily.Items.AddRange(FontList);
cboFontFamily.Text = "Arial";
Run Code Online (Sandbox Code Playgroud)

事件处理程序:

private void cboFontFamily_DrawItem(object sender, DrawItemEventArgs e)
{
    if ((cboFontFamily.Items.Count == 0) || e.Index < 0) return;
    e.DrawBackground();
   
    var flags = TextFormatFlags.Left | TextFormatFlags.VerticalCenter;
    using (var family = new FontFamily(cboFontFamily.GetItemText(cboFontFamily.Items[e.Index])))
    using (var font = new Font(family, 10F, FontStyle.Regular, GraphicsUnit.Point)) {
        TextRenderer.DrawText(e.Graphics, family.Name, font, e.Bounds, cboFontFamily.ForeColor, flags);
    }
    e.DrawFocusRectangle();
}

private void cboFontFamily_MeasureItem(object sender, MeasureItemEventArgs e)
{
    e.ItemHeight = (int)this.Font.Height + 4;
}

private void cboFontFamily_SelectionChangeCommitted(object sender, EventArgs e)
{
    fontObject.FontFamily = new FontFamily(cboFontFamily.GetItemText(cboFontFamily.SelectedItem));
    Canvas.Invalidate();
}
Run Code Online (Sandbox Code Playgroud)


roo*_*h S 5

似乎您首先为字体大小提供了错误的度量,然后为画笔添加了额外的厚度。试试这个:

using (GraphicsPath path = new GraphicsPath())
{
    path.AddString(
        text,                         
        _fontStyle.FontFamily,      
        (int)_fontStyle.Style,      
        e.Graphics.DpiY * fontSize / 72f,       // em size
        new Point(0, 0),                       // location where to draw text
        string_format);          

    e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
    e.Graphics.CompositingQuality = CompositingQuality.HighQuality;
    e.Graphics.CompositingMode = CompositingMode.SourceOver;
    e.Graphics.DrawPath(new Pen(Color.Red), path);
}
Run Code Online (Sandbox Code Playgroud)