多页文档

本文介绍 Windows 打印协议,并介绍如何打印包含多个页面的文档。 本文介绍以下主题:

打印协议

若要打印多页文档,框架和视图以以下方式交互。 首先,框架显示“打印”对话框,为打印机创建设备上下文,并调用 CDC 对象的 StartDoc 成员函数。 然后,对于文档的每个页面,框架调用对象的 StartPage 成员函数 CDC ,指示视图对象打印页面,并调用 EndPage 成员函数。 如果在启动特定页面之前必须更改打印机模式,视图将调用 ResetDC,这将更新包含新打印机模式信息的 DEVMODE 结构。 打印整个文档后,框架将调用 EndDoc 成员函数。

重写 View 类函数

CView 类定义打印过程中框架调用的多个成员函数。 通过在视图类中重写这些函数,可以在框架的打印逻辑和视图类的打印逻辑之间提供连接。 下表列出了这些成员函数。

用于打印的 CView 可重写函数

名称 重写的原因
OnPreparePrinting 在“打印”对话框中插入值,尤其是文档的长度
OnBeginPrinting 分配字体或其他 GDI 资源
OnPrepareDC 调整给定页面的设备上下文的属性,或执行打印时分页
OnPrint 打印给定页面
OnEndPrinting 解除分配 GDI 资源

也可以在其他函数中执行与打印相关的处理,但这些函数是驱动打印过程的函数。

下图演示了打印过程中涉及的步骤,并显示了每个 CView打印成员函数的调用位置。 本文的其余部分更详细地介绍了这些步骤。 在 分配 GDI 资源的文章中介绍了打印过程的其他部分。

打印循环过程。
打印循环

分页

框架将有关打印作业的大部分信息存储在 CPrintInfo 结构中。 与分页相关的几个值 CPrintInfo ;这些值可访问,如下表所示。

CPrintInfo 中存储的页码信息

成员变量或

函数名称(s)
引用的页码
GetMinPage/SetMinPage 文档的第一页
GetMaxPage/SetMaxPage 文档的最后一页
GetFromPage 要打印的第一页
GetToPage 要打印的最后一页
m_nCurPage 当前正在打印的页面

页码从 1 开始,即第一页编号为 1,而不是 0。 有关 CPrintInfo 的这些成员和其他成员的详细信息,请参阅 MFC 参考

在打印过程开始时,框架调用视图的 OnPreparePrinting 成员函数,并将指针 CPrintInfo 传递给结构。 应用程序向导提供调用 DoPreparePrinting 的实现OnPreparePrinting,该函数是另一个成员函数CViewDoPreparePrinting 是显示“打印”对话框并创建打印机设备上下文的函数。

此时,应用程序不知道文档中有多少页。 它对文档的第一页和最后一页的数字使用默认值 1 和0xFFFF。 如果知道文档具有多少页,请在将DoPreparePrinting文档发送到结构之前重写OnPreparePrinting和调用 [SetMaxPage]--brokenlink---(reference/cprintinfo-class.md#setmaxpage)。CPrintInfo 这样就可以指定文档的长度。

DoPreparePrinting 然后显示“打印”对话框。 返回时, CPrintInfo 结构包含用户指定的值。 如果用户希望仅打印所选页面范围,则他或她可以在“打印”对话框中指定起始页码和结束页码。 框架使用 GetFromPageCPrintInfo 的函数检索GetToPage这些值。 如果用户未指定页面范围,框架将调用 GetMinPage 并使用 GetMaxPage 返回的值来打印整个文档。

对于要打印的文档的每一页,框架在视图类 (OnPrepareDCOnPrint)中调用两个成员函数,并传递每个函数两个参数:指向 CDC 对象的指针和指向 CPrintInfo 结构的指针。 每次框架调用 OnPrepareDC ,它 OnPrint都会在结构 m_nCurPage 成员 CPrintInfo 中传递不同的值。 这样,框架就会告知视图应打印哪个页面。

OnPrepareDC 成员函数也用于屏幕显示。 在绘图发生之前,它会调整设备上下文。 OnPrepareDC 在打印方面具有类似的作用,但存在一些差异:首先,对象 CDC 表示打印机设备上下文而不是屏幕设备上下文,第二个 CPrintInfo 是作为第二个参数传递对象。 (调用屏幕显示时OnPrepareDC此参数为 NULL。重写OnPrepareDC以根据打印页面对设备上下文进行调整。 例如,可以移动视区原点和剪裁区域,以确保打印文档的相应部分。

OnPrint 成员函数执行页面的实际打印。 本文 介绍了 框架如何使用打印机设备上下文调用 OnDraw 来执行打印。 更确切地说,框架使用CPrintInfo结构和设备上下文调用OnPrint,并将OnPrint设备上下文传递给 OnDraw。 重写 OnPrint 以执行仅在打印期间执行的任何呈现,而不执行屏幕显示。 例如,若要打印页眉或页脚(有关详细信息,请参阅文章 页眉和页脚 )。 然后从重写OnPrint调用OnDraw以执行屏幕显示和打印共有的呈现。

为屏幕显示和打印执行呈现的事实 OnDraw 意味着应用程序是 WYSIWYG:“你看到的内容是你得到的。但是,假设你未编写 WYSIWYG 应用程序。 例如,请考虑使用加粗字体打印的文本编辑器,但显示控件代码以指示屏幕上的粗体文本。 在这种情况下,严格地用于 OnDraw 屏幕显示。 重写 OnPrint时,请将调用 OnDraw 替换为对单独的绘图函数的调用。 该函数使用未在屏幕上显示的属性,以纸张上显示的方式绘制文档。

打印机页与文档页

引用页码时,有时有必要区分打印机对页面的概念和文档的页面概念。 从打印机的角度来看,页面是一张纸。 但是,一张纸不一定等于文档的一页。 例如,如果要打印新闻稿,其中要折叠工作表,则一张纸可能包含文档的第一页和最后一页并排。 同样,如果要打印电子表格,文档根本不包含页面。 相反,一张纸可能包含第 1 到 20 行,第 6 到 10 列。

CPrintInfo 结构中的所有页码都引用打印机页。 框架调用 OnPrepareDCOnPrint 一次用于通过打印机的每个纸张。 重写 OnPreparePrinting 函数以指定文档的长度时,必须使用打印机页。 如果有一对一对应关系(即一个打印机页等于一个文档页),则这很容易。 另一方面,如果文档页和打印机页不直接对应,则必须在它们之间翻译。 例如,请考虑打印电子表格。 重写 OnPreparePrinting时,必须计算打印整个电子表格所需的纸张数,然后在调用 SetMaxPage 成员函数 CPrintInfo时使用该值。 同样,在重写 OnPrepareDC时,必须将 m_nCurPage 转换为将显示在该特定工作表上的行和列的范围,然后相应地调整视区原点。

Print-Time 分页

在某些情况下,视图类可能事先不知道文档的打印时间。 例如,假设应用程序不是 WYSIWYG,因此打印时屏幕上的文档长度与文档的长度不对应。

当替代视图类 的 OnPreparePrinting 时,这会导致问题:无法将值传递给 SetMaxPageCPrintInfo 结构的函数,因为不知道文档的长度。 如果用户未指定使用“打印”对话框停止的页码,框架不知道何时停止打印循环。 确定何时停止打印循环的唯一方法是打印出文档并查看文档的结束时间。 视图类必须在打印文档时检查文档的末尾,然后在到达末尾时通知框架。

框架依赖于视图类的 OnPrepareDC 函数来判断何时停止。 每次调用OnPrepareDC后,框架都会检查名为m_bContinuePrinting的结构的成员CPrintInfo。 其默认值为 TRUE。 只要它保持不变,框架将继续打印循环。 如果设置为 FALSE,框架将停止。 若要执行打印时分页,请重写 OnPrepareDC 以检查文档的末尾是否已到达,并将 m_bContinuePrinting 设置为 FALSE

如果当前页大于 1,则集的默认实现OnPrepareDCm_bContinuePrintingFALSE。 这意味着,如果未指定文档的长度,框架假定文档长度为一页。 其一个后果是,如果调用基类版本的基类版本 OnPrepareDC,则必须小心。 不要假定在调用基类版本后 m_bContinuePrintingTRUE

想要详细了解哪些内容

另请参阅

打印
CView 类
CDC 类