|
|||||||||||||||||||||
|
|||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
IntroductionIt seems from many of my other articles that I have become the "Print" guru here at CodeProject. So I though I would gather together a list of printing Tricks and Tips that I have collected together over a period of time through trial and error.
Your printing code should not make assumptions about where it should print on the page,
and make proper use of the pDC->TextOut(pInfo->m_rectDraw.left, pInfo->m_rectDraw.top, "Only draw inside the reported m_rectDraw area") ; When CDC dc ; AfxGetApp()->CreatePrinterDC(dc) ; ... // when finished with the DC, you should delete it dc.DeleteDC() ; This will create a printer DC for the default printer selected for you application. To switch to a different printer in code you should see my article Setting the default printer programmatically in an MFC application The printable area of a page on a printer is normally contained in the pInfo->m_rectDraw.SetRect(0, 0, pDC->GetDeviceCaps(HORZRES), pDC->GetDeviceCaps(VERTRES)) ; This gets the printable area of a printers page. In many cases you may want to have a user programmable margin around a page so that you do not over-print company logo's etc on
headed paper, so you can set a user programmable range for you margins in 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)) ; You will need to apply these changes to the When printing, choosing a font size that is suitable for the resolution of the printer in the past has been a hit and miss affair. I have had code that worked correctly on my development PC/printer setup, only to die horribly on a users PC/printer in Japan (e.g. the text generated was 1 pixel in height). Getting consistent output across printers can be done by selecting the font size based on the resolution reported by the printer: 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.... We set the CPrintInfo::m_bContinuePrintingIf, when printing your document, you did not know how many pages you were going to print until you actually printed
(as calculating the actual page usage can be difficult), you can set the MFC print architecture to continue to request
pages to print until you have finished with all your output. To do this, you should not sent a maximum page in your
There are 2 places where you can choose to end the printing: 1: In your 2: At the end of your pInfo->m_bContinuePrinting = FALSE ;
DIB's instead of DDB'sWhen printing bitmaps or icons to a printer DC, you should use a Here are some helpful functions that I have acquired from the web. I am not the original author of these, but I forget just where I got them from. But they are free source! // 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; } To use the above function(s) as an example code may be: if (iImage >= 0) { HANDLE hDib ; hDib = ImageToDIB(ℑ_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) ; } Updates
I will update this document with additional tips as I acquire them, so if you have any of your own, please send them in! Enjoy! | ||||||||||||||||||||