65.9K
CodeProject 正在变化。 阅读更多。
Home

从实战中获取的打印技巧和窍门

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.75/5 (24投票s)

2002年6月21日

CPOL

4分钟阅读

viewsIcon

280886

精选的打印技巧和窍门,可帮助简化MFC应用程序中的打印操作

引言

从我的许多其他文章来看,我似乎成了CodeProject上的“打印”大师。因此,我想收集一些我通过反复试验而积累的打印技巧和窍门。

  • 仅在CPrintInfo::m_rectDraw区域内打印
  • 您的打印代码不应假设应在页面上的哪个位置打印,而应正确使用CPrintInfo::m_rectDraw变量。这可以确保您不会覆盖可能在主OnPrint过程中之外打印的页边距/页眉/页脚。

    pDC->TextOut(pInfo->m_rectDraw.left, pInfo->m_rectDraw.top, 
                 "Only draw inside the reported m_rectDraw area") ;
  • 在OnPreparePrinting()中获取PrinterDC
  • 当在CView派生类中调用OnPreparePrinting()时,通常需要设置打印文档所需的输出页数,除非您使用的是CPrintInfo::m_bContinuePrinting方法。但是,如果没有有关打印机分辨率或页面大小的信息,这可能很困难。因此,此时需要获取将要使用的打印机DC对象。由于MFC打印体系结构在调用OnBeginPrinting()函数之前不会创建它,因此您必须自己创建一个,并在计算所需的页数后将其释放。要创建这样的打印机DC,可以使用此代码

    CDC dc ;
    AfxGetApp()->CreatePrinterDC(dc) ;
        
    ...
    
    // when finished with the DC, you should delete it
    dc.DeleteDC() ;

    这将为应用程序选择的默认打印机创建打印机DC。要在代码中切换到其他打印机,请参阅我的文章在MFC应用程序中以编程方式设置默认打印机

  • 获取可打印页面区域的大小
  • 打印机上页面的可打印区域通常包含在CPrintInfo::m_rectDraw成员变量中。CPrintInfo对象会传递到您重写的CView虚拟函数中。但在某些情况下,例如在OnPreparePrinting()OnBeginPrinting()中,此成员变量尚未初始化。因此,您必须自己进行初始化。

    pInfo->m_rectDraw.SetRect(0, 0, 
                              pDC->GetDeviceCaps(HORZRES), 
                              pDC->GetDeviceCaps(VERTRES)) ;

    这会获取打印机页面可打印区域的大小。

  • 边距
  • 在许多情况下,您可能希望在页面周围设置用户可编程的边距,以免在信头纸上覆盖公司徽标等,因此您可以设置一个用户可编程的边距范围(以英寸为单位)。然后,您可以将其转换为设备单位,并通过更改CPrintInfo::m_rectDraw变量的尺寸来保留页面上的空间。例如

    double LeftOffset = 0.5 ;       // in imperial inches!
    double TopOffset = 0.5 ;        // in imperial inches!
    double RightOffset = 0.5 ;      // in imperial inches!
    double BottomOffset = 0.5 ;     // in imperial inches!
    pInfo->m_rectDraw.DeflateRect(
        (int)(pDC->GetDeviceCaps(LOGPIXELSX) * LeftOffset),
        (int)(pDC->GetDeviceCaps(LOGPIXELSY) * TopOffset),
        (int)(pDC->GetDeviceCaps(LOGPIXELSX) * RightOffset),
        (int)(pDC->GetDeviceCaps(LOGPIXELSY) * BottomOffset)) ;

    您需要将这些更改应用于m_rectDraw变量以打印的每一页,因为在MFC库代码的每个页面循环中都会重置该矩形。

  • 选择合适的打印字体大小
  • 在打印时,过去选择适合打印机分辨率的字体大小一直是一种碰运气的事情。我编写的代码在我自己的开发PC/打印机设置上能正常工作,但到了用户在日本的PC/打印机上就彻底失败(例如,生成的文本高度只有1像素)。通过根据打印机报告的分辨率选择字体大小,可以实现跨打印机的一致输出。

    CFont    font ;
    LOGFONT  lf ;
    
    ::ZeroMemory(&lf, sizeof(LOGFONT));
    
    // This aims to get a 12-point size font regardless of the 
    // printer resolution
    lf.lfHeight = -MulDiv(12, pDC->GetDeviceCaps(LOGPIXELSY), 72);
    strcpy(lf.lfFaceName, "Arial");    //    with face name "Arial".
    // make use of the font....

    我们将LOGFONT::lfHeight成员设置为负值,这样Windows就会为我们选择一个合适的宽度,从而得到一种漂亮的比例字体。

  • 如果您不知道要打印多少页,请使用CPrintInfo::m_bContinuePrinting
  • 如果在打印文档时,直到实际打印时才知道要打印多少页(因为计算实际页面使用量可能很困难),您可以设置MFC打印体系结构以继续请求打印页面,直到您完成所有输出为止。要做到这一点,您不应在CView::OnPreparePrinting()函数中设置最大页数。

    您可以在以下两个位置选择结束打印:

    1:在您的CView::OnPrepareDC()覆盖中

    2:在您的CView::OnPrint()函数的末尾,当您已打印完所有输出后

    pInfo->m_bContinuePrinting = FALSE ;
  • 使用DIB而不是DDB
  • 在将位图或图标打印到打印机DC时,应使用DIB(设备无关位图)而不是DDB(设备相关位图)。这是因为打印机设备驱动程序通常不支持BitBlt。您可能会花费大量时间思考为什么位图会在打印预览中显示(因为屏幕DC支持BitBlt),但在打印输出中却不显示(因为打印机驱动程序不支持)。因此,在打印时,请将图像转换为DIB并使用StretchDIBBits打印图像。我还没有遇到过这种技术无效的打印机。

    以下是一些我从网上获得的有用函数。我不是这些函数的原始作者,但我记不清是从哪里得到的了。但它们是免费的!

    // this procedure extracts a single image from an image list into a DIB
    HANDLE ImageToDIB( CImageList* pImageList, int iImageNumber, CWnd* pWnd)
    {
        // Local Variables
        CBitmap     bitmap;
        CWindowDC    dc( pWnd );
    
        CDC         memDC;
        CRect        rect;
        CPalette    pal;
        IMAGEINFO   imageInfo;
    
        if (!pImageList->GetImageInfo( iImageNumber, &imageInfo ))
            {
            // Getting of the Imageinfos failed
            return NULL;
            }
    
        // Create compatible stuff and select Bitmap
        if (!memDC.CreateCompatibleDC(&dc ))
            {
            // Create failed
            return NULL;
            }
    
        if (!bitmap.CreateCompatibleBitmap(&dc, 
                                            imageInfo.rcImage.bottom-imageInfo.rcImage.top, 
                                            imageInfo.rcImage.right-imageInfo.rcImage.left))
            {
            // Create failed
            memDC.DeleteDC() ;
            return NULL;
            }
    
        CBitmap* pOldBitmap = memDC.SelectObject( &bitmap );
        if( NULL == pOldBitmap )
            {
            // Select failed
            memDC.DeleteDC() ;
            return NULL;
            }
    
        // Local Variables for Draw
        CPoint point( 0, 0);
        UINT nStyle = ILD_NORMAL;
    
        // Draw Image to the compatible DC
        if(!pImageList->Draw( &memDC, iImageNumber, point, nStyle ))
            {
            // Drawing of the Image failed
            memDC.SelectObject(pOldBitmap) ;
            VERIFY(bitmap.DeleteObject()) ;
            memDC.DeleteDC() ;
            return NULL;
            }
    
        // Create logical palette if device support a palette
        if( dc.GetDeviceCaps( RASTERCAPS ) & RC_PALETTE )
            {
            UINT        nSize   = sizeof(LOGPALETTE) + ( sizeof(PALETTEENTRY) * 256 );
            LOGPALETTE* pLP     = (LOGPALETTE*)new BYTE[nSize];
            pLP->palVersion     = 0x300;
            pLP->palNumEntries = (unsigned short)GetSystemPaletteEntries( dc, 0, 255, 
            pLP->palPalEntry );
    
            // Create the palette
            pal.CreatePalette( pLP );
    
            // Free memory
            delete[] pLP;
            }
    
        memDC.SelectObject( pOldBitmap );
        memDC.DeleteDC() ;
    
        // Convert the bitmap to a DIB
        HANDLE h = DDBToDIB(bitmap, BI_RGB, &pal );
        VERIFY(bitmap.DeleteObject()) ;
        return h ;
    }
    
    
    // DDBToDIB        - Creates a DIB from a DDB
    // bitmap        - Device dependent bitmap
    // dwCompression    - Type of compression - see BITMAPINFOHEADER
    // pPal            - Logical palette
    HANDLE DDBToDIB( CBitmap& bitmap, DWORD dwCompression, CPalette* pPal ) 
    {
        BITMAP            bm;
        BITMAPINFOHEADER    bi;
        LPBITMAPINFOHEADER     lpbi;
        DWORD            dwLen;
        HANDLE            hDIB;
        HANDLE            handle;
        HDC             hDC;
        HPALETTE        hPal;
    
    
        ASSERT( bitmap.GetSafeHandle() );
    
        // The function has no arg for bitfields
        if( dwCompression == BI_BITFIELDS )
            return NULL;
    
        // If a palette has not been supplied use defaul palette
        hPal = (HPALETTE) pPal->GetSafeHandle();
        if (hPal==NULL)
            hPal = (HPALETTE) GetStockObject(DEFAULT_PALETTE);
    
        // Get bitmap information
        bitmap.GetObject(sizeof(bm),(LPSTR)&bm);
    
        // Initialize the bitmapinfoheader
        bi.biSize        = sizeof(BITMAPINFOHEADER);
        bi.biWidth        = bm.bmWidth;
        bi.biHeight         = bm.bmHeight;
        bi.biPlanes         = 1;
        bi.biBitCount        = (unsigned short)(bm.bmPlanes * bm.bmBitsPixel) ;
        bi.biCompression    = dwCompression;
        bi.biSizeImage        = 0;
        bi.biXPelsPerMeter    = 0;
        bi.biYPelsPerMeter    = 0;
        bi.biClrUsed        = 0;
        bi.biClrImportant    = 0;
    
        // Compute the size of the  infoheader and the color table
        int nColors = 0;
        if(bi.biBitCount <= 8)
            {
            nColors = (1 << bi.biBitCount);
            }
        dwLen  = bi.biSize + nColors * sizeof(RGBQUAD);
    
        // We need a device context to get the DIB from
        hDC = ::GetDC(NULL);
        hPal = SelectPalette(hDC,hPal,FALSE);
        RealizePalette(hDC);
    
        // Allocate enough memory to hold bitmapinfoheader and color table
        hDIB = GlobalAlloc(GMEM_FIXED,dwLen);
    
        if (!hDIB){
            SelectPalette(hDC,hPal,FALSE);
            ::ReleaseDC(NULL,hDC);
            return NULL;
        }
    
        lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDIB);
    
        *lpbi = bi;
    
        // Call GetDIBits with a NULL lpBits param, so the device driver 
        // will calculate the biSizeImage field 
        GetDIBits(hDC, (HBITMAP)bitmap.GetSafeHandle(), 0L, (DWORD)bi.biHeight,
                (LPBYTE)NULL, (LPBITMAPINFO)lpbi, (DWORD)DIB_RGB_COLORS);
    
        bi = *lpbi;
    
        // If the driver did not fill in the biSizeImage field, then compute it
        // Each scan line of the image is aligned on a DWORD (32bit) boundary
        if (bi.biSizeImage == 0){
            bi.biSizeImage = ((((bi.biWidth * bi.biBitCount) + 31) & ~31) / 8) 
                            * bi.biHeight;
    
            // If a compression scheme is used the result may infact be larger
            // Increase the size to account for this.
            if (dwCompression != BI_RGB)
                bi.biSizeImage = (bi.biSizeImage * 3) / 2;
        }
    
        // Realloc the buffer so that it can hold all the bits
        dwLen += bi.biSizeImage;
        handle = GlobalReAlloc(hDIB, dwLen, GMEM_MOVEABLE) ;
        if (handle != NULL)
            hDIB = handle;
        else
            {
            GlobalFree(hDIB);
    
            // Reselect the original palette
            SelectPalette(hDC,hPal,FALSE);
            ::ReleaseDC(NULL,hDC);
            return NULL;
            }
    
        // Get the bitmap bits
        lpbi = (LPBITMAPINFOHEADER)hDIB;
    
        // FINALLY get the DIB
        BOOL bGotBits = GetDIBits( hDC, (HBITMAP)bitmap.GetSafeHandle(),
                    0L,                      // Start scan line
                    (DWORD)bi.biHeight,      // # of scan lines
                    (LPBYTE)lpbi             // address for bitmap bits
                    + (bi.biSize + nColors * sizeof(RGBQUAD)),
                    (LPBITMAPINFO)lpbi,      // address of bitmapinfo
                    (DWORD)DIB_RGB_COLORS);  // Use RGB for color table
    
        if( !bGotBits )
        {
            GlobalFree(hDIB);
            
            SelectPalette(hDC,hPal,FALSE);
            ::ReleaseDC(NULL,hDC);
            return NULL;
        }
    
        SelectPalette(hDC,hPal,FALSE);
        ::ReleaseDC(NULL,hDC);
        return hDIB;
    }

    使用上述函数作为示例代码可以是

    if (iImage >= 0)
        {
        HANDLE hDib ;
        hDib = ImageToDIB(&#8465;_list, iImage, this) ; // this is a dialog window in this example
        BITMAPINFOHEADER    *pBMI ;
        pBMI = (BITMAPINFOHEADER*)GlobalLock(hDib) ;
        int nColors = 0;
        if (pBMI->biBitCount <= 8)
            {
            nColors = (1 << pBMI->biBitCount);
            }
        // print the correct image
        ::StretchDIBits(dc.m_hDC,
                            pInfo.m_rectDraw.left, 
                            pInfo.m_rectDraw.top + cs.cy * j, 
                            cs.cy, 
                            cs.cy,
                            0, 
                            0, 
                            pBMI->biWidth,
                            pBMI->biHeight,
                            (LPBYTE)pBMI + (pBMI->biSize + nColors * sizeof(RGBQUAD)),
                            (BITMAPINFO*)pBMI,
                            DIB_RGB_COLORS, 
                            SRCCOPY);
        // free resources
        GlobalUnlock(hDib) ;
        GlobalFree(hDib) ;
        }

更新

  • 2002年7月25日 - 添加了其他技巧,并对文档进行了一些改写。

我会在我获得更多技巧后更新本文档,如果您有自己的技巧,请发送给我!

尽情享用!

© . All rights reserved.