图形 DrawPath 在呈现文本时产生意外结果
Graphics DrawPath produces unexpected results when rendering text
考虑以下代码:
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
这会按预期生成轮廓文本:
但是,如果我增加笔划宽度的值,我就会开始在文本的某些部分出现奇怪的行为。 Times New Roman + Stroke of 20 给出:
注意句号和 I。此外,Arial 笔划为 40 时给出:
注意 Os、Es、Is 和句号。
可以下载复制项目HERE
如何为所有字体获得一致的轮廓文本?
关于此事的几点说明:
绘制形状的轮廓时,GraphicsPath 会使用指定笔的宽度将轮廓添加到形状的外部。
轮廓从形状的外部边缘开始并向外延伸。
这适用于指定的笔宽度是形状最小尺寸的两倍。
当 Pen 比此度量宽时,Pen 宽度将被裁剪并向外移动等于指定宽度与形状最小尺寸 x2 之间的差值的度量。
例如,如果您有一个大小为 20x30
的形状(例如,椭圆),在它移动到较短维度的外部之前,Pen 宽度可以达到 40.0f
。
下图中椭圆的大小为(20x30
).
轮廓Pen分别设置为40.0f
、46.0f
、52.0f
和65.0f
:
当您将文本添加到 GraphicsPath 并将其转换为曲线时会发生这种情况。尺寸小于 Pen 宽度的形状部分在勾勒轮廓时会导致轮廓移动到外部。文中全部点可以先看到
你可以解决问题(只是一个感知的问题,你看到的其实是你要求画的),你可以加宽路径,因此它会向内扩展,而不仅仅是向外扩展。
您可以使用其 Widen() 方法加宽 Path,指定用于扩展 Path 的 Pen(1.0f
的 Pen 宽度就足够了)。
定义绘图属性:
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:它是一些标准设置的预设,通常用作绘图的基础。您可以初始化此预设,然后在需要时 add/change 特定属性(例如,您需要将文本垂直或水平或同时居中)。
另见注释:
我在这里使用 PictureBox canvas。是一样的(不过默认是双缓冲的)
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);
}
}
考虑以下代码:
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
这会按预期生成轮廓文本:
但是,如果我增加笔划宽度的值,我就会开始在文本的某些部分出现奇怪的行为。 Times New Roman + Stroke of 20 给出:
注意句号和 I。此外,Arial 笔划为 40 时给出:
注意 Os、Es、Is 和句号。
可以下载复制项目HERE
如何为所有字体获得一致的轮廓文本?
关于此事的几点说明:
绘制形状的轮廓时,GraphicsPath 会使用指定笔的宽度将轮廓添加到形状的外部。
轮廓从形状的外部边缘开始并向外延伸。
这适用于指定的笔宽度是形状最小尺寸的两倍。
当 Pen 比此度量宽时,Pen 宽度将被裁剪并向外移动等于指定宽度与形状最小尺寸 x2 之间的差值的度量。
例如,如果您有一个大小为 20x30
的形状(例如,椭圆),在它移动到较短维度的外部之前,Pen 宽度可以达到 40.0f
。
下图中椭圆的大小为(20x30
).
轮廓Pen分别设置为40.0f
、46.0f
、52.0f
和65.0f
:
当您将文本添加到 GraphicsPath 并将其转换为曲线时会发生这种情况。尺寸小于 Pen 宽度的形状部分在勾勒轮廓时会导致轮廓移动到外部。文中全部点可以先看到
你可以解决问题(只是一个感知的问题,你看到的其实是你要求画的),你可以加宽路径,因此它会向内扩展,而不仅仅是向外扩展。
您可以使用其 Widen() 方法加宽 Path,指定用于扩展 Path 的 Pen(1.0f
的 Pen 宽度就足够了)。
定义绘图属性:
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:它是一些标准设置的预设,通常用作绘图的基础。您可以初始化此预设,然后在需要时 add/change 特定属性(例如,您需要将文本垂直或水平或同时居中)。
另见注释:
我在这里使用 PictureBox canvas。是一样的(不过默认是双缓冲的)
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);
}
}