|
|
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.