代码之家  ›  专栏  ›  技术社区  ›  TerribleDog

使用图形路径正确绘制文本

  •  2
  • TerribleDog  · 技术社区  · 7 年前

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

    enter image description here

    这是我的密码:

    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);
    }
    
    2 回复  |  直到 5 年前
        1
  •  5
  •   roozbeh S    7 年前

    似乎你提供了错误的测量字体大小在第一位,然后增加额外的厚度刷。请尝试以下操作:

    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);
    }
    
        2
  •  6
  •   Jimi    6 年前

    这个 GraphicsPath 类以不同的方式计算文本对象的大小(如注释中所述)。文本大小是使用 Ems 边框大小。
    Em 是独立于目标设备上下文的排版度量。
    它指的是字体最宽的字母所占据的矩形,通常是字母“M”(发音) 相对长度单位

    目的地 相对长度单位 大小可以用两种不同的方法计算:一种方法包括 Font descent

    float EMSize = (Font.SizeInPoints * [FontFamily].GetCellAscent([FontStyle]) 
                                      / [FontFamily].GetEmHeight([FontStyle])
    

    float EMSize = (Font.SizeInPoints * ([FontFamily].GetCellAscent([FontStyle] + 
                                         [FontFamily.GetCellDescent([FontStyle])) 
                                      / [FontFamily].GetEmHeight([FontStyle])
    


    FontFamily.GetEmHeight , FontFamily.GetCellAscent FontFamily.GetCellDescent

    我在这里插入你能在文件里找到的数字。

    Font Ascent-Descent

    请参阅此处包含的一般信息:
    Using Font and Text (MSDN)

    本文档报告了有关如何转换点、像素和Ems的详细信息:
    How to: Obtain Font Metrics (MSDN)


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

    此类根据用户选择的字体大小执行一些计算。
    DPI 分辨率(或从像素维度转换为点)。每个度量值也转换为 Ems公司 ,如果您必须使用 GraphicsPath 绘制文本。

    Ems公司 Ascent Descent 字体的名称。
    这个 类可以更好地使用这个度量,因为混合文本可以同时包含这两个部分,如果不包含这两个部分,那么该部分就是 = 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;
    }
    

    把文字画在纸上 设备上下文:

    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;
            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);
            }
        }
    }
    

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

    Font Outline Example

    类对象,用作引用:

    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)) {
                this.currentScreenDPI = g.DpiY; 
            }
            this.Text = text;
            this.FontFamily = fontFamily;
            this.SizeInPoints = FontSize;
            this.FillColor = Color.Black;
            this.Outline = new Pen(Color.Black, 1);
            this.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 => this.m_SizeInPoints;
            set {  this.m_SizeInPoints = value;
                   this.m_SizeInPixels = (value * 72F) / this.currentScreenDPI;
                   this.SizeInEms = GetEmSize();
            }
        }
        public float SizeInPixels {
            get => this.m_SizeInPixels;
            set {  this.m_SizeInPixels = value;
                   this.m_SizeInPoints = (value * this.currentScreenDPI) / 72F;
                   this.SizeInEms = GetEmSize();
            }
        }
    
        public float SizeInEms { get; private set; }
        public PointF Location { get; set; }
        public RectangleF DrawingBox { get; set; }
    
        private float GetEmSize()
        {
            return (this.m_SizeInPoints * 
                   (this.FontFamily.GetCellAscent(this.FontStyle) +
                    this.FontFamily.GetCellDescent(this.FontStyle))) /
                    this.FontFamily.GetEmHeight(this.FontStyle);
        }
    }
    

    :

    创建自定义组合框并设置其 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";
    

    private void cboFontFamily_DrawItem(object sender, DrawItemEventArgs e)
    {
        if ((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();
    }