5

我正在使用该EM_FORMATRANGE消息将富文本控件的输出呈现给任意设备上下文。但是,当渲染到位图时,位图设备上下文的每英寸点数与显示设备的 DPI 相同,即每英寸 96 点。这比我想要渲染的要低得多。我宁愿以更高的 DPI 进行渲染,以便用户可以放大,也许以后可以在高 DPI 打印机上打印。

我怀疑发生的事情是 RTF 控件调用GetDeviceCaps并获取设备每英寸的像素数LOGPIXELSXLOGPIXELSY然后,它使用此 DPI 值以 100% 的缩放级别呈现文档。Windows 显示设备总是返回 96 DPI 的值,除非系统上使用了大字体(如控制面板中设置的那样)并且应用程序可识别 DPI。

Internet 上的许多示例都建议扩展EM_FORMATRANGE. 这样就可以实现任意 DPI 分辨率。大多数示例通常涉及使用SetMapMode, SetWindowExtEx, 和SetViewportExtEx(例如,参见http://social.msdn.microsoft.com/Forums/en-us/netfxbcl/thread/37fd1bfb-f07b-421d-9b5e-5f4492ffbbc3)。这些函数可用于缩放富文本控件的呈现输出:例如,如果我指定 400% 缩放,那么如果富文本控件呈现 5 像素宽的内容,它实际上将变为 20 像素宽。

不幸的是,旧的 GDI 函数使用整数而不是浮点数。例如,假设 RTF 控件决定应在 (12.7, 15.3) 像素处绘制元素。这将四舍五入到 (13, 15) 的位置。这些四舍五入的坐标被传递给 GDI,然后 GDI 使用指定的缩放比例放大图像SetMapMode:对于 400% 的示例,它将是 (13*4, 15*4) 或 (52, 60)。但这并不准确:元素最好放在 (12.7*4, 15.3*4) 或 (51, 61) 处。最糟糕的是,在某些情况下,误差会累积。

我相信这是缩放一些简单文本时这个非常明显的错误的根本原因:

使用 EM_FORMATRANGE<code> 渲染并使用</code>SetMapMode 缩放

EM_FORMATRANGE上面的示例是 8 点 Segoe UI ,使用SetMapMode96 DPI 显示设备上下文缩放到 400% 。文字现在变成了 32 点大小,但是每个字符之间的间距太高,看起来不自然。

在写字板中放大到 400%

上面的示例是在写字板中创建的,通过将文本输入为 8 点 Segoe UI,然后使用缩放控件将缩放级别设置为 400%。每个字符之间的空间看起来很正常。使用 32 磅字体和 100% 缩放级别可实现完全相同的结果。

为了解决这个问题,我尝试了以下方法。对于尝试的每一件事,当缩放到 400% 时,结果同样不令人满意。

  • 使用缩放变换集 using而不是使用等SetWorldTransform完成的缩放。SetMapModeSetWindowExtEx
  • 将元文件的设备上下文传递给EM_FORMATRANGE,然后稍后缩放元文件。
  • 与渲染到图元文件一起使用SetMapMode缩放,然后在不缩放的情况下显示图元文件。

我相信结果总是不能令人满意,因为问题归结为富编辑控件四舍五入到最接近的整数并渲染到它认为是 96 DPI 设备的事实 - 忽略了适当的转换。我查看了元文件格式,我发现单个字符位置实际上以像素级分辨率存储在元文件中 - 这就是为什么缩放元文件显然不起作用,因为此时已经发生了舍入。

我可以想到两个可以解决此问题的真正解决方案:

  • 使用具有更高用户指定的每英寸点数的设备上下文,以便GetDeviceCaps返回不同的值。(注意:一些示例建议使用打印机设备,因为它们通常具有更高的 DPI,但我希望我的代码可以在没有打印机的系统上工作并且能够渲染到屏幕外缓冲区)。
  • 某种方式告诉富编辑控件假设设备上下文的每英寸点数与GetDeviceCaps.

其他任何东西似乎仍然会受到这些舍入误差的影响。

是否有人 (1) 知道如何实现我提出的任一解决方案,或者 (2) 对如何实现将准确的高 DPI 输出放入缓冲区的目标有另一种想法?

4

4 回答 4

0

您可以将控件中所有文本的点大小乘以 4 倍,然后将控件呈现为 4 倍大的位图。

如果您自己填充控件,这将非常简单。如果您支持用户输入的任意内容,这将需要更多的工作,并且需要额外的努力来处理任何不是文本的内容(例如嵌入式位图)。

于 2012-09-28T20:30:34.853 回答
0

尝试使用EM_SETZOOM消息让丰富的编辑控件缩放输出本身。

于 2012-10-10T20:21:13.060 回答
0

我有完全相同的问题。

一个快速的解决方案是将文本绘制成 100% 比例的位图,然后只缩放位图。这不是最好的解决方案,但它可能对你有用。

你有没有找到更好的解决方案?如果是这样,请在这里分享。

另请注意,当您将文本绘制到 100% 元文件然后将元文件缩放到屏幕时也会出现此问题 - 我相信这与不能正常工作的 GDI 文本绘制函数有关缩放。

罗伊

于 2012-07-24T12:59:07.857 回答
0

我刚刚花了两周时间解决一个类似的问题。我需要一个可扩展以进行 WYSISWG 编辑的 Rich Edit。正如我们发现的那样,Windows Rich Edit 控件不支持使用 EM_FORMATRANGE 正确缩放,并且字符间距不会在缩放级别之间更改,并且字体大小只会以离散的字体大小步长进行缩放。

由于我不需要较大的比例差异,因此我选择的解决方案是使用 ITextServices 中的无窗口文本编辑界面以固定分辨率呈现到内部位图。然后我使用 GDI+ 通过三线性过滤将内部位图重新采样到所需的屏幕尺寸。只要比例差异不是太大,结果就可以很好地模拟可扩展的丰富编辑,这足以满足我的需求。

在尝试了许多不同的选项后,我确信您无法使用 windows 丰富的编辑控件获得精确的缩放。您可以编写自己的呈现文本的控件。但是,您需要为每个具有不同样式的文本单独绘制调用。此外,您还需要为您处理所有丰富的编辑句柄,例如突出显示文本、放置光标、处理鼠标和键盘输入、解析 rtf 文本等。在这种情况下,最好只购买第三方组件(我找不到任何合适的免费开源组件)。如果有人想尝试它,我会指出不同 API 的文本渲染的相关起点。

  • GDI - TextOut 未正确设置字符间距。您需要 GetCharacterPlacement 和 ExTextOut。您还需要自己计算缩放比例。你可能不想使用 GDI
  • GDI+ - DrawString 正确处理缩放。GDI+ 是一个合理的选择
  • DirectWrite - 如果您愿意将自己限制在 Vista 平台更新或更高版本,DirectWrite 是 Microsoft 最新的文本 API。

这里还有一个链接,描述了 GDI 和 GDI+ 之间的文本渲染有何不同:

http://windowsclient.net/articles/gdiptext.aspx

于 2012-10-10T19:42:46.757 回答