【问题标题】:Incorrect Character spacing When calling GraphicsPath.AddString In Windows 8 or newer在 Windows 8 或更高版本中调用 GraphicsPath.AddString 时字符间距不正确
【发布时间】:2017-06-28 14:50:55
【问题描述】:

我注意到在 Windows 8.1 和更新的操作系统上,使用 GraphicsPath.AddString() 绘制文本时字形之间的间距不正确。

这个问题在 Script MT Bold 等字体和其他脚本字体上尤为明显。您可以看到字符之间的线没有正确地相互连接。

我尝试使用不同的 StringFormat 配置调用 AddString(),但没有成功。我在使用 PrivateFontCollection 和普通字体时都看到了这种行为。

对于我们的用例,我们必须继续使用 GraphicsPath 对象。

TextRender.DrawText 和 WPF 行为正确。

我怀疑这是 GDI+ 中的错误。

我正在寻找一种解决方法或使 GraphicsPath.AddString() 开始正常运行的方法。

我们也在使用 PrivateFontCollection。

【问题讨论】:

  • 可能与stackoverflow.com/questions/31140819/… 有关。我目前正在与 Microsoft 开发人员支持工程师联系。我还简单地尝试了一种解决方法,我使用 WPF FormattedText 绘制文本,然后将 Geometry 转换为 GraphicsPath。看起来很有希望,但 WPF 缺少我们需要的 PrivateFontCollection。如果我解决了问题,可能会将其作为答案提交。

标签: .net winforms fonts gdi+


【解决方案1】:

如果使用 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+ 稍微慢一些。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-02-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多