给定FillTextSolid()
前面显示的方法:
Graphics DrawPath 在渲染文本时产生意外的结果
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),
format = New StringFormat(StringFormat.GenericTypographic)
format.Trimming = StringTrimming.EllipsisWord
gp.AddString(text, font.FontFamily, font.Style, font.Size, bounds, StringFormat.GenericTypographic)
g.FillPath(brush, gp)
Dim lastCharPosition = GetPathLastCharPosition(g, format, gp, bounds, text, font)
End Using
End Sub
您可以使用当前GraphicsPath、矩形边界、字体大小和用于在图形上下文中绘制文本的样式来计算最后绘制的字符的位置,因此,如果需要,还可以计算最后一个单词。
我添加了对该方法FillTextSolid()
的调用,该GetPathLastCharPosition()
方法负责计算。将描述的对象传递给方法,因为它们当前已配置(这些设置当然可以随时更改:请参阅底部的动画)。
Dim [Last Char Position] = GetPathLastCharPosition(
[Graphics],
[StringFormat],
[GraphicsPath],
[RectangleF],
[String],
[Font]
)
要确定使用 GraphicsPath 对象打印的当前最后一个单词,您不能将字符串拆分为由空格分隔的部分,因为每个字符都是渲染的一部分。
还要注意:要使测量按预期工作,您不能以点为单位设置绘图字体大小,字体大小必须以像素表示。
您也可以使用 Point 单位,但 GraphicsPath 类在指定 Points 时(正确地)生成EM
s 中的 Font 度量 - 考虑到 Font Cell Ascent 和 Descent - 这与Font.Height
.
您当然可以将度量从Em
s 转换为 Pixels,但它只是无缘无故地增加了复杂性(至少在这个问题的上下文中)。
请参阅这些详细信息的描述以及如何计算 GraphicsPath EM:
使用 GraphicsPath 正确绘制文本
GetPathLastCharPosition()
使用Graphics.MeasureCharacterRanges测量文本字符串中每个字符的边界矩形,每次迭代以 32 个字符为单位。这是因为StringFormat.SetMeasurableCharacterRanges最多只需要 32 个CharacterRange元素。
因此,我们以 32 个字符为单位获取文本,获取每个字符的边界区域,并验证该区域是否包含 GraphicsPath 中的最后一个点。
GraphicsPath 生成的最后一个点由GraphicsPath.GetLastPoint()返回。
- 此过程仅考虑从上到下和从左到右绘制的文本。
它可以适应处理从右到左的语言。
当然,您也可以忽略最后一点,只考虑区域边界是否落在画布的边界矩形之外。
无论如何,当找到包含最后一个点的区域时,该方法停止并返回作为绘图一部分的字符串中最后一个字符的位置。
Private Function GetPathLastCharPosition(g As Graphics, format As StringFormat, path As GraphicsPath, bounds As RectangleF, text As String, font As Font) As Integer
Dim textLength As Integer = text.Length
Dim p = path.GetLastPoint()
bounds.Height += font.Height
For charPos As Integer = 0 To text.Length - 1 Step 32
Dim count As Integer = Math.Min(textLength - charPos, 32)
Dim charRanges = Enumerable.Range(charPos, count).Select(Function(c) New CharacterRange(c, 1)).ToArray()
format.SetMeasurableCharacterRanges(charRanges)
Dim regions As Region() = g.MeasureCharacterRanges(text, font, bounds, format)
For r As Integer = 0 To regions.Length - 1
If regions(r).IsVisible(p.X, p.Y) Then
Return charRanges(r).First
End If
Next
Next
Return -1
End Function
这是它的工作原理:

该方法的 C# 版本:
private int GetPathLastCharPosition(Graphics g, StringFormat format, GraphicsPath path, RectangleF bounds, string text, Font font)
{
int textLength = text.Length;
var p = path.GetLastPoint();
bounds.Height += font.Height;
for (int charPos = 0; charPos < text.Length; charPos += 32) {
int count = Math.Min(textLength - charPos, 32);
var charRanges = Enumerable.Range(charPos, count).Select(c => new CharacterRange(c, 1)).ToArray();
format.SetMeasurableCharacterRanges(charRanges);
Region[] regions = g.MeasureCharacterRanges(text, font, bounds, format);
for (int r = 0; r < regions.Length; r++) {
if (regions[r].IsVisible(p.X, p.Y)) {
return charRanges[r].First;
}
}
}
return -1;
}