我现在要做一个打印输出的程序,请高人帮忙。

解决方案 »

  1.   

    //VC数据库编程中的打印控制
    VC数据库编程中的打印控制比较复杂,但它也给程序员最大的灵活性,而这种灵活性正是我们需要的。因为各行业、部门的报表一般都不太规整,特别是表头部分,二、三重嵌套的情况很常见。下面我们就开发中碰到的一些问题与同行们探讨。   Document/View框架之外的打印   熟悉VC的程序员都知道Microsoft 的AppWizard生成的应用程序框架中,可以选择免费的打印及打印预览功能,但前提是必须选择Document/View结构。然而我们的数据库应用中一般都不需要一个文档类来保存文档,因为数据库(源)就是我们的文档,数据一般是在一个对话框或视中与用户交互,编辑或修改结果直接通过数据库引擎写回数据库中。我们的程序主框架要么是基于对话框的、要么是基于无文档类的单视(或多视)结构,在这种情况下,AppWizard 在打印控制部分并不能给我们任何帮助,只能自己负责完成打印控制。   总结起来,一次打印操作要遵循以下步骤:   1.得到或生成打印设备场景,可通过显示打印对话框让用户选择打印机与纸张等设置,也可在程序中直接取系统缺省打印机设置,然后根据报表格式设置纸张大小和打印方向;   2.开始在该设备场景中的一次打印作业,实际打印报表内容,终止打印作业;   3.清除打印设备场景,完成本次打印操作。   在下面的例子中,我们在一个对话框中让用户选择打印某个报表,没有显示打印设置对话框,而是直接取系统缺省打印机设置,然后根据报表格式设置纸张大小和打印方向。之所以这样做是因为各行业、部门的报表格式一般都是至上而下的统一格式。函数DoPreparePrintDC()、DoPrint()和DoClearPrintDC()分别对应上述的三个步骤:   BOOL CMyDialog::DoPreparePrintDC()   // 准备打印场景   {    #define  FONTSIZE 14    // 获取打印机的设备属性    CPrintDialog dlgPrint( FALSE );    // 得到当前系统缺省打印机设置    if(!dlgPrint.GetDefaults()) return FALSE;    LPDEVMODE pDM=dlgPrint.GetDevMode();    if(pDM==NULL) return FALSE;    ::GlobalUnlock(pDM);    // 联结打印DC,m—hDC是定义为HDC m—hDC的类成员变量    m—hDC=dlgPrint.CreatePrinterDC();    // m—DC是定义为CDC m—DC的类成员变量    if(!m—DC.Attach(m—hDC) return FALSE;    // 设置打印标志    m—DC.m—bPrinting=TRUE;    short cxInch=m—DC.GetDeviceCaps(LOGPIXELSX);    short cyInch=m—DC.GetDeviceCaps(LOGPIXELSY);    // 建立打印字体,m—fontPrint 是定义为CFont m_fontPrint的类成员变量    if(!m—fontPrint.CreateFont(MulDiv(FONTSIZE, -cyInch, 72),0,0,0, FW—NORMAL,0,0,0, GB2312—CHARSET, OUT—CHARACTER—PRECIS, CLIP—CHARACTER—PRECIS,DEFAULT—QUALITY, DEFAULT—PITCH|FF—DONTCARE, HFONTNAME)    return FALSE;    return TRUE;}   void CDlgDataPrint::DoClearPrintDC()   // 清除打印场景   { m—fontPrint.DeleteObject();    m—DC.Detach();    ::DeleteDC( m—hDC );}   void CDlgDataPrint::DoPrint()   // 实际打印输出   { if(DoPreparePrintDC()==FALSE) return;    // 开始一次打印作业    CString str;    str.LoadString(AFX—IDS—APP—TITLE);    DOCINFO di;    ::ZeroMemory (&di, sizeof (DOCINFO));    di.cbSize=sizeof (DOCINFO);    di.lpszDocName=str;    m—DC.StartDoc(&di);    m—DC.StartPage();    // 将打印字体选进设备场景    CFont* pOldFont=m—DC.SelectObject(&m—fontPrint);    // 输出报表,建议用CDC::DrawText()函数便于控制打印范围    ...    // 终止打印作业    m—DC.EndPage();    m—DC.EndDoc();    m—DC.SelectObject(pOldFont);    // 清除打印场景    DoClearPrintDC();}   表格问题   对格式固定或表头、表体比较复杂的情况,特别是有嵌套表格的情况下,我们认为先将打印结果以文本形式写入一临时文件,然后再对文件进行打印输出的方法会使事情简单一些。因为这样在程序中就可以只关心数据(表体)的输出,而表头则可以固定格式存入数据库。   但是,我们在对文本形式的报表进行打印输出时却发现一个有趣的现象,有时输出结果的相邻两行表格竖线不能对齐,而有时又可以。原来是 Windows 图形输出的问题,只要我们在建立打印字体时,字体的高度是偶数就可保证竖线对齐,如果是奇数则不能对齐。知道了原因,在建立打印字体时只需加入如下代码即可:   ...   #define  FONTSIZE 14   #define  HFONTNAME ″宋体″   // 建立打印字体,m—fontPrint是定义为CFont m—fontPrint的类成员变量   int nFontHeight=MulDiv(FONTSIZE,-cyInch, 72);   if(nFontHeight % 2) nFontHeight++;   if(!m—fontPrint.CreateFont(nFontHeight, 0, 0, 0, FW—NORMAL, 0, 0,0,GB2312—CHARSET, OUT—CHARACTER—PRECIS, CLIP—CHARACTER—PRECIS,DEFAULT—QUALITY, DEFAULT—PITCH | FF—DONTCARE, HFONTNAME))    return FALSE;   ...   还有一点小技巧,输出文本形式的报表时,对报表的横线只需要输出其正常高度的二分之一或三分之一,这样报表看起来更紧凑美观。这也是我们在前面建议用CDC::DrawText()进行绘制输出的原因。 
      

  2.   

    //在VC中简易实现所见即所得打印 
    在VC中简易实现所见即所得打印 
    文/赵征宇    在VC++5.0中用应用程序向导(AppWizard)生成的单文档或多文档程序提供了对打印功能的实现,但遗憾的是如果对自动生成的框架程序不做任何改进,打印出来的文档或图形和屏幕上的显示相比就会特别小。为什么会这样呢?本文对这种现象的原因和 MFC的打印机制进行了深入的分析,并提出了一种特别简单的方法,在原有的程序中只需加入几行代码就能解决这一问题,实现所见即所得的打印。 ----首先分析MFC的打印机制,把原理弄清楚了,就不难明白现象形成的原因和提出解决办法。MFC应用程序的核心是文档对象以及相关的视图窗口的概念,即CDocument类和CView类的构成和关系,简单地说CDocument类负责数据的生成和储存,CView类负责数据的显示和用户交互。输出到屏幕和输出到打印机都是数据的显示,实质上是一样的,所以打印功能也是由CView类来实现的。 ----在CView类中由应用程序向导自动生成的源代码提供了一个OnDraw(CDC* pDC)的函数,通过重载这个函数,利用它提供的pDC(设备上下文)指针,可以在屏幕上显示各种图形和数据。CView类的打印是通过OnPrint(CDC* pDC, CPrintInfo* pInfo)这个函数实现的,应用程序向导自动生成的源代码中没有这个函数的框架,而这个函数对打印的实现就是简单地调用OnDraw(CDC* pDC)这个函数,把打印机的设备上下文指针pDC传递给OnDraw(CDC* pDC)函数。 ----可见CView类对输出到屏幕和输出到打印机的处理都是一样的,只是换了一个设备上下文而已,那么为什么输出到打印机的图像特别小呢?这与VC采用的缺省的坐标映射方式MM_TEXT有关,这种方式的好处是用户图形坐标和设备的像素完全一致。但是在屏幕的像素大小为800*600时,每逻辑英寸包含的屏幕像素为96,而打印机的点数却要多好几倍,如当打印机为HP LaserJet 6L时每逻辑英寸包含的打印机点数为600,也就是说打印机的清晰度比屏幕要高得多。这样的后果就是在屏幕上显示出来的满屏图像在打印出来的纸上却只有一点点大,怎么解决这个问题呢?一种简单的方法就是转换坐标映射方式,使得打印时采用的坐标比例比显示时采用的坐标比例相应地大若干倍,就可以解决这一问题。下面将给出详细的方法。 ----注意到CView类在进行显示和打印之前都会调用virtual void OnPrepareDC( CDC* pDC, CPrintInfo* pInfo = NULL )这个虚拟成员函数来准备设备上下文,我们可以在CView类中重载这个虚拟成员函数,进行坐标转换。首先用VC的 ClassWizard实现对OnPrepareDC( CDC* pDC, CPrintInfo* pInfo = NULL )函数的重载, ClassWizard生成的源代码如下: void CTempView::OnPrepareDC
    (CDC* pDC,  CPrintInfo* pInfo) 
        {
    // TODO: Add your specialized code here and
      /or call the base class
        CView::OnPrepareDC(pDC, pInfo);
       }
    ----我们只需在源代码中加入以下几行代码即可,如下:    void CPrintSameView::OnPrepareDC
       (CDC* pDC, CPrintInfo* pInfo)    {
        CView::OnPrepareDC(pDC, pInfo);
    pDC->SetMapMode(MM_ANISOTROPIC);
    //转换坐标映射方式
        CSize size = CSize(800, 560);
        pDC->SetWindowExt(size);      //确定窗口大小
        //得到实际设备每逻辑英寸的像素数量
        int xLogPixPerInch = pDC->GetDeviceCaps(LOGPIXELSX); 
        int yLogPixPerInch = pDC->GetDeviceCaps(LOGPIXELSY);
        //得到设备坐标和逻辑坐标的比例
        long xExt = (long)size.cx * xLogPixPerInch/96 ;
        long yExt = (long)size.cy * yLogPixPerInch/96 ;
    pDC->SetViewportExt((int)xExt, (int)yExt);
    //确定视口大小
    }
    ----如上所示,首先将坐标映射方式改变为 MM_ANISOTROPIC方式,即各向异性的意思,在这种坐标方式下,X轴和Y轴的逻辑单位可以进行任意的缩放。改变坐标映射方式后,就要确定窗口大小和视口大小,注意窗口大小就是我们在屏幕上所见的尺寸,而视口大小则是实际设备,如打印机等,和显示器设备每逻辑英寸的像素数量比较所得的比例尺寸。通过函数得到显示器和打印机每逻辑英寸的像素数量,然后对视口大小进行相应的缩放,就可以使得屏幕上的显示和打印机的输出是一致的了。 ---- 这样,只通过几行简单的代码,我们就实现了所见即所得的打印。