图形DrawPath在呈现文本时产生意外结果 [英] Graphics DrawPath produces unexpected results when rendering text

查看:56
本文介绍了图形DrawPath在呈现文本时产生意外结果的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

考虑以下代码:

Private Sub Form1_Paint(sender As Object, e As PaintEventArgs) Handles MyBase.Paint
    Dim g As Graphics = e.Graphics

    g.TextRenderingHint = Drawing.Text.TextRenderingHint.AntiAliasGridFit
    g.SmoothingMode = SmoothingMode.HighQuality
    g.PixelOffsetMode = PixelOffsetMode.HighQuality

    Dim text = "Lorem Ipsum is simply dummy text of the printing and typesetting industry.... Lorem Ipsum has been the industry's standard dummy text ever since the 1500s"
    Dim f = New Font("Times New Roman", 60, FontStyle.Regular)

    Dim gp As GraphicsPath = New GraphicsPath()
    Dim strokepenwidth = 10

    Dim strokepen As New Pen(New SolidBrush(Color.Black), strokepenwidth) With {.LineJoin = LineJoin.Round}

    gp.AddString(text, f.FontFamily, f.Style, f.Size, New Rectangle(10, 10, Me.Width, Me.Height), New StringFormat)

    g.DrawPath(strokepen, gp)
    g.FillPath(New SolidBrush(Color.Yellow), gp)
End Sub

这将生成预期的带轮廓文本:

但是,如果我增加笔划宽度的值,我开始在文本的某些部分出现奇怪的行为。泰晤士报新罗马+笔画20给予:

请注意句号和是。此外,敲击40下的Arial:

请注意OS、ES、IS和句号。

可以下载复制项目HERE

如何获得所有字体的一致轮廓文本?

推荐答案

有关此事的几点注意事项:

绘制形状的轮廓时,GraphicsPath使用指定的钢笔宽度将轮廓添加到形状的外部。
轮廓从形状的外部边缘开始,向外扩展。
这适用于指定的笔宽是形状最小尺寸的两倍时。
当钢笔宽度大于此度量值时,钢笔宽度将被裁剪并向外移动,其度量值等于指定的宽度与形状最小尺寸x2之间的差值。

例如,如果您有一个大小为20x30的形状(例如椭圆),则在将其移动到较短维度的外部之前,笔宽可以达到40.0f

下图中椭圆的大小为(20x30)。
轮廓笔分别设置为40.0f46.0f52.0f65.0f

将文本添加到GraphicsPath并将其转换为曲线时会发生这种情况。形状部分的大小小于钢笔宽度,当轮廓被勾勒出来时,轮廓会移动到外部。您可以首先在文本中的所有中看到它。

您可以解决问题(只是一个感知的问题,您看到的实际上是您要求绘制的内容),您可以加宽路径,这样它将向内扩展,而不仅仅是向外扩展。
您可以使用其Widen()方法加宽路径,指定用于展开路径的钢笔(钢笔宽度1.0f就足够了)。

定义图形属性

Imports System.Drawing
Imports System.Drawing.Drawing2D

Private drawText As String = "Lorem Ipsum is simply dummy text of the printing and typesetting industry"
Private drawFont As New Font("Times New Roman", 60, FontStyle.Regular)
Private textBounds As RectangleF = RectangleF.Empty
Private outlineWidth As Single = 20.0F
Private outlineColor As Color = Color.Black
Private fillColor As Color = Color.Yellow
首先在指定的边界内绘制轮廓,然后使用相同的边界和StringFormat属性填充路径。
在这里,我使用StringFormat.GenericTypographic:它是一些标准设置的预置,通常用作绘图的基础。您可以初始化此预设置,然后在需要时添加/更改特定属性(例如,您需要将文本垂直或水平居中,或两者兼而有之)。

另请参阅:
Properly draw text using GraphicsPath
Flip the GraphicsPath that draws the text/string

我在这里使用PictureBox作为画布。这是一样的(但默认情况下是双缓冲的)。

Private Sub PictureBox1_Paint(sender As Object, e As PaintEventArgs) Handles PictureBox1.Paint
    e.Graphics.SmoothingMode = SmoothingMode.AntiAlias
    ' Add composition if you have a composited background
    'e.Graphics.CompositingQuality = CompositingQuality.HighQuality

    textBounds = RectangleF.Inflate(PictureBox1.ClientRectangle, -5, -5)
    textBounds.Offset(10, 10)

    DrawTextOutline(e.Graphics, textBounds, drawText, drawFont, outlineColor, outlineWidth, LineCap.Round)
    FillTextSolid(e.Graphics, textBounds, drawText, drawFont, fillColor)
End Sub

Private Sub DrawTextOutline(g As Graphics, bounds As RectangleF, text As String, font As Font, penColor As Color, penSize As Single, cap As LineCap)
    Dim lJoin As LineJoin = If(cap = LineCap.Round, LineJoin.Round, LineJoin.Bevel)
    Using gp As GraphicsPath = New GraphicsPath(FillMode.Winding),
        pen As New Pen(penColor, penSize) With {.LineJoin = lJoin, .StartCap = cap, .EndCap = cap},
        widenPen As New Pen(Color.Black, 1.0F)
        gp.AddString(text, font.FontFamily, font.Style, font.Size, bounds, StringFormat.GenericTypographic)
        gp.Widen(widenPen)
        g.DrawPath(pen, gp)
    End Using
End Sub

Private Sub FillTextSolid(g As Graphics, bounds As RectangleF, text As String, font As Font, fillColor As Color)
    Using gp As GraphicsPath = New GraphicsPath(),
        brush As New SolidBrush(fillColor)
        gp.AddString(text, font.FontFamily, font.Style, font.Size, bounds, StringFormat.GenericTypographic)
        g.FillPath(brush, gp)
    End Using
End Sub

样本结果


C#版本

常规设置:

using System.Drawing;
using System.Drawing.Drawing2D;

private static string drawText = "Lorem Ipsum is simply dummy text of the printing and typesetting industry";
private static Font drawFont = new Font("Times New Roman", 60, FontStyle.Regular);
private static RectangleF textBounds = RectangleF.Empty;
private static float outlineWidth = 20f;
private static Color outlineColor = Color.Black;
private static Color fillColor = Color.Yellow;

画法:

private void PictureBox1_Paint(object sender, PaintEventArgs e)
{
    e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
    textBounds = RectangleF.Inflate(pictureBox1.ClientRectangle, -5, -5);
    textBounds.Offset(10, 10);

    DrawTextOutline(e.Graphics, textBounds, drawText, drawFont, outlineColor, outlineWidth, LineCap.Round);
    FillTextSolid(e.Graphics, textBounds, drawText, drawFont, fillColor);
}

private void DrawTextOutline(Graphics g, RectangleF bounds, string text, Font font, Color penColor, float penSize, LineCap cap)
{
    LineJoin lJoin = cap == LineCap.Round ? LineJoin.Round : LineJoin.Bevel;
    using (var gp = new GraphicsPath(FillMode.Winding))
    using (Pen pen = new Pen(penColor, penSize) { LineJoin = lJoin, StartCap = cap, EndCap = cap })
    using (Pen widenPen = new Pen(Color.Black, 1.0f)) {
        gp.AddString(text, font.FontFamily, (int)font.Style, font.Size, bounds, StringFormat.GenericTypographic);
        gp.Widen(widenPen);
        g.DrawPath(pen, gp);
    };
}

private void FillTextSolid(Graphics g, RectangleF bounds, string text, Font font, Color fillColor)
{
    using (var gp = new GraphicsPath())
    using (var brush = new SolidBrush(fillColor)) {
        gp.AddString(text, font.FontFamily, (int)font.Style, font.Size, bounds, StringFormat.GenericTypographic);
        g.FillPath(brush, gp);
    }
}

这篇关于图形DrawPath在呈现文本时产生意外结果的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆