|
|
1.1 ! root 1: /*==============================================================*\ ! 2: * Prnt.c - routines for printing of a document ! 3: * Created 1990, Microsoft, IBM Corp. ! 4: *-------------------------------------------------------------- ! 5: * ! 6: * This module contains the routines for the printing of a ! 7: * a document. Included are the routines for calling the ! 8: * standard dialogs for obtaining the user's printer and ! 9: * page setup preferences. Also included is the routine to ! 10: * determine the number of pages in a document as well as the ! 11: * routine to obtain the printer DC and print the document. ! 12: * ! 13: *-------------------------------------------------------------- ! 14: * ! 15: * This source file contains the following functions: ! 16: * ! 17: * InitPrinting(); ! 18: * PageSetup(hwndOwner); ! 19: * PrintSetup(hwndOwner); ! 20: * Print(); ! 21: * CalcPrintArea(hpsPrinter, ! 22: * hdcPrinter, ! 23: * prclPrintArea, ! 24: * pfm); ! 25: * PrintDoc(hdc); ! 26: * QueryMaxPages(); ! 27: * ! 28: * ! 29: \*==============================================================*/ ! 30: ! 31: /*--------------------------------------------------------------*\ ! 32: * Include files, macros, defined constants, and externs ! 33: \*--------------------------------------------------------------*/ ! 34: ! 35: #define INCL_WINMLE ! 36: #define INCL_GPICONTROL ! 37: #define INCL_GPITRANSFORMS ! 38: #define INCL_GPIPRIMITIVES ! 39: #define INCL_GPILCIDS ! 40: #define INCL_DEV ! 41: ! 42: #include <os2.h> ! 43: #include "sty_main.h" ! 44: #include "sty_xtrn.h" ! 45: ! 46: #if 0 ! 47: #include "pmkitp.h" ! 48: #define PRINT_DLGS_ENABLED ! 49: #endif ! 50: ! 51: #define MAXHEADERLEN 100 /* maximum length of header */ ! 52: #define MAXFOOTERLEN 100 /* maximum length of footer */ ! 53: #define DLGTITLELEN 30 /* length of dialog title string */ ! 54: ! 55: #define DEVDATACOUNT 7L /* # fields of DEVOPENSTRUCT used */ ! 56: ! 57: #define FONTLCID 1L /* LCID for printer font */ ! 58: ! 59: #define QMP_ERROR -1 /* error return from QueryMaxPages() */ ! 60: ! 61: /* constants used in InitPrintingDlgs() for initial and maximum margins */ ! 62: #define ONEINCH (MAKEFIXED(1, 0)) ! 63: #define EIGHTINCHES (MAKEFIXED(8, 0)) ! 64: #define ELEVENINCHES (MAKEFIXED(11, 0)) ! 65: ! 66: /* Macro for converting a FIXED integer value into LO_ENGLISH ! 67: world coordinates */ ! 68: #define FIXEDTOWORLD(x) ((FIXEDINT(x) * 100L) + \ ! 69: ((FIXEDFRAC(x) * 100L) / 65536L)) ! 70: ! 71: ! 72: /*--------------------------------------------------------------*\ ! 73: * Global variables ! 74: \*--------------------------------------------------------------*/ ! 75: #ifdef PRINT_DLGS_ENABLED ! 76: ! 77: static PAGESETUPDLG pgsd; ! 78: static PRINTSETUPDLG psd; ! 79: static PRINTDLG prtd; ! 80: static CHAR szHeader[MAXHEADERLEN], szFooter[MAXFOOTERLEN]; ! 81: static CHAR szPgsdTitle[DLGTITLELEN]; ! 82: static CHAR szPsdtitle[DLGTITLELEN]; ! 83: static CHAR szPrtdTitle[DLGTITLELEN]; ! 84: ! 85: #endif ! 86: ! 87: /*--------------------------------------------------------------*\ ! 88: * Entry point declarations ! 89: \*--------------------------------------------------------------*/ ! 90: SHORT QueryMaxPages(VOID); ! 91: BOOL PrintDoc(HDC hdc); ! 92: BOOL CalcPrintArea(HPS hpsPrinter, ! 93: PRECTL prclPrintArea, ! 94: PFONTMETRICS pfm); ! 95: ! 96: ! 97: /****************************************************************\ ! 98: * Initalizes the print dialog structures ! 99: *-------------------------------------------------------------- ! 100: * ! 101: * Name: InitPrintDialogs() ! 102: * ! 103: * Purpose: Places defaults in the global info structures ! 104: * used by the standard print dialogs ! 105: * ! 106: * Usage: called once from the initialization routine ! 107: * ! 108: * Method: places standard defaults into each of the fields ! 109: * of the Page Setup, Print Setup, and Print Dialog ! 110: * structures. The values used are standard defaults, ! 111: * such as inches for the page units, which can be ! 112: * easily changed by changing the default values in ! 113: * the routine. The application developer should ! 114: * modify these values if he wishes to have the ! 115: * default printer and page setup use different values. ! 116: * ! 117: * Returns: ! 118: * ! 119: \****************************************************************/ ! 120: VOID InitPrintingDialogs(VOID) ! 121: { ! 122: ! 123: #ifdef PRINT_DLGS_ENABLED ! 124: ! 125: fPrintEnabled = TRUE; ! 126: ! 127: /*--------------------------------------------------------------*\ ! 128: * Initialize the Page Setup structure ! 129: \*--------------------------------------------------------------*/ ! 130: pgsd.kgd.cbSize = sizeof(PAGESETUPDLG); ! 131: pgsd.kgd.flStyle = PGSS_CENTER; ! 132: ! 133: if(!WinLoadString(hab, ! 134: NULL, ! 135: IDS_PAGESETUPTITLE, ! 136: DLGTITLELEN, ! 137: (PSZ)szPgsdTitle)) { ! 138: ! 139: MessageBox(hwndMain, IDMSG_CANNOTLOADSTRING, FALSE); ! 140: fPrintEnabled = FALSE; ! 141: return; ! 142: } ! 143: ! 144: pgsd.kgd.pszDlgTitle = szPgsdTitle; ! 145: pgsd.kgd.pfnDlgProc = NULL; ! 146: pgsd.kgd.hmod = NULL; ! 147: pgsd.kgd.idDlg = 0; ! 148: pgsd.kgd.lReturn = 0L; ! 149: pgsd.kgd.x = 0; ! 150: pgsd.kgd.y = 0; ! 151: ! 152: pgsd.flFlags = PGSF_INCHES; ! 153: ! 154: /* Setup the header and footer fields. */ ! 155: pgsd.cchHeader = MAXFOOTERLEN; ! 156: pgsd.cchFooter = MAXHEADERLEN; ! 157: pgsd.pszHeader = szHeader; ! 158: pgsd.pszFooter = szFooter; ! 159: ! 160: /* Setup the margin fields to one inch top and side margins */ ! 161: pgsd.fxLeftMargin = ONEINCH; ! 162: pgsd.fxRightMargin = ONEINCH; ! 163: pgsd.fxTopMargin = ONEINCH; ! 164: pgsd.fxBottomMargin = ONEINCH; ! 165: ! 166: /* maximum margins are 8 inch left and right, 11 inch bottom. We ! 167: are assuming the default page size is 8 1/2" x 11 " */ ! 168: pgsd.fxLeftMarginMax = EIGHTINCHES; ! 169: pgsd.fxRightMarginMax = EIGHTINCHES; ! 170: pgsd.fxTopMarginMax = ELEVENINCHES; ! 171: pgsd.fxBottomMarginMax = ELEVENINCHES; ! 172: ! 173: pgsd.lUser = 0L; ! 174: ! 175: ! 176: /*--------------------------------------------------------------*\ ! 177: * Initialize the Print Setup structure ! 178: \*--------------------------------------------------------------*/ ! 179: psd.kgd.cbSize = sizeof(PRINTSETUPDLG); ! 180: psd.kgd.flStyle = PGSS_CENTER; ! 181: ! 182: if(!WinLoadString(hab, ! 183: NULL, ! 184: IDS_PRINTSETUPTITLE, ! 185: DLGTITLELEN, ! 186: (PSZ)szPsdTitle)) { ! 187: ! 188: MessageBox(hwndMain, IDMSG_CANNOTLOADSTRING, FALSE); ! 189: fPrintEnabled = FALSE; ! 190: return; ! 191: } ! 192: ! 193: psd.kgd.pszDlgTitle = szPsdTitle; ! 194: psd.kgd.pfnDlgProc = NULL; ! 195: psd.kgd.hmod = NULL; ! 196: psd.kgd.idDlg = 0; ! 197: psd.kgd.lReturn = 0L; ! 198: psd.kgd.x = 0; ! 199: psd.kgd.y = 0; ! 200: ! 201: psd.pszAppName = szAppName; ! 202: psd.lUser = 0L; ! 203: ! 204: /*--------------------------------------------------------------*\ ! 205: * Initialize the Print Dialog structure ! 206: \*--------------------------------------------------------------*/ ! 207: prtd.kgd.cbSize = sizeof(PRINTDLG); ! 208: prtd.kgd.flStyle = PGSS_CENTER; ! 209: ! 210: if(!WinLoadString(hab, ! 211: NULL, ! 212: IDS_PRINTTITLE, ! 213: DLGTITLELEN, ! 214: (PSZ)szPrtdTitle)) { ! 215: ! 216: MessageBox(hwndMain, IDMSG_CANNOTLOADSTRING, FALSE); ! 217: fPrintEnabled = FALSE; ! 218: return; ! 219: } ! 220: ! 221: ! 222: prtd.kgd.pszDlgTitle = szPrtdTitle; ! 223: prtd.kgd.pfnDlgProc = NULL; ! 224: prtd.kgd.hmod = NULL; ! 225: prtd.kgd.idDlg = 0; ! 226: prtd.kgd.lReturn = 0L; ! 227: prtd.kgd.x = 0; ! 228: prtd.kgd.y = 0; ! 229: ! 230: prtd.flFlags = PRTF_ALLPAGES; ! 231: prtd.cPages = 0; ! 232: prtd.cCopies = 1; ! 233: ! 234: prtd.iFrom = 0; ! 235: prtd.iTo = 0; ! 236: ! 237: prtd.cchFileName = 0; ! 238: prtd.pszFileName = NULL; ! 239: ! 240: prtd.lUser = 0L; ! 241: ! 242: ! 243: /*--------------------------------------------------------------*\ ! 244: * Associate all of the dialog structures with each other so ! 245: * that the dialogs can be called from one another ! 246: \*--------------------------------------------------------------*/ ! 247: pgsd.ppsd = &psd; ! 248: pgsd.pprtd = &prtd; ! 249: psd.ppgsd = &pgsd; ! 250: psd.pprtd = &prtd; ! 251: prtd.ppsd = &psd; ! 252: prtd.ppgsd = &pgsd; ! 253: ! 254: ! 255: /*--------------------------------------------------------------*\ ! 256: * Initialize the DEVOPENSTRUC in psd and prtd with the data ! 257: * from the default printer in os2.ini ! 258: \*--------------------------------------------------------------*/ ! 259: psd.kgd.flStyle |= PSS_INITDEVOPENSTRUC; ! 260: if(!KitPrintSetupDlg(NULL, &psd)) ! 261: fPrintEnabled = FALSE; ! 262: ! 263: prtd.kgd.flStyle |= PSS_INITDEVOPENSTRUC; ! 264: if(!KitPrintDlg(NULL, &prtd)) ! 265: fPrintEnabled = FALSE; ! 266: ! 267: if(!fPrintEnabled) ! 268: MessageBox(HWND_DESKTOP, ! 269: IDMSG_PRINTINITFAILED, ! 270: MB_OK | MB_ERROR, ! 271: FALSE); ! 272: ! 273: #else ! 274: fPrintEnabled = FALSE; ! 275: #endif ! 276: ! 277: } /* InitPrintDialogs() */ ! 278: ! 279: #ifdef PRINT_DLGS_ENABLED ! 280: ! 281: ! 282: /****************************************************************\ ! 283: * Calls the page setup dialog ! 284: *-------------------------------------------------------------- ! 285: * ! 286: * Name: PageSetup(hwndOwner) ! 287: * ! 288: * Purpose: Changes the page setup options by calling the ! 289: * standard page setup dialog ! 290: * ! 291: * Usage: called whenever the user wants to update his ! 292: * page setup. ! 293: * ! 294: * Method: Calls the standard Page Setup dialog with the ! 295: * global PAGESETUPDLG structure which currently ! 296: * holds the user's choices. ! 297: * ! 298: * Returns: ! 299: * ! 300: \****************************************************************/ ! 301: VOID PageSetup(hwndOwner) ! 302: HWND hwndOwner; /* Owner of the dialog */ ! 303: { ! 304: if(!KitPageSetupDlg(hwndOwner, &pgsd)) ! 305: MessageBox(hwndOwner, ! 306: IDMSG_CANNOTRUNPAGESETUP, ! 307: MB_OK | MB_ERROR, ! 308: TRUE); ! 309: ! 310: } /* PageSetup() */ ! 311: ! 312: ! 313: /****************************************************************\ ! 314: * Calls the print setup dialog ! 315: *-------------------------------------------------------------- ! 316: * ! 317: * Name: PrintSetup(hwndOwner) ! 318: * ! 319: * Purpose: Changes the printer setup by calling the standard ! 320: * print setup dialog ! 321: * ! 322: * Usage: called whenever the user wants to update his ! 323: * printer set up. ! 324: * ! 325: * Method: Calls the standard Print Setup dialog with the ! 326: * global PRINTSETUPDLG structure which currently ! 327: * holds the user's choices. ! 328: * ! 329: * Returns: ! 330: * ! 331: \****************************************************************/ ! 332: VOID PrintSetup(hwndOwner) ! 333: HWND hwndOwner; /* Owner of the dialog */ ! 334: { ! 335: ! 336: if(!KitPrintSetupDlg(hwndOwner, &psd)) ! 337: MessageBox(hwndOwner, ! 338: IDMSG_CANNOTRUNPRINTSETUP, ! 339: MB_OK | MB_ERROR, ! 340: TRUE); ! 341: ! 342: } /* PrintSetup() */ ! 343: ! 344: ! 345: /****************************************************************\ ! 346: * Begins the printing of a document ! 347: *-------------------------------------------------------------- ! 348: * ! 349: * Name: Print() ! 350: * ! 351: * Purpose: Does the groundwork necessary when printing a file ! 352: * ! 353: * Usage: called whenever the user wants to print his file ! 354: * ! 355: * Method: - determines the number of pages in the document ! 356: * - calls the standard print dialog to get the user's ! 357: * printing options ! 358: * - opens the DC for the selected printer ! 359: * - calls the application's printing routine, passing ! 360: * it the handle to the DC of the printer ! 361: * ! 362: * Returns: ! 363: * ! 364: \****************************************************************/ ! 365: VOID Print(hwndOwner) ! 366: HWND hwndOwner; /* Owner of the dialog */ ! 367: { ! 368: HDC hdcPrinter; ! 369: ! 370: /* Determine number of pages available to print */ ! 371: prtd.cPages = QueryMaxPages(); ! 372: if(prtd.cPages == QMP_ERROR) { ! 373: MessageBox(hwndOwner, IDMSG_CANNOTGETPAGEINFO, MB_OK | MB_ERROR, FALSE); ! 374: return; ! 375: } ! 376: ! 377: prtd.cCopies = 1; /* initialize the number of copies to one */ ! 378: ! 379: /* Bring up the Print Dialog */ ! 380: if(!KitPrintDlg(hwndOwner, &prtd)) { ! 381: MessageBox(hwndOwner, IDMSG_CANNOTRUNPRINT, MB_OK | MB_ERROR, TRUE); ! 382: return; ! 383: } ! 384: ! 385: /* if user cancelled the dialog, cancel the printing */ ! 386: if(prtd.kgd.lReturn == ID_CANCEL) ! 387: return; ! 388: ! 389: ! 390: /*--------------------------------------------------------------*\ ! 391: * Open the DC for the default printer. The default printer ! 392: * DEVOPENDATA is stored in the prtd structure ! 393: \*--------------------------------------------------------------*/ ! 394: hdcPrinter = DevOpenDC(hab, ! 395: OD_QUEUED, ! 396: "*", ! 397: DEVDATACOUNT, ! 398: (PDEVOPENDATA)&prtd.ppsd->dop, ! 399: (HDC)NULL); ! 400: ! 401: if(!hdcPrinter) { ! 402: MessageBox(hwndMain, IDMSG_CANNOTOPENPRINTER, MB_OK | MB_ERROR, FALSE); ! 403: return; ! 404: } ! 405: ! 406: /*--------------------------------------------------------------*\ ! 407: * Call the application's printing function ! 408: \*--------------------------------------------------------------*/ ! 409: ! 410: if(!PrintDoc(hdcPrinter)) ! 411: MessageBox(hwndOwner, IDMSG_PRINTERROR, MB_OK | MB_ERROR, FALSE); ! 412: ! 413: DevCloseDC(hdcPrinter); ! 414: ! 415: } /* Print() */ ! 416: ! 417: ! 418: /****************************************************************\ ! 419: * Calculates the print area of the printer page ! 420: *-------------------------------------------------------------- ! 421: * ! 422: * Name: CalcPrintArea(hpsPrinter, prclPrintArea, pfm) ! 423: * ! 424: * Purpose: Sets up the print HPS by installing the font, ! 425: * calculating the print rectangle, and returning ! 426: * the metrics of the chosen font ! 427: * ! 428: * Usage: called by PrintDoc() and QueryMaxPages() to set ! 429: * up the printer HPS for printing. ! 430: * ! 431: * Note: HPS must be associated with the printer HDC ! 432: * ! 433: * Method: Routine creates an HPS for the printer DC that ! 434: * uses LOENGLISH for page units. A similar HPS is ! 435: * created for the MLE through which the current MLE ! 436: * font is translated from device metrics to LOENGLISH ! 437: * metrics. The print rectangle is determined by ! 438: * finding the printer page size and then subtracting ! 439: * the margins and the headers and footers, if any. ! 440: * The new font is selected into the printer PS and ! 441: * the metrics are returned for the font. The HPS ! 442: * is returned with the printer font selected. ! 443: * ! 444: * Returns: TRUE if successfull, FALSE if not ! 445: * ! 446: \****************************************************************/ ! 447: BOOL CalcPrintArea(hpsPrinter, prclPrintArea, pfm) ! 448: HPS hpsPrinter; /* INPUT: HPS associated with the printer DC */ ! 449: PRECTL prclPrintArea; /* OUTPUT: buffer for the print rectangle */ ! 450: PFONTMETRICS pfm; /* OUTPUT: pointer to buffer for fontmetrics of ! 451: printer font */ ! 452: { ! 453: HPS hpsMLE; ! 454: SIZEL szl; ! 455: FATTRS fattr; ! 456: FONTMETRICS fm; ! 457: HDC hdcPrinter; ! 458: ! 459: szl.cx = szl.cy = 0L; ! 460: ! 461: /* get the handle to the HDC of the printer */ ! 462: hdcPrinter = GpiQueryDevice(hpsPrinter); ! 463: if(!hdcPrinter) ! 464: return FALSE; ! 465: ! 466: /* get width and height of paper and convert those points to page units */ ! 467: if(!DevQueryCaps(hdcPrinter, CAPS_WIDTH, 2L, (PLONG)&szl)) ! 468: return FALSE; ! 469: ! 470: if(GpiConvert(hpsPrinter, CVTC_DEVICE, CVTC_WORLD, 2L, (PPOINTL)&szl) == ! 471: GPI_ERROR) ! 472: return FALSE; ! 473: ! 474: ! 475: ! 476: /*--------------------------------------------------------------*\ ! 477: * prclPrintArea will be set to the rectangle that encompasses ! 478: * the area of the page where the text will be printed. The ! 479: * rectangle is defined to be the rectangle of the page minus ! 480: * the margins on each side. For example, the print area ! 481: * on a page using the default margins will be the rectangle ! 482: * that is one inch from the left, bottom, right, and top ! 483: * edges of the papaer. ! 484: * ! 485: * This routine creates the print area rectangle in world ! 486: * coordinates. Since the printer HPS was created in LOENGLISH ! 487: * page units, each point of world coordinate space equals ! 488: * .01 inch, or 100 points equals one inch. The left and ! 489: * bottom sides of the print area rectangle are equal to the ! 490: * the number of inches of the left and bottom margins times ! 491: * 100. The top and right margins can also be calculated by ! 492: * multiplying the margin size * 100, but then that distance ! 493: * must be subtracted from the width and height of the paper. ! 494: * ! 495: * The FIXEDTOWORLD() macro, as defined above, converts a ! 496: * FIXED integer value into LOENGLISH world coordinates. If ! 497: * you change either the page units of the printer HPS or the ! 498: * scale of the margins, you must changed FIXEDTOWORLD() to ! 499: * translate the margin dimensions to fit the new parameters. ! 500: * ! 501: \*--------------------------------------------------------------*/ ! 502: prclPrintArea->xLeft = FIXEDTOWORLD(pgsd.fxLeftMargin); ! 503: ! 504: prclPrintArea->yBottom = FIXEDTOWORLD(pgsd.fxBottomMargin); ! 505: ! 506: prclPrintArea->xRight = szl.cx - FIXEDTOWORLD(pgsd.fxRightMargin); ! 507: ! 508: prclPrintArea->yTop = szl.cy - FIXEDTOWORLD(pgsd.fxTopMargin); ! 509: ! 510: ! 511: ! 512: /* get a PS for the MLE and set it to be LOENGLISH also. ! 513: * ! 514: * Note: only GpiSetPS is checked for an error return. That is ! 515: * because if the WinGetPS() function returns an error, hpsMLE ! 516: * will be NULL. If hpsMLE is NULL, then GpiSetPS() will fail. ! 517: * So, GpiSetPS() will fail if WinGetPS() fails, so we only need ! 518: * to check the return code from GpiSetPS() ! 519: */ ! 520: ! 521: hpsMLE = WinGetPS(hwndMLE); ! 522: if(GpiSetPS(hpsMLE, &szl, PU_LOENGLISH) == GPI_ERROR) ! 523: return FALSE; ! 524: ! 525: /* get font information from MLE and create similar font on printer */ ! 526: WinSendMsg(hwndMLE, MLM_QUERYFONT, MPFROMP(&fattr), NULL); ! 527: ! 528: /* convert font information to LOENGLISH by setting the font into ! 529: * the LOENGLISH PS we created for the MLE, getting the ! 530: * fontmetrics, and then updating the fattr structure. ! 531: * ! 532: * Note: only GpiSetCharSet's return value is checked, because if ! 533: * the GpiCreateLogFont call fails, the LCID passed to GpiSetCharSet ! 534: * will be invalid and GpiSetCharSet will fail as well. ! 535: */ ! 536: ! 537: GpiCreateLogFont(hpsMLE, (PSTR8)fattr.szFacename, FONTLCID, &fattr); ! 538: if(GpiSetCharSet(hpsMLE, FONTLCID) == GPI_ERROR) ! 539: return FALSE; ! 540: ! 541: if(GpiQueryFontMetrics(hpsMLE, ! 542: (ULONG)sizeof(FONTMETRICS), ! 543: (PFONTMETRICS)&fm) == GPI_ERROR) ! 544: return FALSE; ! 545: ! 546: /* delete font and release MLE HPS. Return values are not checked for ! 547: * error because a failure in the clean up of the HPS will not ! 548: * adversely affect this routine ! 549: */ ! 550: GpiSetCharSet(hpsMLE, 0); ! 551: GpiDeleteSetId(hpsMLE, FONTLCID); ! 552: WinReleasePS(hpsMLE); ! 553: ! 554: /* The FATTRS structure was filled with the information for the font ! 555: * of the MLE. The height and width of the font is in device ! 556: * coordinates, however. Since we created the same font on a ! 557: * PU_LOENGLISH HPS, we now can get the height and width in ! 558: * PU_LOENGLISH page units. These values are contained in the ! 559: * fm structure. We replace the height and width of the font with ! 560: * the PU_LOENGLISH equavalents and set the lMatch to 0, so that ! 561: * GPI will look through all printer fonts for the font that most ! 562: * closely matches the size of the font used on the screen. ! 563: */ ! 564: ! 565: fattr.lMaxBaselineExt = fm.lMaxBaselineExt; ! 566: fattr.lAveCharWidth = fm.lAveCharWidth; ! 567: fattr.lMatch = 0L; ! 568: ! 569: /* Create the font on the printer HPS. ! 570: * ! 571: * Note: if GpiCreateLogFont() returns 1, then an exact match for ! 572: * the font desired was not found. This application does not care ! 573: * if the font matches exactly or not, but your application might. ! 574: * If GpiCreateLogFont() returns 0, and error occurred, which will ! 575: * be caught when we attempt GpiSetCharSet() with this font's LCID ! 576: */ ! 577: GpiCreateLogFont(hpsPrinter, (PSTR8)fattr.szFacename, FONTLCID, &fattr); ! 578: ! 579: if(GpiSetCharSet(hpsPrinter, FONTLCID) == GPI_ERROR) ! 580: return FALSE; ! 581: ! 582: /* get printer font information for return in pfm */ ! 583: if(GpiQueryFontMetrics(hpsPrinter, ! 584: (LONG)sizeof(FONTMETRICS), ! 585: pfm) == GPI_ERROR) ! 586: return FALSE; ! 587: ! 588: ! 589: /*--------------------------------------------------------------*\ ! 590: * If a header or footer is present, it will take two lines - ! 591: * one for the text and one blank line. Since rclPrintArea ! 592: * is the rectangle where the document text is to be printed, ! 593: * the two lines of the header or footer are removed from ! 594: * the current print area. The top and bottom side are ! 595: * adjusted accordingly. ! 596: * ! 597: * When the header and footer are printed, they are printed ! 598: * two lines above and below the print area. ! 599: * ! 600: \*--------------------------------------------------------------*/ ! 601: ! 602: /* if header is present, adjust top for it */ ! 603: if(pgsd.pszHeader[0]) ! 604: prclPrintArea->yTop -= (pfm->lMaxBaselineExt + ! 605: pfm->lExternalLeading) * 2L; ! 606: ! 607: /* if footer is present, adjust bottom for it */ ! 608: if(pgsd.pszFooter[0]) ! 609: /*--------------------------------------------------------------*\ ! 610: * A line of text has a height equal to the MaxBaselineExt ! 611: * followed by the ExternalLeading. Since the footer is ! 612: * printed on the bottom of the page, the ExternalLeading ! 613: * following the footer is not necessary. The height of the ! 614: * footer, then, is calculated as the sum of the heights ! 615: * of the text line, the blank line, and the External Leading * ! 616: * between them. ! 617: * ! 618: \*--------------------------------------------------------------*/ ! 619: prclPrintArea->yBottom += (pfm->lMaxBaselineExt * 2L) + ! 620: pfm->lExternalLeading; ! 621: ! 622: return TRUE; ! 623: ! 624: } /* CalcPrintArea() */ ! 625: ! 626: ! 627: /****************************************************************\ ! 628: * Determines the maximum number of pages in the document ! 629: *-------------------------------------------------------------- ! 630: * ! 631: * Name: QueryMaxPages() ! 632: * ! 633: * Purpose: returns the maximum number of pages in a file ! 634: * ! 635: * Usage: called by Print() to initialize the prtd.cPages ! 636: * field ! 637: * ! 638: * Method: ! 639: * ! 640: * Returns: Number of pages in the document or QMP_ERROR if an error ! 641: * ! 642: \****************************************************************/ ! 643: SHORT QueryMaxPages(VOID) ! 644: { ! 645: HDC hdcPrinter; ! 646: FONTMETRICS fm; ! 647: RECTL rclPrintArea; ! 648: HPS hpsPrinter; ! 649: SIZEL szl; ! 650: LONG lLines, lLinesPerPage; ! 651: ! 652: /*--------------------------------------------------------------*\ ! 653: * Code to determine the number of pages in the document ! 654: * to be printed ! 655: \*--------------------------------------------------------------*/ ! 656: ! 657: /* create an info dc for the printer */ ! 658: hdcPrinter = DevOpenDC(hab, ! 659: OD_INFO, ! 660: "*", ! 661: DEVDATACOUNT, ! 662: (PDEVOPENDATA)&prtd.ppsd->dop, ! 663: (HDC)NULL); ! 664: if(!hdcPrinter) ! 665: return QMP_ERROR; ! 666: ! 667: /* create a ps for the DC. Use english page units so we can ! 668: measure the margins easily */ ! 669: szl.cx = szl.cy = 0L; ! 670: hpsPrinter = GpiCreatePS(hab, ! 671: hdcPrinter, ! 672: &szl, ! 673: PU_LOENGLISH | GPIT_NORMAL | GPIF_DEFAULT | GPIA_ASSOC); ! 674: if(!hpsPrinter) ! 675: return QMP_ERROR; ! 676: ! 677: if(!CalcPrintArea(hpsPrinter, &rclPrintArea, &fm)) ! 678: return QMP_ERROR; ! 679: ! 680: /* get number of lines in the document */ ! 681: lLines = (LONG)WinSendMsg(hwndMLE, MLM_QUERYLINECOUNT, NULL, NULL); ! 682: ! 683: /* calculate the number of lines per page */ ! 684: lLinesPerPage = (rclPrintArea.yTop - rclPrintArea.yBottom) / ! 685: (fm.lMaxBaselineExt + fm.lExternalLeading); ! 686: ! 687: GpiAssociate(hpsPrinter, NULL); ! 688: GpiDestroyPS(hpsPrinter); ! 689: DevCloseDC(hdcPrinter); ! 690: ! 691: return(SHORT)(lLines / lLinesPerPage) + 1; ! 692: ! 693: } /* QueryMaxPages() */ ! 694: ! 695: ! 696: /****************************************************************\ ! 697: * Performs the actual printing of a document ! 698: *-------------------------------------------------------------- ! 699: * ! 700: * Name: PrintDoc(hdc) ! 701: * ! 702: * Purpose: Performs the printing of a file ! 703: * ! 704: * Usage: called by Print() to actually print the file ! 705: * ! 706: * Method: This routine prints each line of the MLE. If a ! 707: * line is longer than the page width, then the line ! 708: * is clipped to the margin; it is not wrapped to ! 709: * the next line. ! 710: * ! 711: * Note: this routine should NOT close the printer DC ! 712: * ! 713: * Returns: TRUE if successfull, FALSE if not ! 714: * ! 715: \****************************************************************/ ! 716: BOOL PrintDoc(hdc) ! 717: HDC hdc; /* hdc of the printer */ ! 718: { ! 719: USHORT usSpoolId, usNumCopies; ! 720: LONG lOutData, lOffset = 0L, lFileEnd, lLine, lT, lPageLines; ! 721: LONG lLinesPerPage, lLineHeight, lStartOffset, lTotalLines; ! 722: RECTL rclPrintArea, rclText; ! 723: HPS hpsPrinter; ! 724: SIZEL szl; ! 725: PVOID pvBuf; ! 726: FONTMETRICS fm; ! 727: POINTL ptl; ! 728: BOOL fRet = TRUE; ! 729: ! 730: /*--------------------------------------------------------------*\ ! 731: * Creates a PS for the DC. Note that you can associate an ! 732: * existing HPS to the DC here instead if you are using ! 733: * retained graphics in the HPS. The PS must use PU_LOENGLISH ! 734: * page units since the CalcPrintArea() routine expects the ! 735: * the PS to use them. ! 736: \*--------------------------------------------------------------*/ ! 737: szl.cx = szl.cy = 0L; ! 738: hpsPrinter = GpiCreatePS(hab, ! 739: hdc, ! 740: &szl, ! 741: PU_LOENGLISH | GPIT_NORMAL | GPIF_DEFAULT | GPIA_ASSOC); ! 742: if(!hpsPrinter) ! 743: return FALSE; ! 744: ! 745: if(!CalcPrintArea(hpsPrinter, &rclPrintArea, &fm)) { ! 746: fRet = FALSE; ! 747: goto PrintDocExit; ! 748: } ! 749: ! 750: /*--------------------------------------------------------------*\ ! 751: * Note: CalcPrintArea() has created the printer font on ! 752: * hpsPrinter and selected the font. It has also returned ! 753: * the metrics for the font in fm. rclPrintArea is the ! 754: * rectangle bounding the print area of the page, excluding ! 755: * the header and footer. ! 756: \*--------------------------------------------------------------*/ ! 757: ! 758: /*--------------------------------------------------------------*\ ! 759: * rclText is set to the rectangle that will encompass one ! 760: * line of output. Its left and right sides are set to the ! 761: * margins of the paper, namely the left and right sides of ! 762: * the print area as represented by rclPrintArea. Its top ! 763: * will be repositioned for each line of output and its ! 764: * bottom will always be set to one line's height below the ! 765: * top. All text output will be clipped to this rectangle. ! 766: \*--------------------------------------------------------------*/ ! 767: ! 768: /* set rectangle for each text line to have its left and right sides ! 769: equal to the left and right margin of the print area */ ! 770: rclText.xLeft = rclPrintArea.xLeft; ! 771: rclText.xRight = rclPrintArea.xRight; ! 772: ! 773: /* calculate the height of each printed line, including any space ! 774: (leading) between the lines */ ! 775: lLineHeight = fm.lMaxBaselineExt + fm.lExternalLeading; ! 776: ! 777: /* calculate the number of lines that fit on a page */ ! 778: lLinesPerPage = (rclPrintArea.yTop - rclPrintArea.yBottom) / ! 779: lLineHeight; ! 780: ! 781: /* If our page size is too small to print even one line, then ! 782: abort the print job */ ! 783: if(lLinesPerPage < 0L) { ! 784: MessageBox(hwndMLE, IDMSG_PRINTAREATOOSMALL, MB_OK | MB_ERROR, FALSE); ! 785: goto PrintDocExit; ! 786: } ! 787: ! 788: ! 789: /* get the length of the output file */ ! 790: lFileEnd = (LONG)WinSendMsg(hwndMLE, MLM_QUERYTEXTLENGTH, NULL, NULL); ! 791: ! 792: /* create a buffer to hold the file */ ! 793: DosAllocMem((PPVOID) &pvBuf, lFileEnd, fALLOC); ! 794: ! 795: /* set up the buffer for the MLE text */ ! 796: WinSendMsg(hwndMLE, ! 797: MLM_SETIMPORTEXPORT, ! 798: MPFROMP(pvBuf), ! 799: MPFROMLONG(lFileEnd)); ! 800: ! 801: ! 802: /* if printing all pages, set file offset to beginning of file *\ ! 803: * and set the last line to the end of the file. If printing ! 804: * only certain pages, calculate the beginning and ending line ! 805: \* based on the pages */ ! 806: ! 807: if(prtd.flFlags & PRTF_ALLPAGES) { ! 808: lStartOffset = 0L; ! 809: lTotalLines = (LONG)WinSendMsg(hwndMLE, ! 810: MLM_QUERYLINECOUNT, ! 811: NULL, ! 812: NULL); ! 813: } else { ! 814: /* set lStartOffset to the beginning of the first line of the ! 815: first page set to print */ ! 816: lT = (LONG)(prtd.iFrom - 1L) * lLinesPerPage; ! 817: ! 818: if(lT < 0L); /* lT should never be less than 0, so this is */ ! 819: lT = 0L; /* purely defensive programming */ ! 820: ! 821: /* lT is the number of lines in all pages from the beginning ! 822: of the document to the first page printed */ ! 823: ! 824: lStartOffset = (LONG)WinSendMsg(hwndMLE, ! 825: MLM_CHARFROMLINE, ! 826: MPFROMLONG(lT), ! 827: NULL); ! 828: ! 829: /* set lTotalLines to the number of lines in the pages between the ! 830: starting and ending */ ! 831: lTotalLines = (LONG)(prtd.iTo - prtd.iFrom + 1) * lLinesPerPage; ! 832: lFileEnd = (LONG)WinSendMsg(hwndMLE, ! 833: MLM_QUERYLINECOUNT, ! 834: NULL, ! 835: NULL); ! 836: ! 837: /* if last line is beyond end of file, set the last line to ! 838: be the end of file */ ! 839: if(lTotalLines + lT > lFileEnd) ! 840: lTotalLines = lFileEnd - lT; ! 841: } ! 842: ! 843: ! 844: ! 845: /* print one document for each number of copies */ ! 846: usNumCopies = (USHORT)prtd.cCopies; ! 847: while(usNumCopies--) { ! 848: ! 849: /* set file offset variable to the beginning of the lines ! 850: being printed; set line variable to the number of lines ! 851: to be printed */ ! 852: lLine = lTotalLines; ! 853: lOffset = lStartOffset; ! 854: ! 855: /* send a DEVESC_STARTDOC to start the printing */ ! 856: if(DevEscape(hdc, ! 857: DEVESC_STARTDOC, ! 858: (LONG)MAXNAMEL, ! 859: (PBYTE)szAppName, ! 860: (PLONG)NULL, ! 861: (PBYTE)NULL) != DEV_OK) { ! 862: ! 863: MessageBox(hwndMain, ! 864: IDMSG_CANNOTOPENPRINTER, ! 865: MB_OK | MB_ERROR, ! 866: FALSE); ! 867: ! 868: fRet = FALSE; ! 869: goto PrintDocExit; ! 870: } ! 871: ! 872: while(lLine) { ! 873: ! 874: /* if we have header text, print the header at the top of the page */ ! 875: if(pgsd.pszHeader[0]) { ! 876: ! 877: /* set text rectangle to two lines above print area since ! 878: we reserved two lines for it in CalcPrintArea() */ ! 879: ! 880: rclText.yTop = rclPrintArea.yTop + (lLineHeight * 2L); ! 881: rclText.yBottom = rclText.yTop - fm.lMaxBaselineExt; ! 882: ! 883: ptl.x = rclText.xLeft; ! 884: ptl.y = rclText.yTop - fm.lMaxAscender; ! 885: ! 886: GpiMove(hpsPrinter, &ptl); ! 887: ! 888: GpiCharStringPos(hpsPrinter, ! 889: &rclText, ! 890: CHS_CLIP, ! 891: pgsd.cchHeader, ! 892: pgsd.pszHeader, ! 893: NULL); ! 894: ! 895: } ! 896: ! 897: /*==============================================================*\ ! 898: * Now print each line of text in the print area ! 899: \*==============================================================*/ ! 900: ! 901: /*--------------------------------------------------------------*\ ! 902: * The top of rclText is set here to the top of the print ! 903: * area while the bottom is set to the height of one line ! 904: * below the top. The measurement used to position the ! 905: * bottom is not the lLineHeight variable, but rather the ! 906: * actual height of the characters in the font as given by ! 907: * the lMaxBaselineExt field of the fontmetrics structure. ! 908: * Thus, rclText will be the height of the characters in the ! 909: * line and will not include any external leading. ! 910: \*--------------------------------------------------------------*/ ! 911: rclText.yTop = rclPrintArea.yTop; ! 912: rclText.yBottom = rclText.yTop - fm.lMaxBaselineExt; ! 913: ! 914: /*--------------------------------------------------------------*\ ! 915: * ptl will be the beginning point of the text string. The ! 916: * x coordinate of the point will always be on the left side ! 917: * of the print line, as given the rclText.xLeft. The y ! 918: * coordinate is set to the baseline of the font. The ! 919: * baseline is the line at the bottom of all characters, ! 920: * excluding any descenders. The baseline is determined by ! 921: * taking the top of the text line and subtracting the height ! 922: * of the ascender of the font. Thus: ! 923: * ! 924: * **** === === ! 925: * * * | | ! 926: * * * | ascender | ! 927: * * * | | MaxBaselineExt ! 928: * baseline --- *****------=== | ! 929: * * | | ! 930: * * * | descender | ! 931: * **** === === ! 932: * ! 933: * ptl.y is set to the baseline coordinate by subracting the ! 934: * Ascender of the font (fm.lMaxAscender) from the top of ! 935: * the text rectangle (rclText.yTop). ! 936: * ! 937: \*--------------------------------------------------------------*/ ! 938: ! 939: ptl.x = rclText.xLeft; ! 940: ptl.y = rclText.yTop - fm.lMaxAscender; ! 941: ! 942: /* set lPageLines to the number of lines on the page. If ! 943: there are fewer lines left in the document, then set ! 944: it to the number of lines left */ ! 945: lPageLines = min(lLinesPerPage, lLine); ! 946: ! 947: while(lPageLines--) { ! 948: ptl.y = rclText.yTop - fm.lMaxAscender; /* re-set baseline */ ! 949: ! 950: /* get current line from MLE */ ! 951: lT = WinSendMsg(hwndMLE, ! 952: MLM_QUERYFORMATLINELENGTH, ! 953: MPFROMLONG(lOffset), ! 954: NULL); ! 955: /* lT is the length of the current line, including the ! 956: CR/LF pair at the end */ ! 957: ! 958: lOutData = lT - 2L; /* length of line minus CR/LF pair */ ! 959: ! 960: /* get the text from the MLE */ ! 961: WinSendMsg(hwndMLE, ! 962: MLM_EXPORT, ! 963: MPFROMP(&lOffset), ! 964: MPFROMP(&lT)); ! 965: ! 966: /* print the string */ ! 967: if(GpiMove(hpsPrinter, &ptl) == GPI_ERROR) { ! 968: fRet = FALSE; ! 969: goto PrintDocExit; ! 970: } ! 971: ! 972: ! 973: if(GpiCharStringPos(hpsPrinter, ! 974: &rclText, ! 975: CHS_CLIP, ! 976: lOutData, ! 977: pvBuf, ! 978: NULL) == GPI_ERROR) { ! 979: fRet = FALSE; ! 980: goto PrintDocExit; ! 981: } ! 982: ! 983: /* subtract one from the lines remaining */ ! 984: lLine--; ! 985: ! 986: /* move offset pointer to the beginning of next line */ ! 987: lOffset += lT; ! 988: ! 989: /* set the top of the text rectangle to the top of the ! 990: next line. Set the bottom to a character's height ! 991: below the top */ ! 992: rclText.yTop = rclText.yBottom - fm.lExternalLeading; ! 993: rclText.yBottom = rclText.yTop - fm.lMaxBaselineExt; ! 994: ! 995: } /* while(lPageLines) */ ! 996: ! 997: ! 998: /* add footer if necessary */ ! 999: if(pgsd.pszFooter[0]) { ! 1000: ! 1001: /* set text rectangle to two lines below print area since ! 1002: we reserved two lines for it in CalcPrintArea(). Refer ! 1003: to the CalcPrintArea() function for an explanation of ! 1004: the height of the footer */ ! 1005: ! 1006: rclText.yBottom = rclPrintArea.yBottom - ! 1007: ((fm.lMaxBaselineExt * 2L) + ! 1008: fm.lExternalLeading); ! 1009: ! 1010: rclText.yTop = rclText.yBottom + fm.lMaxBaselineExt; ! 1011: ! 1012: /* set ptl to baseline */ ! 1013: ptl.y = rclText.yTop - fm.lMaxAscender; ! 1014: ! 1015: /* print the footer text */ ! 1016: if(GpiMove(hpsPrinter, &ptl) == GPI_ERROR) { ! 1017: fRet = FALSE; ! 1018: goto PrintDocExit; ! 1019: } ! 1020: ! 1021: if(GpiCharStringPos(hpsPrinter, ! 1022: &rclText, ! 1023: CHS_CLIP, ! 1024: pgsd.cchFooter, ! 1025: pgsd.pszFooter, ! 1026: NULL) == GPI_ERROR) { ! 1027: fRet = FALSE; ! 1028: goto PrintDocExit; ! 1029: } ! 1030: ! 1031: } ! 1032: ! 1033: /* send a DEVESC_NEWFRAME to end this page */ ! 1034: if(DevEscape(hdc, ! 1035: DEVESC_NEWFRAME, ! 1036: 0L, ! 1037: (PBYTE)NULL, ! 1038: (PLONG)NULL, ! 1039: (PBYTE)NULL) != DEV_OK) { ! 1040: ! 1041: fRet = FALSE; ! 1042: goto PrintDocExit; ! 1043: } ! 1044: ! 1045: } /* while(lLine) */ ! 1046: ! 1047: ! 1048: ! 1049: /* send a DEVESC_ENDDOC to end the printing */ ! 1050: if(DevEscape(hdc, ! 1051: DEVESC_ENDDOC, ! 1052: 0L, ! 1053: (PBYTE)NULL, ! 1054: (PLONG)&lOutData, ! 1055: (PBYTE)&usSpoolId) != DEV_OK) { ! 1056: ! 1057: fRet = FALSE; ! 1058: goto PrintDocExit; ! 1059: } ! 1060: ! 1061: } /* while(usNumCopies) */ ! 1062: ! 1063: PrintDocExit: ! 1064: ! 1065: /* free the text buffer and destroy the PS */ ! 1066: GpiFreeMem((PVOID)pvBuf); ! 1067: ! 1068: GpiAssociate(hpsPrinter, NULL); ! 1069: GpiDestroyPS(hpsPrinter); ! 1070: ! 1071: return fRet; ! 1072: ! 1073: } /* PrintDoc() */ ! 1074: ! 1075: ! 1076: #endif
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.