在WinForms应用程序中准确测量,渲染,命中测试和打印文本

时间:2012-05-24 12:21:56

标签: c# winforms gdi+ gdi textrenderer

我们需要:

  1. 准确测量文字。
  2. 在存在应用于图形上下文的平移和缩放变换时,逐行渲染文本到屏幕图形上下文。
  3. 命中测试:允许使用鼠标或显示的插入符号精确选择文本。
  4. 如果需要,使用打印机尽可能准确地打印结果。 注意:这是次要的。屏幕渲染和命中测试是主要的。
  5. 在Windows XP及更高版本的操作系统上运行。
  6. 在WinForms应用程序中,该应用程序也将图形和图像渲染到相同的图形上下文。

    我们遇到过四种技术。我们尝试使用前两个,并在几个月的时间内遇到了所描述的问题。

    GDI +

    据称独立于决议的文本。但是根据this question和其他来源 - 由于质量问题,应该避免使用这项技术。

    MSDN表示调用Graphics.MeasureString以及StringFormat.GenericTypographicTextRenderingHint.AntiAlias会产生准确的字符串测量。但是,根据我们和其他人的经验,情况并非如此 - 我们没有得到准确的字符串测量。

    • 优点:快速
    • 缺点:字符串测量不准确。

    结果:由于字符串测量不准确而无法使用。

    GDI通过TextRenderer

    这是为了克服GDI +的局限性而引入的。然而,这引入了自己的局限性:

    结果:由于这些原因不可用

    GDI通过p / invoke

    致电GetTextExtentExPoint进行文字测量,并DrawText / DrawTextEx / ExtTextOut进行渲染。

    我们还没有尝试过。

    的DirectWrite

    这看起来很有希望,因为它可以与其他技术(包括GDI / GDI +)互操作,所以我们的其他图形渲染可能不会改变。但是,它仅适用于Windows Vista和更新的Windows版本。这是目前的问题,因为Windows XP仍然具有重要的安装基础。

    问题

    根据要求,哪些技术可以使用?

    注意:关于这个话题有很多错误的信息,所以请在这个领域有专业知识的情况下回答这个问题。 另外,请不要建议WPF - 这不是我们正在考虑使用的东西。

3 个答案:

答案 0 :(得分:4)

据推测,MeasureCharacterRanges功能比MeasureString更准确。

答案 1 :(得分:3)

如果您必须同时定位屏幕和打印机,那么在决定使用哪个渲染引擎之前,您需要对您的方法做出一些决定。以下是一些可能性:

  1. 以屏幕为单位进行布局,并为打印机做最佳近似。
  2. 在打印机单元中布局并对屏幕进行最佳近似。
  3. 在理论上的高分辨率空间中进行布局,并对打印机和屏幕进行最佳近似。
  4. 在所有情况下,最佳可能的近似可能是通过每一步的非常仔细的近似来完成的,这可以为每个设备提供最高的保真度,但代价是匹配的准确性,或者您可以渲染到位图并缩放到设备,它提供较低的保真度,但最好的近似其他空间。

    我用GDI完成了核心打印预览。这很难,但可以进行常规布局。但如果你正在处理任意变换,尤其是+/- 90度以外的旋转,那几乎是不可能的。

    很容易犯下微妙的错误,并相信你得到的测量结果是错误的。

    • 当笔划宽度与dpi处于同一数量级时,字体提示会使字体缩放变为非线性,因此如果某些文本的宽度为w,则双倍宽度字体的大小可能不完全是2*w

    • 字体替换在许多打印机驱动程序中很常见。即使您选择TrueType或OpenType字体,您也可能在打印机上获得与屏幕上相同的字体。

    • 非方形像素在某些打印机中很常见。当你做的不是轴对齐的事情时,这些甚至可能会破坏正常的光栅化器。

    • Kerning可能会令人惊讶。不要假设宽度('a')+宽度('b')==宽度(“ab”)。

    • 很容易实现舍入错误。有时你必须知道底层光栅化器使用的舍入规则。

    • 在错误的环境中进行测量太常见了。不要尝试在屏幕上下文中进行测量并应用转换来获取打印机单元。如果您需要打印机单元,请在打印机上下文中进行测量。

    我今天的观点是,如果您只需要打印预览,那么您应该在打印机单元中布局一个尺寸适合页面的位图,并根据DPI的比例将位图缩放到屏幕。这是最简单的事情,它会产生很好的效果。但目前尚不清楚一个好的硬拷贝和打印预览是否真的是你所追求的。

答案 2 :(得分:3)

我有类似的要求,并遇到了与文本渲染和缩放相同的问题。由于许多原因,即使我尝试使用WPF(.NET 3.5)也是一场噩梦。

我最终使用GDI + graphics.DrawString,尽管被“弃用”,但却有一个有趣的技巧来获得准确的文本测量。

static public RectangleF MeasureInkBox(Graphics graphics, string text, Font font)
{
    var bounds = new RectangleF();
    using (var textPath = new GraphicsPath())
    {
        textPath.AddString(
            text,
            font.FontFamily,
            (int)font.Style,
            font.Size,
            new PointF(0, 0),
            StringFormat.GenericTypographic );
        bounds = textPath.GetBounds();
    }
    return bounds;
}

速度和质量令人满意。此外,graphics.DrawString是使用winforms打印文本的推荐方法。

由于字距调整,测量单个字符位置可能会非常棘手,但我想这个方法稍微复杂一点就可以完成这项工作。例如,对于“Hello”,它将测量“H”,然后是“He”,然后是Hel“,依此类推。它不应该显着降低性能,特别是如果你在接收单击单词时飞行它。” / p>