我正在使用该EM_FORMATRANGE
消息将富文本控件的输出呈现给任意设备上下文。但是,当渲染到位图时,位图设备上下文的每英寸点数与显示设备的 DPI 相同,即每英寸 96 点。这比我想要渲染的要低得多。我宁愿以更高的 DPI 进行渲染,以便用户可以放大,也许以后可以在高 DPI 打印机上打印。
我怀疑发生的事情是 RTF 控件调用GetDeviceCaps
并获取设备每英寸的像素数LOGPIXELSX
。LOGPIXELSY
然后,它使用此 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
上面的示例是 8 点 Segoe UI ,使用SetMapMode
96 DPI 显示设备上下文缩放到 400% 。文字现在变成了 32 点大小,但是每个字符之间的间距太高,看起来不自然。
上面的示例是在写字板中创建的,通过将文本输入为 8 点 Segoe UI,然后使用缩放控件将缩放级别设置为 400%。每个字符之间的空间看起来很正常。使用 32 磅字体和 100% 缩放级别可实现完全相同的结果。
为了解决这个问题,我尝试了以下方法。对于尝试的每一件事,当缩放到 400% 时,结果同样不令人满意。
- 使用缩放变换集 using而不是使用等
SetWorldTransform
完成的缩放。SetMapMode
SetWindowExtEx
- 将元文件的设备上下文传递给
EM_FORMATRANGE
,然后稍后缩放元文件。 - 与渲染到图元文件一起使用
SetMapMode
缩放,然后在不缩放的情况下显示图元文件。
我相信结果总是不能令人满意,因为问题归结为富编辑控件四舍五入到最接近的整数并渲染到它认为是 96 DPI 设备的事实 - 忽略了适当的转换。我查看了元文件格式,我发现单个字符位置实际上以像素级分辨率存储在元文件中 - 这就是为什么缩放元文件显然不起作用,因为此时已经发生了舍入。
我可以想到两个可以解决此问题的真正解决方案:
- 使用具有更高用户指定的每英寸点数的设备上下文,以便
GetDeviceCaps
返回不同的值。(注意:一些示例建议使用打印机设备,因为它们通常具有更高的 DPI,但我希望我的代码可以在没有打印机的系统上工作并且能够渲染到屏幕外缓冲区)。 - 某种方式告诉富编辑控件假设设备上下文的每英寸点数与
GetDeviceCaps
.
其他任何东西似乎仍然会受到这些舍入误差的影响。
是否有人 (1) 知道如何实现我提出的任一解决方案,或者 (2) 对如何实现将准确的高 DPI 输出放入缓冲区的目标有另一种想法?