MFC打印及打印预览

 
 

总结了一下其中的一些用法:

1:用CPrintDialog 可以呼出窗口让用户选择打印机或者采用默认的打印机直接打印。

    StartPage…..EndPage  完成一页文档的打印。  如需改变DC设置,需要调用ReSetDC()

    多次调用这两个函数,可以实现打印多页

2:TextOut坐标的确定。

     TextOut中的坐标是DC中的逻辑坐标。

     和实际的物理坐标需要转换。

    GetDeviceCpas();  //获取DC中的Device信息。

    GetTextMetrics(); //获取字体的信息。 字体高度和本身高度+行距。

3:TextOut中的字体可以设置。

    关键代码如下:

     CFont myFont;

     myFont.CreatePontFont(….);

     oldFont = dc.SelectObject();

     myFont.DelectObject()';

 

 打印单位的转换

这个的关键是我们要得到显示设备的DPI,即每英寸设备支持的点数。有了DPI我们就可以在我们习惯的单位和设备单位之间转换了,用到的API是GetDeviceCaps(),这个函数功能强大,具体可参考MSDN,在这里我们只用来得到设备DPI。看示例代码:

    //某些设备水平和垂直方向的DPI可能不同,所以要分别得到。
    
    //得到水平方向的转换比例
    float getXScale(HDC printDC)  //printDC: 打印机DC设备句柄
    {
        //得到打印机水平方向的DPI
        int xDPI = GetDeviceCaps(printDC, LOGPIXELSX);
        
        //254.0: 每英寸25.4毫米
        float xScale = (float)(254.0 / xDPI);
        
        return xScale;
    }
    
    //得到垂直方向的转换比例
    float getYScale(HDC printDC)  //printDC: 打印机DC设备句柄
    {
        //得到打印机垂直方向的DPI
        int yDPI = GetDeviceCaps(printDC, LOGPIXELSY);
        
        float yScale = (float)(254.0 / yDPI);
        
        return yScale;
    }

    //厘米转换为设备像素,水平方向
    double cm2Unit_W(float xScale, double w) //w单位为厘米
    {
        return w*100/xScale;
    }

    //厘米转换为设备像素,垂直方向
    double cm2Unit_H(float yScale, double h) //h单位为厘米
    {
        return h*100/yScale;
    }

上面代码中,如果将printDC换为显示器设备DC句柄,即可得到厘米转换为显示器像素单位,打印预览时使用。得到显示器设备句柄可用::GetDC(NULL)得到。

打印基本流程

有了前面的知识,现在我们就可以打印了。Windows打印是有着固定的流程的,流程也比较简单。按照固定的顺序调用打印API即可:StartDoc() -> StartPage() -> 打印具体内容 -> EndPage() -> EndDoc()。如果要打印多页,循环StartPage()至EndPage()即可。

示例代码:

    DOCINFO   doc;   
    ZeroMemory(&doc,sizeof(doc));   
    doc.cbSize = sizeof(doc);   
    doc.lpszDocName = (LPCTSTR)"Print Test File";
    
    StartDoc(printDC, &doc);  //准备打印

    StartPage(printDC);  //开始打印页
    
    //在(2.5cm, 2.5cm)处打印文本
    char* text = "打印测试文本";
    
    float xScale = getXScale(printDC);
    float yScale = getYScale(printDC);
    
    double x = cm2Unit_W(xScale, 2.5);
    double y = cm2Unit_H(yScale, 2.5);
    
    //在指定位置打印文本
    TextOut(printDC, (int)x, (int)y, (LPTSTR)text, strlen(text));
    
    EndPage(printDC);  //结束打印页
    
    EndDoc(printDC);  //结束打印
打印字体的设置

很多时候我们希望能控制打印字体,下面的代码可以创建你想要的字体格式:

    /*  创建自定义打印字体
        fontFace:字体名称,如果为NULL,默认采用“宋体”
        fontHeight:字体高度,单位为厘米
        isBold:是否加粗
        isItalic:是否斜体
        isUnderLine:是否下划线
        isStrikeOut:是否删除线
    */
    HFONT getFont(LPCTSTR fontFace, double fontHeight, bool isBold, bool isItalic, bool isUnderLine, bool isStrikeOut)
    {
        int width = 0; //字体宽度由系统决定
        int weight = FW_NORMAL;
        DWORD italic = 0;
        DWORD underLine = 0;
        DWORD strikeOut = 0;
        DWORD charSet = DEFAULT_CHARSET;
        LPCTSTR face = (LPCTSTR)"宋体";
        
        float height = cm2Unit_H(fontHeight);
        
        if(fontFace != 0)
        {
            face = fontFace;
        }

        if(isBold)
        {
            weight = FW_BOLD;
        }
        
        if(isItalic)
        {
            italic = 1;
        }
        
        if(isUnderLine)
        {
            underLine = 1;
        }
        
        if(isStrikeOut)
        {
            strikeOut = 1;
        }

        HFONT font = CreateFont((int)height, width, 0, 0, 
                                 weight, italic, underLine, strikeOut, charSet,
                                 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, PROOF_QUALITY, 
                                 DEFAULT_PITCH | FF_DONTCARE, face );
        return font;
    }
小结

以上就是我们在实现打印中遇到的常见问题,相信理解了这些,在我们自己的程序中实现打印就不再是什么困难的事情了。

 

 

 

打印协议

为打印多页文档,框架与视图以如下方式交互。首先,框架显示“打印”对话框,为打印机创建设备上下文,并调用

CDC

对象的

StartDoc

成员函数。然后,对于文档中的每一页,框架调用 CDC 对象的

StartPage

成员函数,指示视图对象打印页,并调用

EndPage

成员函数。如果在开始某个特定页之前必须更改打印机模式,视图将调用

ResetDC

,它将更新含有新的打印机模式信息的 结构。全部文档打印完毕后,框架调用

EndDoc

成员函数。

重写视图类函数

CView

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

用于打印的 CView 可重写函数

名称
重写原因

OnPreparePrinting

在“打印”对话框中插入值,尤其是文档的长度

OnBeginPrinting

分配字体或其他 GDI 资源

OnPrepareDC

调整给定页的设备上下文属性,或进行打印时分页

OnPrint

打印给定页

OnEndPrinting

解除分配 GDI 资源

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

下图阐释了打印过程所涉及的步骤,并显示了在何处调用 CView 的每个打印成员函数。本文其余部分是对大多数步骤更详细的解释。一文介绍了打印过程的其他部分。

打印循环

分页

框架在

CPrintInfo

结构中存储许多关于打印作业的信息。CPrintInfo 中的几个值是关于分页的;这些值如下表所示。

存储在 CPrintInfo 中的页码信息

成员变量或函数名
引用的页码

GetMinPage/SetMinPage
文档的第一页

GetMaxPage/SetMaxPage
文档的最后一页

GetFromPage
要打印的第一页

GetToPage
要打印的最后一页

m_nCurPage
当前正在打印的页

页码从 1 开始,也就是说,第一页的编号为 1,而不是 0。有关上述及其他

CPrintInfo

成员的更多信息,请参见“MFC 参考”。

在打印过程的开始,框架调用视图的

OnPreparePrinting

成员函数,向 CPrintInfo 结构传递一个指针。“应用程序向导”提供调用 CView 的另一个成员函数

DoPreparePrinting

OnPreparePrinting 实现。DoPreparePrinting 是显示“打印”对话框并创建打印机设备上下文的函数。

这时,应用程序并不知道文档的页数。它对文档的第一页和最后一页的页码使用默认值 1 和 0xFFFF。如果您知道文档的页数,在将文档发送到 DoPreparePrinting 之前,请重写 OnPreparePrinting,并为 CPrintInfo 结构调用

SetMaxPage

。这使您得以指定文档的长度。

接下来 DoPreparePrinting 显示“打印”对话框。当它返回时,CPrintInfo 结构包含用户指定的值。如果用户希望只打印选定页码范围内的文档,他或她可以在“打印”对话框中指定起始页码和终止页码。框架使用

CPrintInfo

GetFromPageGetToPage 函数检索这些值。如果用户未指定页码范围,框架将调用 GetMinPageGetMaxPage 并使用返回的值来打印整个文档。

对于文档中将要打印的每一页,框架调用视图类中的两个成员函数(

OnPrepareDC

OnPrint

),并传递给每个函数两个参数:一个指向

CDC

对象的指针和一个指向 CPrintInfo 结构的指针。每次当框架调用 OnPrepareDCOnPrint 时,它都传递 CPrintInfo 结构的 m_nCurPage 成员中的不同值。框架通过这种方式通知视图要打印哪些页。

OnPrepareDC

成员函数也可用于屏幕显示。它在绘制之前调整设备上下文。OnPrepareDC 在打印中起同样的作用,但有两点不同:首先,CDC 对象表示打印机设备上下文而不是屏幕设备上下文;其次,CPrintInfo 对象作为第二个参数传递。(当为屏幕显示调用 OnPrepareDC 时,此参数为 NULL。)重写 OnPrepareDC 以便根据正在打印的页的不同来调整设备上下文。例如,可通过移动视区原点和剪辑区域,确保文档中的适当部分得到打印。

OnPrint

成员函数执行页的实际打印。一文显示了框架是如何用打印机设备上下文调用

OnDraw

来执行打印的。更确切地说,框架用 CPrintInfo 结构和设备上下文调用 OnPrintOnPrint 将设备上下文传递给 OnDraw。重写 OnPrint,执行任何只应在打印而非屏幕显示期间完成的呈现。例如,打印页眉或页脚(有关更多信息,请参见一文)。然后,从 OnPrint 的重写调用 OnDraw 来进行屏幕显示和打印的共同呈现。

OnDraw 既进行屏幕显示呈现又进行打印呈现的事实意味着您的应用程序是 WYSIWYG(所见即所得)。然而,假设您正在写的不是“所见即所得”应用程序。例如,一个文本编辑器使用粗体作为打印字体,但同时显示控制代码指示屏幕上粗体文本。对于这种情况,应严格使用 OnDraw 进行屏幕显示。当您重写 OnPrint 时,用一个对单独绘制函数的调用取代对 OnDraw 的调用。该函数使用未显示在屏幕上的属性,以文档显示在纸上的方式绘制文档。

打印机页与文档页

提到页码,有时必须得区分打印机页的概念和文档页的概念。从打印机的角度讲,一页是一张纸。但是,一张纸并不一定等于文档中的一页。例如,如果打印一份通讯稿,纸张将要折叠,一张纸可能同时含有文档的第一页和最后一页(它们是并排的)。同样,如果打印一份电子表格,文档根本不是由页组成的。相反,一张纸可能包含第一到第二十行,第六到第十列。

所有

CPrintInfo

结构中的页码都是指打印机页。框架对通过打印机的每一页纸调用一次 OnPrepareDCOnPrint。当重写

OnPreparePrinting

函数来指定文档长度时,必须使用打印机页。如果存在一一对应关系(即,一个打印机页等于一个文档页),那么事情就简单了。反之,如果文档页与打印机页并非一一对应,则必须在两者之间进行转换。例如,打印一份电子表格。当重写 OnPreparePrinting 时,必须计算打印整个电子表格所需的纸张数,并在调用 CPrintInfoSetMaxPage 成员函数时使用该值。同样,当重写 OnPrepareDC 时,必须将 m_nCurPage 转换成会出现在特定纸张上的行和列的范围,然后相应调整视区原点。

打印时分页

有些情况下,您的视图类可能在文档实际打印之前并不会事先知道文档的长度。例如,如果您的应用程序不是“所见即所得”的,那么屏幕上的文档长度并不对应于其打印时的长度。

当您为视图类重写

OnPreparePrinting

时,就会产生这样的问题:无法向

CPrintInfo

结构的 SetMaxPage 函数传递值,因为您不知道文档的长度。如果用户不使用“打印”对话框指定终止页码,框架就不知该在何时停止打印循环。唯一可以确定何时停止打印循环的方法是打印出全部文档并弄清其何时终止。您的视图类必须在文档打印期间检查是否到达文档末尾,并在到达时通知框架。

框架依赖于您的视图类的

OnPrepareDC

函数来告知何时停止。在每次调用 OnPrepareDC 之后,框架检查 CPrintInfo 结构的一个名为 m_bContinuePrinting 的成员。该成员的默认值为 TRUE。只要它保持此默认值,框架就继续打印循环。如果它设置为 FALSE,框架就停止。若要执行打印时分页,请重写 OnPrepareDC 以检查是否已到达文档末尾,如果已经到达,则将 m_bContinuePrinting 设置为 FALSE

如果当前页大于 1,OnPrepareDC 的默认实现将把 m_bContinuePrinting 设置为 FALSE。这意味着如果未指定文档长度,框架将假设文档的长度为一页。这样做的结果之一就是在调用 OnPrepareDC 的基类版本时必须谨慎。不要假定 m_bContinuePrinting 在调用基类版本之后将为 TRUE

2:MSDN上的一段示例代码:

// get the default printer
CPrintDialog dlg(FALSE);
dlg.GetDefaults();

// is a default printer set up?
HDC hdcPrinter = dlg.GetPrinterDC();
if (hdcPrinter == NULL)
{
   MessageBox(_T("Buy a printer!"));
}
else
{
   // create a CDC and attach it to the default printer
   CDC dcPrinter;
   dcPrinter.Attach(hdcPrinter);

   // call StartDoc() to begin printing
   DOCINFO docinfo;
   memset(&docinfo, 0, sizeof(docinfo));
   docinfo.cbSize = sizeof(docinfo);
   docinfo.lpszDocName = _T("CDC::StartDoc() Code Fragment");

   // if it fails, complain and exit gracefully
   if (dcPrinter.StartDoc(&docinfo) < 0)
   {
      MessageBox(_T("Printer wouldn't initalize"));
   }
   else
   {
      // start a page
      if (dcPrinter.StartPage() < 0)
      {
         MessageBox(_T("Could not start page"));
         dcPrinter.AbortDoc();
      }
      else
      {
         // actually do some printing
         CGdiObject* pOldFont = dcPrinter.SelectStockObject(SYSTEM_FONT);

         dcPrinter.TextOut(50, 50, _T("Hello World!"), 12);

         dcPrinter.EndPage();
         dcPrinter.EndDoc();
         dcPrinter.SelectObject(pOldFont);
      }
   }
}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值