如果使用 WPF 是一个选项,您可以使用 FormattedText 生成几何图形,然后将几何图形逐个图形添加到 GraphicsPath 对象。
要在 WinForms 应用程序中使用 WPF,您需要参考
- 演示框架
- WindowsBase
- PresentationCore
这些方法可能会让您入门。
public static readonly int FORMATTED_TEXT_MAX_SIZE = 3579139;
private static void addStringUsingFormattedText(GraphicsPath targetPath, string text, System.Drawing.FontFamily fontFamily, int fontStyle, float fontSize, System.Drawing.RectangleF layoutRect, System.Drawing.StringFormat stringFormat)
{
if (string.IsNullOrEmpty(text))
return;
var typeFaceToUse = createTypeFace((System.Drawing.FontStyle)fontStyle, fontFamily.Name);
var ft = createFormattedText(text, fontSize, layoutRect.Width, layoutRect.Height, typeFaceToUse, stringFormat);
var geometry = ft.BuildGeometry(new System.Windows.Point(layoutRect.X, layoutRect.Y));
targetPath.StartFigure();
foreach (PathFigure pf in geometry.GetFlattenedPathGeometry().Figures)
{
foreach (PolyLineSegment polySegment in pf.Segments)
targetPath.AddPolygon(polySegment.Points.Select(point => new System.Drawing.PointF((float)point.X, (float)point.Y)).ToArray());
}
targetPath.CloseFigure();
}
private static FormattedText createFormattedText(string text, float fontSize, float width, float height, Typeface typeFaceToUse, System.Drawing.StringFormat stringFormat)
{
var ft = new FormattedText(text, System.Globalization.CultureInfo.CurrentCulture, System.Windows.FlowDirection.LeftToRight
, typeFaceToUse, fontSize, Brushes.Black);
if (width > 0)
ft.MaxTextWidth = Math.Min(FORMATTED_TEXT_MAX_SIZE, width);
if (height > 0)
ft.MaxTextHeight = Math.Min(FORMATTED_TEXT_MAX_SIZE, height);
ft.Trimming = TextTrimming.None;
switch (stringFormat.Alignment)
{
case System.Drawing.StringAlignment.Center:
ft.TextAlignment = TextAlignment.Center;
break;
case System.Drawing.StringAlignment.Far:
ft.TextAlignment = ft.FlowDirection == FlowDirection.LeftToRight ? TextAlignment.Right : TextAlignment.Left;
break;
case System.Drawing.StringAlignment.Near:
ft.TextAlignment = ft.FlowDirection == FlowDirection.LeftToRight ? TextAlignment.Left : TextAlignment.Right;
break;
}
return ft;
}
我们注意到,如果我们在 StringFormat 上设置了对齐方式,它的行为会略有不同。您可能需要为此调整逻辑。
在我们的例子中,我们还使用 Graphics.MeasureString() 来预测字符串的宽度。以下这些方法应该有效:
private static System.Drawing.SizeF measureStringUsingFormattedText(string text, System.Drawing.Font measureFont, System.Drawing.RectangleF layoutRect, System.Drawing.StringFormat stringFormat)
{
if (string.IsNullOrEmpty(text))
return new System.Drawing.SizeF(0, 0);
var typeFaceToUse = createTypeFace(measureFont.Style, measureFont.Name);
var ft = createFormattedText(text, measureFont.Size, layoutRect.Width, layoutRect.Height, typeFaceToUse, stringFormat);
bool includeTrailingSpaces = System.Drawing.StringFormatFlags.MeasureTrailingSpaces == (stringFormat.FormatFlags & System.Drawing.StringFormatFlags.MeasureTrailingSpaces);
var rectangleToReturn = new System.Drawing.SizeF((float)(includeTrailingSpaces ? ft.WidthIncludingTrailingWhitespace : ft.Width), (float)ft.Height);
return rectangleToReturn;
}
private static System.Drawing.SizeF measureStringUsingFormattedText(string text, System.Drawing.Font measureFont, System.Drawing.SizeF layoutArea, System.Drawing.StringFormat stringFormat, out int linesFilled)
{
if (string.IsNullOrEmpty(text))
{
linesFilled = 0;
return new System.Drawing.SizeF(0, 0);
}
var typeFaceToUse = createTypeFace(measureFont.Style, measureFont.Name);
var ft = createFormattedText(text, measureFont.Size, layoutArea.Width, layoutArea.Height, typeFaceToUse, stringFormat);
bool includeTrailingSpaces = System.Drawing.StringFormatFlags.MeasureTrailingSpaces == (stringFormat.FormatFlags & System.Drawing.StringFormatFlags.MeasureTrailingSpaces);
var rectangleToReturn = new System.Drawing.SizeF((float)(includeTrailingSpaces ? ft.WidthIncludingTrailingWhitespace : ft.Width), (float)ft.Height);
linesFilled = (int)Math.Floor(ft.Height / ft.Baseline);
return rectangleToReturn;
}
对我们来说,复杂的部分是如何获取 TypeFace 对象。您不能将 System.Drawing.FontFamily 转换为 System.Windows.Media.TypeFace 对象。只要您不需要 PrivateFontCollection,就没有问题。
private static Typeface createTypeFace(System.Drawing.FontStyle fontStyle, string familyName)
{
var fwToUse = System.Drawing.FontStyle.Bold == (fontStyle & System.Drawing.FontStyle.Bold) ? FontWeights.Bold : FontWeights.Normal;
var fsToUse = System.Drawing.FontStyle.Italic == (fontStyle & System.Drawing.FontStyle.Italic) ? FontStyles.Italic : FontStyles.Normal;
// Create and return a System.Windows.Media.TypeFace object...
// if you previously used PrivateFontCollection you might need to load the font from a file.
}
我还没有对此进行适当的性能测试。我觉得这比使用 GDI+ 稍微慢一些。