【问题标题】:how do I do print preview in win32 c++?如何在 win32 c++ 中进行打印预览?
【发布时间】:2011-01-15 11:07:27
【问题描述】:

我有一个只需要 HDC 的绘图功能。 但我需要展示将要打印的内容的精确缩放版本。

所以目前,我使用 CreateCompatibleDC() 与打印机 HDC 和 CreateCompatibleBitmap() 与打印机的 HDC。

我认为这样 DC 将具有打印机的确切宽度和高度。 当我在这个 HDC 中选择字体时,文本将完全按照打印机的比例缩放。

不幸的是,我无法通过 StretchBlt() 将此 HDC 的像素复制到控件的 HDC,因为我猜它们属于不同的 HDC 类型。

如果我从具有与打印机页面相同的 w,h 的窗口 HDC 创建“内存画布”, 字体很小,因为它们是针对屏幕而不是页面缩放的...

我应该从窗口的 DC 和 CreateCompatibleDC() 来自打印机 DC 的 CreateCompatibleBitmap() 之类的??

如果有人可以解释这样做的正确方法。 (并且仍然有一些看起来与打印机上完全相同的东西)......

好吧,我会很感激的!!

...史蒂夫

【问题讨论】:

  • 我对自己决定的路线并不感到骄傲...:/ 获取打印机 DC 信息 - 宽度、高度和 logpixelsy。 CreateCompatibleDC,来自打印预览窗口 DC 的位图,但具有打印机的宽度、高度(mondo 大小的内存杀死位图)当我 CreateFont 时,我使用 -::MulDiv (point, logPxlY, 72) 作为高度(使用来自打印机 dc 的 logPxlY)。太棒了,我希望重新审视这个不是非常理想的解决方案,但我还有其他功能要先添加......:/ pianocheater.com fyi :)
  • 啊!!这不是一个好的解决方案(我不引以为豪的解决方案)。在我的具有 4 Gigs RAM 的 Vista 机器上,我无法使用 w,h 打印机创建屏幕兼容位图的 CompatibleBitmap - 内存不足 - 可能是显卡内存量,因为我确信它适合 4 gigs常规记忆。回到绘图板...
  • 我知道这是不久前的事了,但你有没有试过从打印机兼容的 DC 创建位图,然后取消选择它并将其选择到新的屏幕兼容 DC 中用于 StretchBlt?此外,您可以将位图的尺寸和坐标除以 2 以使其适合而不会有太多失真的风险。
  • 我很惭愧地说我就这样,最终买了一台显卡更好的电脑,问题就消失了(对我来说:)这是一个免费软件应用程序,所以我不感到内疚。我将更新打印,但它不会有打印预览......它在屏幕上看起来已经相同了。 (这是一些音乐符号。)

标签: c++ windows winapi gdi print-preview


【解决方案1】:

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

有很多方法。听起来您正在尝试绘制到打印机大小的位图,然后将其缩小。这样做的步骤是:

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

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

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

另一种方法是计算某个虚构高分辨率单元中的所有尺寸。例如,假设一切都在千分之一英寸。然后,您的绘图例程会将这个虚数单位缩放到目标设备使用的实际 dpi。

最后一种方法(也可能是元文件方法)的问题在于 GDI 字体不能完美地线性缩放。根据目标分辨率调整单个字符的宽度。在高分辨率设备(如 300+ dpi 激光打印机)上,这种调整很少。但是在 96 dpi 的屏幕上,这些调整可能会在一行的长度上增加一个显着的错误。因此,预览窗口中的文本可能与打印页面上的不成比例(通常更宽)。

因此,核心方法是在打印机上下文中测量文本,然后在屏幕上下文中再次测量,并针对差异进行调整。例如(使用虚构的数字),您可能会测量打印机上下文中某些文本的宽度,结果是 900 打印机像素。假设打印机像素与屏幕像素的比率为 3:1。您希望屏幕上的相同文本为 300 屏幕像素宽。但是您在屏幕上下文中进行测量,您会得到一个类似于 325 屏幕像素的值。当您绘制到屏幕上时,您必须以某种方式使文本变窄 25 像素。您可以将字符靠得更近,或者选择稍小的字体,然后将它们拉长。

硬核方法涉及更多复杂性。例如,您可以尝试检测打印机驱动程序所做的字体替换,并尽可能将它们与可用的屏幕字体匹配。

我在大位图和硬核方法的混合方面很幸运。我没有为整个页面制作一个巨大的位图,而是制作了一个足够大以容纳一行文本的位图。然后我以打印机大小绘制到屏幕外位图,StretchBlt 将其缩小到屏幕大小。这消除了在字体质量略有下降的情况下处理大小差异的问题。它适用于实际的打印预览,但您不会想要构建这样的 WYSIWYG 编辑器。单行位图足够小,可以实现这一点。

好消息是只有文字很难。所有其他绘图都是坐标和大小的简单缩放。

我没有经常使用 GDI+,但我认为它消除了非线性字体缩放。所以如果你使用 GDI+,你应该只需要缩放你的坐标。缺点是我不认为 GDI+ 上的字体质量那么好。

最后,如果您是 Vista 或更高版本的本机应用程序,请确保您已将进程标记为“DPI-aware”。否则,如果用户在高 DPI 屏幕上,Windows 会欺骗您并声称分辨率仅为 96 dpi,然后对您绘制的任何内容进行模糊放大。这会降低视觉质量,并使调试打印预览变得更加复杂。由于很多程序不能很好地适应更高 DPI 的屏幕,因此微软从 Vista 开始默认添加了“高 DPI 缩放”。

编辑添加

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

您可以先在打印机IC中选择HFONT,然后使用GetTextFaceGetTextMetricsGetOutlineTextMetrics等GDI函数来了解实际选择的字体。然后,您可以创建一个新的 LOGFONT 以尝试更接近地匹配打印机将使用的内容,将其转换为 HFONT,然后将其选择到您的内存 DC 中。这是一个非常好的实现的标志。

【讨论】:

  • 圣鳄梨酱。感谢您提供所有详细信息。将某人指向正确的方向对你来说还不够好,是吗?必须用砖和沙子铺平地面...... :) 看起来我有很多东西要测试。非常感谢!
  • GDI+ 可能不是输出的最佳选择,但它可以实现出色的缩放 - 比我认为的 StretchBlt 更好。
  • @MarkRansom:缩小打印预览时,屏幕上文本的易读性通常不是最高优先级。
【解决方案2】:

可能值得尝试的一件事是创建一个增强的图元文件 DC,正常绘制它,然后使用打印机指标缩放此图元文件。这是WTL BmpView 示例所使用的方法——我不知道这有多准确,但它可能值得一看(将相关类移植到 Win32 应该很容易,但 WTL 是 Win32 的一个很好的替代品编程所以可能值得利用。)

【讨论】:

  • 太糟糕了,我不能选择 2 个答案:/ 我肯定会尝试这种方法(也)。非常感谢!
【解决方案3】:

它看起来不一样,因为打印机 DC 的分辨率更高,所以你必须编写一个转换函数。我会采用你必须工作的方法,但文本太小,只需将每个位置/字体大小乘以打印机窗口宽度并除以源窗口宽度。

【讨论】:

  • 这也是我最初的想法。但是,看起来我现在得到了更多:)
猜你喜欢
  • 1970-01-01
  • 2011-05-13
  • 1970-01-01
  • 2013-10-19
  • 1970-01-01
  • 2011-08-04
  • 2022-08-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多