17.6.6 打印文档(1)
在函数OnPrint()中,可以将数据写入打印机设备上下文。打印每个页面时都将调用这个函数。需要使用CSketcherView类的Properties窗口在这个类中添加这个函数的重载函数。从重载函数的列表中选择OnPrint,然后在右边的列中单击<Add> OnPrint。
从CPrintInfo对象的成员m_nCurPage中可以获得当前页的页码,然后使用这个值计算文档中对应于当前页左上角的位置的坐标。利用一个示例可以充分理解这种方法,假设现在要打印一个8页文档的第7页,如图17-7所示。

这些是逻辑坐标,x正方向是从左到右,y轴的方向是从上到下,其值是负数。将这个页码减1,然后除以文档打印区域所需的页面宽度的数量,取其余数,就可以得到这一页水平位置的索引。把这个结果乘以printWidth,就得到该页面左上角的x坐标,它相对于包围文档中元素的矩形的左上角。类似地,将当前页码减1,然后除以文档水平宽度上所需的页宽度的数量,就可以确定文档垂直位置的索引。将余数乘以printLength,将得到该页左上角的相对y坐标。可以在下列两个语句中表示这两种计算:
- CPrintData* p(static_cast<CPrintData*>(pInfo->m_lpUserData));
- int xOrg = p->m_DocRefPoint.x + p->printWidth*
- ((pInfo->m_nCurPage - 1)%(p->m_nWidths));
- int yOrg = p->m_DocRefPoint.y + p->printLength*
- ((pInfo->m_nCurPage - 1)/(p->m_nWidths));
如果能够在每一页的顶部打印出文档的文件名,在每一页的底部打印出页码,就比较理想了。但是希望能够确保打印的文档数据不会盖住文件名和页码。还希望打印区域在页面的中间。为此,可以在打印出文件名以后,在打印机设备上下文中移动坐标系统的原点。图17-8对此进行了说明。
图17-8说明了设备上下文中打印页面区域和要在文档数据的参考框架中打印的页面之间的联系。图中给出了计算偏移量的表达式,它们是偏移打印该页面的printWidth×printLength区域原点的距离。由于想在页面上所示的虚线区域中打印文档的信息,因此需要将文档中的点xOrg、yOrg映射到打印页面中所示的位置,它是由页面原点偏移xOffset和yOffset值而得到的。
默认情况下,在定义文档中元素时使用的坐标系统的原点将映射为设备环境中的原点,但是可以进行修改。CDC对象为此提供了一个函数SetWindowOrg()。这个函数可以在文档的逻辑坐标系统中定义一个对应于设备上下文中原点的点,这里是打印机输出所在的点(0,0)。从函数SetWindowOrg()返回的旧原点一定要保存起来。在完成当前页的绘制以后,必须还原这个旧原点;否则,在打印下一页时就不能正确地设置CPrintInfo对象的成员m_rectDraw。

文档中要映射为页面原点的点具有坐标xOrg-xOffset、yOrg-yOffset。这也许不容易形象化,但是记住,通过设置窗口原点,我们将定义映射为视口原点的点。如果考虑到这一点,就应当明白文档中的点xOrg、yOrg正是页面中需要的点。
打印一页文档的完整代码是:
- // Print a page of the document
- void CSketcherView::OnPrint(CDC* pDC, CPrintInfo* pInfo)
- {
- CPrintData* p(static_cast<CPrintData*>(pInfo->m_lpUserData));
- // Output the document filename
- pDC->SetTextAlign(TA_CENTER); // Center the following text
- pDC->TextOut(pInfo->m_rectDraw.right/2, 20, p->m_DocTitle);
- CString str; str.Format(_T(“Page %u”) , pInfo->m_nCurPage);
- pDC->TextOut(pInfo->m_rectDraw.right/2, pInfo->m_rectDraw.bottom-20, str);
- pDC->SetTextAlign(TA_LEFT); // Left justify text
- // Calculate the origin point for the current page
- int xOrg = p->m_DocRefPoint.x +
- p->printWidth*((pInfo->m_nCurPage - 1)%(p->m_nWidths));
- int yOrg = p->m_DocRefPoint.y +
- p->printLength*((pInfo->m_nCurPage - 1)/(p->m_nWidths));
- // Calculate offsets to center drawing area on page as positive values
- int xOffset = (pInfo->m_rectDraw.right - p->printWidth)/2;
- int yOffset = (pInfo->m_rectDraw.bottom - p->printLength)/2;
- // Change window origin to correspond to current page & save old origin
- CPoint OldOrg = pDC->SetWindowOrg(xOrg - xOffset, yOrg - yOffset);
- // Define a clip rectangle the size of the printed area
- pDC->IntersectClipRect(xOrg, yOrg, xOrg + p->printWidth, yOrg + p->printLength);
- OnDraw(pDC); // Draw the whole document
- pDC->SelectClipRgn(nullptr); // Remove the clip rectangle
- pDC->SetWindowOrg(OldOrg); // Restore old window origin
- }