如何在Win32 C++中进行打印预览?

9
我有一个绘图函数,只需要一个HDC。 但我需要显示一个精确缩放版本的打印内容。
所以目前,我使用CreateCompatibleDC()与打印机HDC和 使用打印机的HDC创建CreateCompatibleBitmap()。
我想这样DC将具有打印机的精确宽度和高度。 当我把字体选择到这个HDC中时,文本将被缩放得像打印机一样准确。
不幸的是,我不能使用StretchBlt()将此HDC的像素复制到控件的HDC中,因为它们属于不同的HDC类型,我想是这个原因。
如果我从具有与打印机页面相同w,h的窗口的HDC创建“内存画布”, 那么字体看起来非常小,因为它们被缩放到屏幕而不是页面...
我应该从窗口的DC中创建CreateCompatibleDC(), 并从打印机的DC中创建CreateCompatibleBitmap()或者其他方法吗?
如果有人能解释正确的做法。 (并且仍然具有完全按照打印机打印的外观)...
嗯,我会很感激的!
...Steve

我对我决定的路线并不感到自豪...:/ 获取打印机DC信息 - 宽度、高度和logpixelsy。从打印预览窗口的DC创建CompatibleDC、位图,但宽度、高度为打印机大小(超大内存杀手位图)。当我CreateFont时,我使用-::MulDiv(point,logPxlY,72)来设置高度(使用打印机dc中的logPxlY)。所以,我希望重新审视这个不太优化的解决方案,但我首先要添加其他功能...:/ http://pianocheater.com fyi :) - Stephen Hazel
啊!这不是一个好的解决方案(我不为之感到自豪)。在我的拥有4GB内存的Vista机器上,我无法创建一个与打印机兼容的屏幕兼容位图,其宽度和高度为w和h - 出现了内存不足的错误 - 可能是显卡内存的问题,因为我确定它可以适应常规内存的4GB。回到绘图板上... - Stephen Hazel
我知道这是一段时间以前的事情了,但你是否曾尝试过从打印机兼容的DC创建位图,然后取消选择它并将其选择到新的屏幕兼容的DC中进行StretchBlt?此外,您可以将位图的尺寸和坐标除以2,以使其适合而不会冒太多失真的风险。 - Mark Ransom
我很惭愧,我只是让它保持原样,最终购买了一台更好的图形卡片的电脑,问题(对于我来说)解决了。它是一个免费软件应用,所以我不感到有罪。 我将更新打印功能,但它不会有打印预览... 它在屏幕上已经看起来一样了。(这是一些音符记谱软件。) - Stephen Hazel
3个回答

10

根据您想要的精度,这可能会变得很困难。

有许多方法。听起来你正试图绘制到一张打印机大小的位图,然后缩小它。做到这一点的步骤如下:

  1. 为打印机创建一个DC(最好是IC-信息内容)
  2. 查询打印机DC以了解分辨率、页面大小、物理偏移等信息。
  3. 为窗口/屏幕创建一个DC。
  4. 创建一个兼容DC(内存DC)。
  5. 为窗口/屏幕创建一个兼容位图,但其大小应该是打印机页面的像素大小。(采用这种方法的问题在于这是一张巨大的位图,可能会失败)
  6. 将兼容位图选择到内存DC中。
  7. 使用与实际打印机相同的坐标在内存DC上绘制。(在选择字体时,请确保将它们缩放到打印机的逻辑英寸而不是屏幕的逻辑英寸)
  8. StretchBlt 内存DC到窗口,这将缩小整个图像。您可能需要尝试不同的拉伸模式,以找到适合要显示的图像类型的最佳效果。
  9. 释放所有资源。

但在朝这个方向前,考虑一下其他选择。该方法涉及分配一个巨大的离屏位图。这可能会在资源匮乏的计算机上失败。即使它没有失败,您也可能会饿死其他应用程序。

在另一个答案中给出的元文件方法是许多应用程序的不错选择。我会从这个开始。

另一种方法是找出所有尺寸的虚构高分辨率单位。例如,假设所有尺寸都以1000分之一英寸为单位。然后,您的绘图例程将把这个想象的单位缩放到目标设备使用的实际dpi。

这种方法存在问题(可能元文件也是如此),因为GDI字体不是完全线性缩放的。根据目标分辨率调整单个字符的宽度。在高分辨率设备上(如300+ dpi激光打印机),此调整最小。但是在96 dpi屏幕上,修正值可能会在一行长度内累加到显着的误差。因此,在预览窗口中的文本可能看起来失真(通常比打印页面更宽)。
因此,“强硬派”方法是在打印机上下文中测量文本大小,再在屏幕上下文中测量文本大小,并对其进行调整以解决差异。例如(使用虚构数字),您可以在打印机上下文中测量某些文本的宽度,结果为900个打印机像素。假设打印机像素与屏幕像素的比率为3:1。您期望屏幕上相同的文本宽度为300个屏幕像素。但是在屏幕上下文中测量时,可能得到325个屏幕像素这样的值。绘制到屏幕上时,您必须使文本窄25个像素。可以将字符压缩在一起,或选择稍小的字体然后将其拉伸以达到目的。
“强硬派”方法涉及更多的复杂性。例如,您可以尝试检测打印机驱动程序进行的字体替换,并尽可能匹配可用的屏幕字体。
我用大位图和“强硬派”方法的混合方法取得了良好的效果。而不是为整个页面制作巨型位图,我制作足够一行文本大小的一个大位图。然后我按打印机大小在离屏位图上绘制并使用StretchBlt将其缩小到屏幕大小。这消除了在稍微降低字体质量的情况下处理大小差异。适用于实际的打印预览,但不适用于构建所见即所得的编辑器。一行位图足够小,可以使这个操作变得实用。
好消息是,只有文本较难。所有其他绘图都是坐标和大小的简单缩放。

我很少使用GDI+,但我认为它取消了非线性字体缩放。所以如果您正在使用GDI+,您只需要缩放坐标。缺点是我认为GDI+的字体质量不如其他技术好。

最后,如果您是Vista或更新版本上的本机应用程序,请确保将进程标记为“DPI-aware”。否则,如果用户在高DPI屏幕上,则Windows会向您声明分辨率仅为96 dpi,并对您绘制的任何内容进行模糊放大。这会降低视觉质量,并使调试打印预览更加复杂。由于许多程序无法适应更高DPI的屏幕,Microsoft从Vista开始默认添加了“高DPI缩放”。

编辑补充:

另一个警告:如果您使用打印机大小的位图选择HFONT,那么在实际打印机DC中选择相同的HFONT可能会得到不同的字体。这是因为一些打印机驱动程序将常见字体替换为内存中的字体。例如,某些PostScript打印机将为某些常见TrueType字体替换内部PostScript字体。

您可以首先将HFONT选择到打印机IC中,然后使用GDI函数如GetTextFaceGetTextMetrics和可能的GetOutlineTextMetrics来了解所选实际字体。然后您可以创建一个新的LOGFONT,尝试更接近打印机使用的字体,将其转换为HFONT,并将其选择到内存DC中。这是真正好的实现标志。

另一个编辑:

我最近编写了新代码,使用了增强型元文件,并且效果很好,至少在没有字体替换的情况下对于TrueType和OpenType字体。这消除了我之前所描述的创建屏幕字体以与打印机字体按比例匹配的所有工作。您只需运行正常的打印代码,并像将打印输出到打印机DC一样将其打印到增强型元文件DC。


圣鳄梨!感谢您提供的详细信息。仅仅指导别人方向还不够好吗?您得铺平道路,用砖头和沙子把路面铺得光滑... :) 看起来我有很多东西要测试。非常感谢! - Stephen Hazel
GDI+可能不是输出的最佳选择,但它非常适合缩放 - 比StretchBlt获得更好的结果。 - Mark Ransom
@MarkRansom:在进行打印预览时,缩小比例通常并不是屏幕上文本可读性的最高优先级。 - Adrian McCarthy

3

有一个值得尝试的方法是创建一个增强型元文件 DC,像平常一样绘制,然后使用打印机度量来缩放这个元文件。这是WTL BmpView示例所采用的方法 - 我不知道这种方法的准确性如何,但值得一试(将相关类移植到 Win32 应该很容易,但 WTL 是 Win32 编程的极佳替代品,值得利用)。


太遗憾了,我不能选择两个答案 :/ 我一定会尝试这种方法(也)。 非常感谢! - Stephen Hazel

2

由于打印机 DC 的分辨率更高,因此打印出的内容看起来会有所不同,您需要编写一种转换方法。我建议使用您之前已经成功过的方法,但是如果文本太小,可以将每个位置/字体大小乘以打印窗口宽度并除以源窗口宽度。


那也是我的最初想法。但是,现在看来我得到了更多的东西 :) - Stephen Hazel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接