/* * print.c * * Source file for Device-Independent Bitmap (DIB) API. Provides * the following functions: * * PrintWindow() - Prints all or part of a window * PrintScreen() - Prints the entire screen * PrintDIB() - Prints the specified DIB * * Development Team: Mark Bader * Patrick Schreiber * Garrett McAuliffe * Eric Flo * Tony Claflin * * Written by Microsoft Product Support Services, Developer Support. * Copyright (c) 1991 Microsoft Corporation. All rights reserved. */ #include #include #include "dibapi.h" // Header for DIB functions #include "dibutil.h" // Auxilirary functions #include "dialogs.h" // Header for "Now Printing" dialog box #include "errors.h" // Contains error numbers extern HANDLE ghInst; // Global handle to instance of main window /*************************************************************** * Typedefs **************************************************************/ /* Structure used for Banding */ typedef struct { BOOL bGraphics; BOOL bText; RECT GraphicsRect; } BANDINFOSTRUCT; /**************************************************************** * Variables ***************************************************************/ HWND hDlgAbort; // Handle to Abort Dialog char szPrintDlg[] = "Printing"; // Name of Print dialog from .RC BOOL bAbort = FALSE; // Abort a print operation? char gszDevice[50]; // Keeps track out device (e.g. "HP LaserJet") char gszOutput[50]; // Output device (e.g. "LPT1:") /*************************************************************** * Function prototypes for functions local to this module **************************************************************/ BOOL FAR PASCAL PrintAbortProc(HDC, short); int FAR PASCAL PrintAbortDlg(HWND, unsigned, WORD, LONG); WORD PrintBand(HDC, LPRECT, LPRECT, BOOL, BOOL, LPBITMAPINFOHEADER, LPSTR); HDC GetPrinterDC(void); void CalculatePrintRect(HDC, LPRECT, WORD, DWORD, DWORD); /********************************************************************** * * PrintWindow() * * * Description: * * This function prints the specified window on the default * printer. * * Parameters: * * HWND hWnd - Specifies the window to print. The window must * not be iconic and must be topmost on the display. * * WORD fPrintArea - Specifies the area of the window to print. Must be * one of PW_ALL, PW_CLIENT, PW_CAPTION, or PW_MENUBAR * * WORD fPrintOpt - Print options (one of PW_BESTFIT, PW_STRETCHTOPAGE, or * PW_SCALE) * * WORD wXScale, wYScale - X and Y scaling factors if PW_SCALE is specified * * LPSTR szJobName - Name that you would like to give to this print job (this * name shows up in the Print Manager as well as the * "Now Printing..." dialog box). * Return Value: * ERR_DIBFUNCTION or any return value from PrintDIB * **********************************************************************/ WORD PrintWindow(HWND hWnd, // Window to be printed WORD fPrintArea, // Area of window to be printed WORD fPrintOpt, // Print options WORD wXScale, // X Scaling factor if PW_SCALE is used WORD wYScale, // Y Scaling factor if PW_SCALE is used LPSTR szJobName) // Name of print job { HDIB hDib; // Handle to the DIB WORD wReturn; // our return value /* * Parameter validation */ if (!hWnd) return (ERR_INVALIDHANDLE); // Invalid Window /* * Copy the Window to a DIB and print it. */ hDib = CopyWindowToDIB(hWnd, fPrintArea); if (!hDib) return (ERR_DIBFUNCTION); // CopyWindowToDIB failed! wReturn = PrintDIB(hDib, fPrintOpt, wXScale, wYScale, szJobName); /* * Call DestroyDIB to free the memory the dib takes up. */ DestroyDIB(hDib); return wReturn; // return the value from PrintDIB } /********************************************************************** * * PrintScreen() * * * Description: * * This function prints the specified portion of the display screen on the * default printer using the print options specified. The print * options are listed in dibapi.h. * * Parameters: * * LPRECT rRegion - Specifies the region of the screen (in screen * coordinates) to print * * WORD fPrintOpt - Print options (PW_BESTFIT, PW_STRETCHTOPAGE, or PW_SCALE) * * WORD wXScale, wYScale - X and Y scaling factors if PW_SCALE is specified * * LPSTR szJobName - Name that you would like to give to this print job (this * name shows up in the Print Manager as well as the * "Now Printing..." dialog box). * * Return Value: * ERR_DIBFUNCTION or any return value from PrintDIB * **********************************************************************/ WORD PrintScreen(LPRECT rRegion, // Region to print (in screen coords) WORD fPrintOpt, // print options WORD wXScale, // X scaling (used if PW_SCALE specified) WORD wYScale, // Y scaling (used if PW_SCALE specified) LPSTR szJobName) // Name of print job { HDIB hDib; // A Handle to our DIB WORD wReturn; // Return value /* * Copy the screen contained in the specified rectangle to a DIB */ hDib = CopyScreenToDIB(rRegion); if (!hDib) return (ERR_DIBFUNCTION); // CopyScreenToDIB failed! wReturn = PrintDIB(hDib, fPrintOpt, wXScale, wYScale, szJobName); DestroyDIB(hDib); return wReturn; // Return the value that PrintDIB returned } /********************************************************************** * * PrintDIB() * * Description: * * This routine prints the specified DIB. The actual printing is done * in the PrintBand() routine (see below), this procedure drives the * printing operation. PrintDIB() has the code to handle both banding * and non-banding printers. A banding printer can be distinguished by * the GetDeviceCaps() API (see the code below). On banding devices, * must repeatedly call the NEXTBAND escape to get the next banding * rectangle to print into. If the device supports the BANDINFO escape, * it should be used to determine whether the band "wants" text or * graphics (or both). On non-banding devices, we can ignore all this * and call PrintBand() on the entire page. * * Parameters: * * HDIB hDib - Handle to dib to be printed * * WORD fPrintOpt - tells which print option to use (PW_BESTFIT, * PW_STRETCHTOPAGE, OR PW_SCALE) * * WORD wXScale, wYScale - X and Y scaling factors (integers) for * printed output if the PW_SCALE option is used. * * LPSTR szJobName - Name that you would like to give to this print job (this * name shows up in the Print Manager as well as the * "Now Printing..." dialog box). * * Return Value: (see errors.h for description) * * One of: ERR_INVALIDHANDLE * ERR_LOCK * ERR_SETABORTPROC * ERR_STARTDOC * ERR_NEWFRAME * ERR_ENDDOC * ERR_GETDC * ERR_STRETCHDIBITS * * ********************************************************************/ WORD PrintDIB(HDIB hDib, // Handle to the DIB WORD fPrintOpt, // Print Options WORD wXScale, // X Scaling factor WORD wYScale, // Y Scaling factor LPSTR szJobName) // Name of print job { HDC hPrnDC; // DC to the printer RECT rect; // Rect structure used for banding BANDINFOSTRUCT biBandInfo; // Used for banding static FARPROC lpAbortProc; // ProcInstance to the Abort Proc static FARPROC lpAbortDlg; // ProcInstance to the Dialog Box Procedure int nTemp; // Temp number used to check banding capability LPSTR lpBits; // pointer to the DIB bits LPBITMAPINFOHEADER lpDIBHdr; // Pointer to DIB header int nBandCount = 0; // used for print dialog box to count bands WORD wErrorCode = 0; // Error code to return RECT rPrintRect; // Rect which specifies the area on the printer // (in printer coordinates) which we // want the DIB to go to char szBuffer[70]; // Buffer to hold message for "Printing" dlg box char szJobNameTrunc[35]; // szJobName truncated to 31 characters, since // STARTDOC can't accept a string longer than 31 /* * Paramter validation */ if (!hDib) return (ERR_INVALIDHANDLE); /* * Get pointer to DIB header */ lpDIBHdr = (LPBITMAPINFOHEADER)GlobalLock(hDib); if (!lpDIBHdr) // Check that we have a valid pointer return (ERR_LOCK); lpBits = FindDIBBits((LPSTR)lpDIBHdr); // Find pointer to DIB bits if (hPrnDC = GetPrinterDC()) { SetStretchBltMode(hPrnDC, COLORONCOLOR); /* * Determine rPrintRect (printer area to print to) from the * fPrintOpt. Fill in rPrintRect.left and .top from wXScale and * wYScale just in case we use PW_SCALE (see the function * CalculatePrintRect). */ rPrintRect.left = wXScale; rPrintRect.top = wYScale; CalculatePrintRect(hPrnDC, &rPrintRect, fPrintOpt, lpDIBHdr->biWidth, lpDIBHdr->biHeight); /* * Initialize the abort procedure. */ lpAbortProc = MakeProcInstance(PrintAbortProc, ghInst); lpAbortDlg = MakeProcInstance(PrintAbortDlg, ghInst); hDlgAbort = CreateDialog(ghInst, szPrintDlg, GetFocus(), lpAbortDlg); /* * Set the text inside the dialog to the name of our print job */ lstrcpy(szJobNameTrunc, szJobName); szJobNameTrunc[31] = '\0'; // Truncate string to 31 chars wsprintf(szBuffer, "Printing '%s'", (LPSTR)szJobNameTrunc); SetDlgItemText(hDlgAbort, IDC_PRINTTEXT1, (LPSTR)szBuffer); /* * Set global variable bAbort to FALSE. This will get set to TRUE * in our PrintAbortDlg() proceudre if the user selects the * CANCEL button in our dialog box */ bAbort = FALSE; /* * Call the Escape() which will set up the Abort Procedure */ if (Escape(hPrnDC, SETABORTPROC, NULL, (LPSTR)(FARPROC)lpAbortProc, NULL ) < 0) return (ERR_SETABORTPROC); /* * Call Escape() with STARTDOC -- starts print job */ if (Escape(hPrnDC, STARTDOC, lstrlen((LPSTR)szJobNameTrunc), (LPSTR) szJobNameTrunc, NULL) < 0) { // Oops, something happened, let's clean up here and return DestroyWindow(hDlgAbort); // Remove abort dialog box FreeProcInstance(lpAbortProc); FreeProcInstance(lpAbortDlg); DeleteDC(hPrnDC); GlobalUnlock(hDib); return (ERR_STARTDOC); } /* * Fill in initial values for our BandInfo Structure to * tell driver we can want to do graphics and text, and * also which area we want the graphics to go in. */ biBandInfo.bGraphics = TRUE; biBandInfo.bText = TRUE; biBandInfo.GraphicsRect = rPrintRect; /* * Check if need to do banding. If we do, loop through * each band in the page, calling NEXTBAND and BANDINFO * (if supported) calling PrintBand() on the band. Else, * call PrintBand() with the entire page as our clipping * rectangle! */ nTemp = NEXTBAND; if (Escape(hPrnDC, QUERYESCSUPPORT, sizeof(int), (LPSTR)&nTemp, NULL)) { BOOL bBandInfoDevice; /* * Check if device supports the BANDINFO escape. */ nTemp = BANDINFO; bBandInfoDevice = Escape(hPrnDC, QUERYESCSUPPORT, sizeof(int), (LPSTR )&nTemp, NULL); /* * Do each band -- Call Escape() with NEXTBAND, then the * rect structure returned is the area where we are to * print in. This loop exits when the rect area is empty. */ while (Escape(hPrnDC, NEXTBAND, NULL, NULL, (LPSTR)&rect) && ! IsRectEmpty(&rect)) { char szTmpBuf[100]; /* * Do the BANDINFO, if needed. */ if (bBandInfoDevice) Escape(hPrnDC, BANDINFO, sizeof(BANDINFOSTRUCT), (LPSTR)& biBandInfo, (LPSTR)&biBandInfo); wsprintf(szTmpBuf, "Printing Band Number %d", ++nBandCount); SetDlgItemText(hDlgAbort, IDC_PERCENTAGE, (LPSTR)szTmpBuf); /* * Call PrintBand() to do actual output into band. * Pass in our band-info flags to tell what sort * of data to output into the band. Note that on * non-banding devices, we pass in the default bandinfo * stuff set above (i.e. bText=TRUE, bGraphics=TRUE). */ wErrorCode = PrintBand(hPrnDC, &rPrintRect, &rect, biBandInfo.bText, biBandInfo.bGraphics, lpDIBHdr, lpBits); } } else { /* * Print the whole page -- non-banding device. */ rect = rPrintRect; SetDlgItemText(hDlgAbort, IDC_PERCENTAGE, (LPSTR) "Sending bitmap to printer..."); wErrorCode = PrintBand(hPrnDC, &rPrintRect, &rect, TRUE, TRUE, lpDIBHdr, lpBits); /* * Non-banding devices need a NEWFRAME */ if (Escape(hPrnDC, NEWFRAME, NULL, NULL, NULL) < 0) return (ERR_NEWFRAME); } /* * End the print operation. Only send the ENDDOC if * we didn't abort or error. */ if (!bAbort) { if (Escape(hPrnDC, ENDDOC, NULL, NULL, NULL) < 0) { /* * We errored out on ENDDOC, but don't return here - we still * need to close the dialog box, free proc instances, etc. */ wErrorCode = ERR_ENDDOC; } DestroyWindow(hDlgAbort); } /* * All done, clean up. */ FreeProcInstance(lpAbortProc); FreeProcInstance(lpAbortDlg); DeleteDC(hPrnDC); } else wErrorCode = ERR_GETDC; // Couldn't get Printer DC! GlobalUnlock(hDib); return (wErrorCode); } // ******************************************************************* // Auxilirary Functions // -- Local to this module only // ******************************************************************* /********************************************************************* * * CalculatePrintRect() * * Given fPrintOpt and a size of the DIB, return the area on the * printer where the image should go (in printer coordinates). If * fPrintOpt is PW_SCALE, then lpPrintRect.left and .top should * contain WORDs which specify the scaling factor for the X and * Y directions, respecively. * ********************************************************************/ void CalculatePrintRect(HDC hDC, // HDC to printer DC LPRECT lpPrintRect, // Returned PrintRect WORD fPrintOpt, // Options DWORD cxDIB, // Size of DIB - x DWORD cyDIB) // Size of DIB - y { int cxPage, cyPage, cxInch, cyInch; if (!hDC) return; /* * Get some info from printer driver */ cxPage = GetDeviceCaps(hDC, HORZRES); // Width of printr page - pixels cyPage = GetDeviceCaps(hDC, VERTRES); // Height of printr page - pixels cxInch = GetDeviceCaps(hDC, LOGPIXELSX); // Printer pixels per inch - X cyInch = GetDeviceCaps(hDC, LOGPIXELSY); // Printer pixels per inch - Y switch (fPrintOpt) { /* * Best Fit case -- create a rectangle which preserves * the DIB's aspect ratio, and fills the page horizontally. * * The formula in the "->bottom" field below calculates the Y * position of the printed bitmap, based on the size of the * bitmap, the width of the page, and the relative size of * a printed pixel (cyInch / cxInch). */ case PW_BESTFIT: lpPrintRect->top = 0; lpPrintRect->left = 0; lpPrintRect->bottom = (int)(((double)cyDIB * cxPage * cyInch) / ((double )cxDIB * cxInch)); lpPrintRect->right = cxPage; break; /* * Scaling option -- lpPrintRect's top/left contain * multipliers to multiply the DIB's height/width by. */ case PW_SCALE: { int cxMult, cyMult; cxMult = lpPrintRect->left; cyMult = lpPrintRect->top; lpPrintRect->top = 0; lpPrintRect->left = 0; lpPrintRect->bottom = (int)(cyDIB * cyMult); lpPrintRect->right = (int)(cxDIB * cxMult); } break; /* * Stretch To Page case -- create a rectangle * which covers the entire printing page (note that this * is also the default). */ case PW_STRETCHTOPAGE: default: lpPrintRect->top = 0; lpPrintRect->left = 0; lpPrintRect->bottom = cyPage; lpPrintRect->right = cxPage; break; } } /********************************************************************* * * PrintBand() * * This routine does ALL output to the printer. It is called from * the PrintDIB() routine. It is called for both banding and non- * banding printing devices. lpRectClip contains the rectangular * area we should do our output into (i.e. we should clip our output * to this area). The flags fDoText and fDoGraphics should be set * appropriately (if we want any text output to the rectangle, set * fDoText to true). Normally these flags are returned on banding * devices which support the BANDINFO escape. * ********************************************************************/ WORD PrintBand(HDC hDC, // Handle to the Printer DC LPRECT lpRectOut, // Rect where entire DIB is to go LPRECT lpRectClip, // Clippping rect where this portion goes BOOL fDoText, // TRUE if this band is for text BOOL fDoGraphics, // TRUE if this band is for graphics LPBITMAPINFOHEADER lpDIBHdr, // Pointer to DIB header LPSTR lpDIBBits) // Pointer to DIB bits { RECT rect; // Temporary rectangle double dblXScaling, // X and Y scaling factors dblYScaling; WORD wReturn = 0; // Return code if (fDoGraphics) { dblXScaling = ((double)lpRectOut->right - lpRectOut->left) / (double) lpDIBHdr->biWidth; dblYScaling = ((double)lpRectOut->bottom - lpRectOut->top) / (double) lpDIBHdr->biHeight; /* * Now we set up a temporary rectangle -- this rectangle * holds the coordinates on the paper where our bitmap * WILL be output. We can intersect this rectangle with * the lpClipRect to see what we NEED to output to this * band. Then, we determine the coordinates in the DIB * to which this rectangle corresponds (using dbl?Scaling). */ IntersectRect(&rect, lpRectOut, lpRectClip); if (!IsRectEmpty(&rect)) { RECT rectIn; rectIn.left = (int)((rect.left - lpRectOut->left) / dblXScaling + 0.5 ); rectIn.top = (int)((rect.top - lpRectOut->top) / dblYScaling + 0.5); rectIn.right = (int)(rectIn.left + (rect.right - rect.left) / dblXScaling + 0.5); rectIn.bottom = (int)(rectIn.top + (rect.bottom - rect.top) / dblYScaling + 0.5); if (!StretchDIBits(hDC, // DestDC rect.left, // DestX rect.top, // DestY rect.right - rect.left, // DestWidth rect.bottom - rect.top, // DestHeight rectIn.left, // SrcX (int)(lpDIBHdr->biHeight) - // SrcY rectIn.top - (rectIn.bottom - rectIn.top), rectIn.right - rectIn.left, // SrcWidth rectIn.bottom - rectIn.top, // SrcHeight lpDIBBits, // lpBits (LPBITMAPINFO)lpDIBHdr, // lpBitInfo DIB_RGB_COLORS, // wUsage SRCCOPY)) // dwROP wReturn = ERR_STRETCHDIBITS; // StretchDIBits() failed! } } return wReturn; } /*********************************************************************** * * GetPrinterDC() * * Return a DC to the currently selected printer. * Returns NULL on error. * ***********************************************************************/ HDC GetPrinterDC(void) { static char szPrinter[64]; char *szDevice, *szDriver, *szOutput; GetProfileString("windows", "device", "", szPrinter, 64); if ((szDevice = strtok(szPrinter, ",")) && (szDriver = strtok(NULL, ", ")) && (szOutput = strtok(NULL, ", "))) { lstrcpy((LPSTR)gszDevice, (LPSTR)szDevice); // Copy to global variables lstrcpy((LPSTR)gszOutput, (LPSTR)szOutput); return CreateDC(szDriver, szDevice, szOutput, NULL); } return NULL; } /********************************************************************** * PrintAbortProc() * * Abort procedure - contains the message loop while printing is * in progress. By using a PeekMessage() loop, multitasking * can occur during printing. * **********************************************************************/ BOOL FAR PASCAL PrintAbortProc(HDC hDC, short code) { MSG msg; while (!bAbort && PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) if (!IsDialogMessage(hDlgAbort, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } return (!bAbort); } /*********************************************************************** * * PrintAbortDlg() * * * This is the Dialog Procedure which will handle the "Now Printing" * dialog box. When the user presses the "Cancel" button, the * global variable bAbort is set to TRUE, which causes the * PrintAbortProc to exit, which in turn causes the printing * operation to terminate. * ***********************************************************************/ int FAR PASCAL PrintAbortDlg(HWND hWnd, /* Handle to dialog box */ unsigned msg, /* Message */ WORD wParam, LONG lParam) { switch (msg) { case WM_INITDIALOG: { char szBuffer[100]; /* * Fill in the text which specifies where this bitmap * is going ("on HP LaserJet on LPT1", for example) */ wsprintf(szBuffer, "on %s on %s", (LPSTR)gszDevice, (LPSTR)gszOutput); SetDlgItemText(hWnd, IDC_PRINTTEXT2, (LPSTR)szBuffer); SetFocus(GetDlgItem(hWnd, IDCANCEL)); } return TRUE; // Return TRUE because we called SetFocus() case WM_COMMAND: bAbort = TRUE; DestroyWindow(hWnd); return TRUE; break; } return FALSE; }