|
|
Microsoft OS/2 SDK 2.0 05-30-1990
/*==============================================================*\
* Prnt.c - routines for printing of a document
* Created 1990, Microsoft, IBM Corp.
*--------------------------------------------------------------
*
* This module contains the routines for the printing of a
* a document. Included are the routines for calling the
* standard dialogs for obtaining the user's printer and
* page setup preferences. Also included is the routine to
* determine the number of pages in a document as well as the
* routine to obtain the printer DC and print the document.
*
*--------------------------------------------------------------
*
* This source file contains the following functions:
*
* InitPrinting();
* PageSetup(hwndOwner);
* PrintSetup(hwndOwner);
* Print();
* CalcPrintArea(hpsPrinter,
* hdcPrinter,
* prclPrintArea,
* pfm);
* PrintDoc(hdc);
* QueryMaxPages();
*
*
\*==============================================================*/
/*--------------------------------------------------------------*\
* Include files, macros, defined constants, and externs
\*--------------------------------------------------------------*/
#define INCL_WINMLE
#define INCL_GPICONTROL
#define INCL_GPITRANSFORMS
#define INCL_GPIPRIMITIVES
#define INCL_GPILCIDS
#define INCL_DEV
#include <os2.h>
#include "sty_main.h"
#include "sty_xtrn.h"
#if 0
#include "pmkitp.h"
#define PRINT_DLGS_ENABLED
#endif
#define MAXHEADERLEN 100 /* maximum length of header */
#define MAXFOOTERLEN 100 /* maximum length of footer */
#define DLGTITLELEN 30 /* length of dialog title string */
#define DEVDATACOUNT 7L /* # fields of DEVOPENSTRUCT used */
#define FONTLCID 1L /* LCID for printer font */
#define QMP_ERROR -1 /* error return from QueryMaxPages() */
/* constants used in InitPrintingDlgs() for initial and maximum margins */
#define ONEINCH (MAKEFIXED(1, 0))
#define EIGHTINCHES (MAKEFIXED(8, 0))
#define ELEVENINCHES (MAKEFIXED(11, 0))
/* Macro for converting a FIXED integer value into LO_ENGLISH
world coordinates */
#define FIXEDTOWORLD(x) ((FIXEDINT(x) * 100L) + \
((FIXEDFRAC(x) * 100L) / 65536L))
/*--------------------------------------------------------------*\
* Global variables
\*--------------------------------------------------------------*/
#ifdef PRINT_DLGS_ENABLED
static PAGESETUPDLG pgsd;
static PRINTSETUPDLG psd;
static PRINTDLG prtd;
static CHAR szHeader[MAXHEADERLEN], szFooter[MAXFOOTERLEN];
static CHAR szPgsdTitle[DLGTITLELEN];
static CHAR szPsdtitle[DLGTITLELEN];
static CHAR szPrtdTitle[DLGTITLELEN];
#endif
/*--------------------------------------------------------------*\
* Entry point declarations
\*--------------------------------------------------------------*/
SHORT QueryMaxPages(VOID);
BOOL PrintDoc(HDC hdc);
BOOL CalcPrintArea(HPS hpsPrinter,
PRECTL prclPrintArea,
PFONTMETRICS pfm);
/****************************************************************\
* Initalizes the print dialog structures
*--------------------------------------------------------------
*
* Name: InitPrintDialogs()
*
* Purpose: Places defaults in the global info structures
* used by the standard print dialogs
*
* Usage: called once from the initialization routine
*
* Method: places standard defaults into each of the fields
* of the Page Setup, Print Setup, and Print Dialog
* structures. The values used are standard defaults,
* such as inches for the page units, which can be
* easily changed by changing the default values in
* the routine. The application developer should
* modify these values if he wishes to have the
* default printer and page setup use different values.
*
* Returns:
*
\****************************************************************/
VOID InitPrintingDialogs(VOID)
{
#ifdef PRINT_DLGS_ENABLED
fPrintEnabled = TRUE;
/*--------------------------------------------------------------*\
* Initialize the Page Setup structure
\*--------------------------------------------------------------*/
pgsd.kgd.cbSize = sizeof(PAGESETUPDLG);
pgsd.kgd.flStyle = PGSS_CENTER;
if(!WinLoadString(hab,
NULL,
IDS_PAGESETUPTITLE,
DLGTITLELEN,
(PSZ)szPgsdTitle)) {
MessageBox(hwndMain, IDMSG_CANNOTLOADSTRING, FALSE);
fPrintEnabled = FALSE;
return;
}
pgsd.kgd.pszDlgTitle = szPgsdTitle;
pgsd.kgd.pfnDlgProc = NULL;
pgsd.kgd.hmod = NULL;
pgsd.kgd.idDlg = 0;
pgsd.kgd.lReturn = 0L;
pgsd.kgd.x = 0;
pgsd.kgd.y = 0;
pgsd.flFlags = PGSF_INCHES;
/* Setup the header and footer fields. */
pgsd.cchHeader = MAXFOOTERLEN;
pgsd.cchFooter = MAXHEADERLEN;
pgsd.pszHeader = szHeader;
pgsd.pszFooter = szFooter;
/* Setup the margin fields to one inch top and side margins */
pgsd.fxLeftMargin = ONEINCH;
pgsd.fxRightMargin = ONEINCH;
pgsd.fxTopMargin = ONEINCH;
pgsd.fxBottomMargin = ONEINCH;
/* maximum margins are 8 inch left and right, 11 inch bottom. We
are assuming the default page size is 8 1/2" x 11 " */
pgsd.fxLeftMarginMax = EIGHTINCHES;
pgsd.fxRightMarginMax = EIGHTINCHES;
pgsd.fxTopMarginMax = ELEVENINCHES;
pgsd.fxBottomMarginMax = ELEVENINCHES;
pgsd.lUser = 0L;
/*--------------------------------------------------------------*\
* Initialize the Print Setup structure
\*--------------------------------------------------------------*/
psd.kgd.cbSize = sizeof(PRINTSETUPDLG);
psd.kgd.flStyle = PGSS_CENTER;
if(!WinLoadString(hab,
NULL,
IDS_PRINTSETUPTITLE,
DLGTITLELEN,
(PSZ)szPsdTitle)) {
MessageBox(hwndMain, IDMSG_CANNOTLOADSTRING, FALSE);
fPrintEnabled = FALSE;
return;
}
psd.kgd.pszDlgTitle = szPsdTitle;
psd.kgd.pfnDlgProc = NULL;
psd.kgd.hmod = NULL;
psd.kgd.idDlg = 0;
psd.kgd.lReturn = 0L;
psd.kgd.x = 0;
psd.kgd.y = 0;
psd.pszAppName = szAppName;
psd.lUser = 0L;
/*--------------------------------------------------------------*\
* Initialize the Print Dialog structure
\*--------------------------------------------------------------*/
prtd.kgd.cbSize = sizeof(PRINTDLG);
prtd.kgd.flStyle = PGSS_CENTER;
if(!WinLoadString(hab,
NULL,
IDS_PRINTTITLE,
DLGTITLELEN,
(PSZ)szPrtdTitle)) {
MessageBox(hwndMain, IDMSG_CANNOTLOADSTRING, FALSE);
fPrintEnabled = FALSE;
return;
}
prtd.kgd.pszDlgTitle = szPrtdTitle;
prtd.kgd.pfnDlgProc = NULL;
prtd.kgd.hmod = NULL;
prtd.kgd.idDlg = 0;
prtd.kgd.lReturn = 0L;
prtd.kgd.x = 0;
prtd.kgd.y = 0;
prtd.flFlags = PRTF_ALLPAGES;
prtd.cPages = 0;
prtd.cCopies = 1;
prtd.iFrom = 0;
prtd.iTo = 0;
prtd.cchFileName = 0;
prtd.pszFileName = NULL;
prtd.lUser = 0L;
/*--------------------------------------------------------------*\
* Associate all of the dialog structures with each other so
* that the dialogs can be called from one another
\*--------------------------------------------------------------*/
pgsd.ppsd = &psd;
pgsd.pprtd = &prtd;
psd.ppgsd = &pgsd;
psd.pprtd = &prtd;
prtd.ppsd = &psd;
prtd.ppgsd = &pgsd;
/*--------------------------------------------------------------*\
* Initialize the DEVOPENSTRUC in psd and prtd with the data
* from the default printer in os2.ini
\*--------------------------------------------------------------*/
psd.kgd.flStyle |= PSS_INITDEVOPENSTRUC;
if(!KitPrintSetupDlg(NULL, &psd))
fPrintEnabled = FALSE;
prtd.kgd.flStyle |= PSS_INITDEVOPENSTRUC;
if(!KitPrintDlg(NULL, &prtd))
fPrintEnabled = FALSE;
if(!fPrintEnabled)
MessageBox(HWND_DESKTOP,
IDMSG_PRINTINITFAILED,
MB_OK | MB_ERROR,
FALSE);
#else
fPrintEnabled = FALSE;
#endif
} /* InitPrintDialogs() */
#ifdef PRINT_DLGS_ENABLED
/****************************************************************\
* Calls the page setup dialog
*--------------------------------------------------------------
*
* Name: PageSetup(hwndOwner)
*
* Purpose: Changes the page setup options by calling the
* standard page setup dialog
*
* Usage: called whenever the user wants to update his
* page setup.
*
* Method: Calls the standard Page Setup dialog with the
* global PAGESETUPDLG structure which currently
* holds the user's choices.
*
* Returns:
*
\****************************************************************/
VOID PageSetup(hwndOwner)
HWND hwndOwner; /* Owner of the dialog */
{
if(!KitPageSetupDlg(hwndOwner, &pgsd))
MessageBox(hwndOwner,
IDMSG_CANNOTRUNPAGESETUP,
MB_OK | MB_ERROR,
TRUE);
} /* PageSetup() */
/****************************************************************\
* Calls the print setup dialog
*--------------------------------------------------------------
*
* Name: PrintSetup(hwndOwner)
*
* Purpose: Changes the printer setup by calling the standard
* print setup dialog
*
* Usage: called whenever the user wants to update his
* printer set up.
*
* Method: Calls the standard Print Setup dialog with the
* global PRINTSETUPDLG structure which currently
* holds the user's choices.
*
* Returns:
*
\****************************************************************/
VOID PrintSetup(hwndOwner)
HWND hwndOwner; /* Owner of the dialog */
{
if(!KitPrintSetupDlg(hwndOwner, &psd))
MessageBox(hwndOwner,
IDMSG_CANNOTRUNPRINTSETUP,
MB_OK | MB_ERROR,
TRUE);
} /* PrintSetup() */
/****************************************************************\
* Begins the printing of a document
*--------------------------------------------------------------
*
* Name: Print()
*
* Purpose: Does the groundwork necessary when printing a file
*
* Usage: called whenever the user wants to print his file
*
* Method: - determines the number of pages in the document
* - calls the standard print dialog to get the user's
* printing options
* - opens the DC for the selected printer
* - calls the application's printing routine, passing
* it the handle to the DC of the printer
*
* Returns:
*
\****************************************************************/
VOID Print(hwndOwner)
HWND hwndOwner; /* Owner of the dialog */
{
HDC hdcPrinter;
/* Determine number of pages available to print */
prtd.cPages = QueryMaxPages();
if(prtd.cPages == QMP_ERROR) {
MessageBox(hwndOwner, IDMSG_CANNOTGETPAGEINFO, MB_OK | MB_ERROR, FALSE);
return;
}
prtd.cCopies = 1; /* initialize the number of copies to one */
/* Bring up the Print Dialog */
if(!KitPrintDlg(hwndOwner, &prtd)) {
MessageBox(hwndOwner, IDMSG_CANNOTRUNPRINT, MB_OK | MB_ERROR, TRUE);
return;
}
/* if user cancelled the dialog, cancel the printing */
if(prtd.kgd.lReturn == ID_CANCEL)
return;
/*--------------------------------------------------------------*\
* Open the DC for the default printer. The default printer
* DEVOPENDATA is stored in the prtd structure
\*--------------------------------------------------------------*/
hdcPrinter = DevOpenDC(hab,
OD_QUEUED,
"*",
DEVDATACOUNT,
(PDEVOPENDATA)&prtd.ppsd->dop,
(HDC)NULL);
if(!hdcPrinter) {
MessageBox(hwndMain, IDMSG_CANNOTOPENPRINTER, MB_OK | MB_ERROR, FALSE);
return;
}
/*--------------------------------------------------------------*\
* Call the application's printing function
\*--------------------------------------------------------------*/
if(!PrintDoc(hdcPrinter))
MessageBox(hwndOwner, IDMSG_PRINTERROR, MB_OK | MB_ERROR, FALSE);
DevCloseDC(hdcPrinter);
} /* Print() */
/****************************************************************\
* Calculates the print area of the printer page
*--------------------------------------------------------------
*
* Name: CalcPrintArea(hpsPrinter, prclPrintArea, pfm)
*
* Purpose: Sets up the print HPS by installing the font,
* calculating the print rectangle, and returning
* the metrics of the chosen font
*
* Usage: called by PrintDoc() and QueryMaxPages() to set
* up the printer HPS for printing.
*
* Note: HPS must be associated with the printer HDC
*
* Method: Routine creates an HPS for the printer DC that
* uses LOENGLISH for page units. A similar HPS is
* created for the MLE through which the current MLE
* font is translated from device metrics to LOENGLISH
* metrics. The print rectangle is determined by
* finding the printer page size and then subtracting
* the margins and the headers and footers, if any.
* The new font is selected into the printer PS and
* the metrics are returned for the font. The HPS
* is returned with the printer font selected.
*
* Returns: TRUE if successfull, FALSE if not
*
\****************************************************************/
BOOL CalcPrintArea(hpsPrinter, prclPrintArea, pfm)
HPS hpsPrinter; /* INPUT: HPS associated with the printer DC */
PRECTL prclPrintArea; /* OUTPUT: buffer for the print rectangle */
PFONTMETRICS pfm; /* OUTPUT: pointer to buffer for fontmetrics of
printer font */
{
HPS hpsMLE;
SIZEL szl;
FATTRS fattr;
FONTMETRICS fm;
HDC hdcPrinter;
szl.cx = szl.cy = 0L;
/* get the handle to the HDC of the printer */
hdcPrinter = GpiQueryDevice(hpsPrinter);
if(!hdcPrinter)
return FALSE;
/* get width and height of paper and convert those points to page units */
if(!DevQueryCaps(hdcPrinter, CAPS_WIDTH, 2L, (PLONG)&szl))
return FALSE;
if(GpiConvert(hpsPrinter, CVTC_DEVICE, CVTC_WORLD, 2L, (PPOINTL)&szl) ==
GPI_ERROR)
return FALSE;
/*--------------------------------------------------------------*\
* prclPrintArea will be set to the rectangle that encompasses
* the area of the page where the text will be printed. The
* rectangle is defined to be the rectangle of the page minus
* the margins on each side. For example, the print area
* on a page using the default margins will be the rectangle
* that is one inch from the left, bottom, right, and top
* edges of the papaer.
*
* This routine creates the print area rectangle in world
* coordinates. Since the printer HPS was created in LOENGLISH
* page units, each point of world coordinate space equals
* .01 inch, or 100 points equals one inch. The left and
* bottom sides of the print area rectangle are equal to the
* the number of inches of the left and bottom margins times
* 100. The top and right margins can also be calculated by
* multiplying the margin size * 100, but then that distance
* must be subtracted from the width and height of the paper.
*
* The FIXEDTOWORLD() macro, as defined above, converts a
* FIXED integer value into LOENGLISH world coordinates. If
* you change either the page units of the printer HPS or the
* scale of the margins, you must changed FIXEDTOWORLD() to
* translate the margin dimensions to fit the new parameters.
*
\*--------------------------------------------------------------*/
prclPrintArea->xLeft = FIXEDTOWORLD(pgsd.fxLeftMargin);
prclPrintArea->yBottom = FIXEDTOWORLD(pgsd.fxBottomMargin);
prclPrintArea->xRight = szl.cx - FIXEDTOWORLD(pgsd.fxRightMargin);
prclPrintArea->yTop = szl.cy - FIXEDTOWORLD(pgsd.fxTopMargin);
/* get a PS for the MLE and set it to be LOENGLISH also.
*
* Note: only GpiSetPS is checked for an error return. That is
* because if the WinGetPS() function returns an error, hpsMLE
* will be NULL. If hpsMLE is NULL, then GpiSetPS() will fail.
* So, GpiSetPS() will fail if WinGetPS() fails, so we only need
* to check the return code from GpiSetPS()
*/
hpsMLE = WinGetPS(hwndMLE);
if(GpiSetPS(hpsMLE, &szl, PU_LOENGLISH) == GPI_ERROR)
return FALSE;
/* get font information from MLE and create similar font on printer */
WinSendMsg(hwndMLE, MLM_QUERYFONT, MPFROMP(&fattr), NULL);
/* convert font information to LOENGLISH by setting the font into
* the LOENGLISH PS we created for the MLE, getting the
* fontmetrics, and then updating the fattr structure.
*
* Note: only GpiSetCharSet's return value is checked, because if
* the GpiCreateLogFont call fails, the LCID passed to GpiSetCharSet
* will be invalid and GpiSetCharSet will fail as well.
*/
GpiCreateLogFont(hpsMLE, (PSTR8)fattr.szFacename, FONTLCID, &fattr);
if(GpiSetCharSet(hpsMLE, FONTLCID) == GPI_ERROR)
return FALSE;
if(GpiQueryFontMetrics(hpsMLE,
(ULONG)sizeof(FONTMETRICS),
(PFONTMETRICS)&fm) == GPI_ERROR)
return FALSE;
/* delete font and release MLE HPS. Return values are not checked for
* error because a failure in the clean up of the HPS will not
* adversely affect this routine
*/
GpiSetCharSet(hpsMLE, 0);
GpiDeleteSetId(hpsMLE, FONTLCID);
WinReleasePS(hpsMLE);
/* The FATTRS structure was filled with the information for the font
* of the MLE. The height and width of the font is in device
* coordinates, however. Since we created the same font on a
* PU_LOENGLISH HPS, we now can get the height and width in
* PU_LOENGLISH page units. These values are contained in the
* fm structure. We replace the height and width of the font with
* the PU_LOENGLISH equavalents and set the lMatch to 0, so that
* GPI will look through all printer fonts for the font that most
* closely matches the size of the font used on the screen.
*/
fattr.lMaxBaselineExt = fm.lMaxBaselineExt;
fattr.lAveCharWidth = fm.lAveCharWidth;
fattr.lMatch = 0L;
/* Create the font on the printer HPS.
*
* Note: if GpiCreateLogFont() returns 1, then an exact match for
* the font desired was not found. This application does not care
* if the font matches exactly or not, but your application might.
* If GpiCreateLogFont() returns 0, and error occurred, which will
* be caught when we attempt GpiSetCharSet() with this font's LCID
*/
GpiCreateLogFont(hpsPrinter, (PSTR8)fattr.szFacename, FONTLCID, &fattr);
if(GpiSetCharSet(hpsPrinter, FONTLCID) == GPI_ERROR)
return FALSE;
/* get printer font information for return in pfm */
if(GpiQueryFontMetrics(hpsPrinter,
(LONG)sizeof(FONTMETRICS),
pfm) == GPI_ERROR)
return FALSE;
/*--------------------------------------------------------------*\
* If a header or footer is present, it will take two lines -
* one for the text and one blank line. Since rclPrintArea
* is the rectangle where the document text is to be printed,
* the two lines of the header or footer are removed from
* the current print area. The top and bottom side are
* adjusted accordingly.
*
* When the header and footer are printed, they are printed
* two lines above and below the print area.
*
\*--------------------------------------------------------------*/
/* if header is present, adjust top for it */
if(pgsd.pszHeader[0])
prclPrintArea->yTop -= (pfm->lMaxBaselineExt +
pfm->lExternalLeading) * 2L;
/* if footer is present, adjust bottom for it */
if(pgsd.pszFooter[0])
/*--------------------------------------------------------------*\
* A line of text has a height equal to the MaxBaselineExt
* followed by the ExternalLeading. Since the footer is
* printed on the bottom of the page, the ExternalLeading
* following the footer is not necessary. The height of the
* footer, then, is calculated as the sum of the heights
* of the text line, the blank line, and the External Leading *
* between them.
*
\*--------------------------------------------------------------*/
prclPrintArea->yBottom += (pfm->lMaxBaselineExt * 2L) +
pfm->lExternalLeading;
return TRUE;
} /* CalcPrintArea() */
/****************************************************************\
* Determines the maximum number of pages in the document
*--------------------------------------------------------------
*
* Name: QueryMaxPages()
*
* Purpose: returns the maximum number of pages in a file
*
* Usage: called by Print() to initialize the prtd.cPages
* field
*
* Method:
*
* Returns: Number of pages in the document or QMP_ERROR if an error
*
\****************************************************************/
SHORT QueryMaxPages(VOID)
{
HDC hdcPrinter;
FONTMETRICS fm;
RECTL rclPrintArea;
HPS hpsPrinter;
SIZEL szl;
LONG lLines, lLinesPerPage;
/*--------------------------------------------------------------*\
* Code to determine the number of pages in the document
* to be printed
\*--------------------------------------------------------------*/
/* create an info dc for the printer */
hdcPrinter = DevOpenDC(hab,
OD_INFO,
"*",
DEVDATACOUNT,
(PDEVOPENDATA)&prtd.ppsd->dop,
(HDC)NULL);
if(!hdcPrinter)
return QMP_ERROR;
/* create a ps for the DC. Use english page units so we can
measure the margins easily */
szl.cx = szl.cy = 0L;
hpsPrinter = GpiCreatePS(hab,
hdcPrinter,
&szl,
PU_LOENGLISH | GPIT_NORMAL | GPIF_DEFAULT | GPIA_ASSOC);
if(!hpsPrinter)
return QMP_ERROR;
if(!CalcPrintArea(hpsPrinter, &rclPrintArea, &fm))
return QMP_ERROR;
/* get number of lines in the document */
lLines = (LONG)WinSendMsg(hwndMLE, MLM_QUERYLINECOUNT, NULL, NULL);
/* calculate the number of lines per page */
lLinesPerPage = (rclPrintArea.yTop - rclPrintArea.yBottom) /
(fm.lMaxBaselineExt + fm.lExternalLeading);
GpiAssociate(hpsPrinter, NULL);
GpiDestroyPS(hpsPrinter);
DevCloseDC(hdcPrinter);
return(SHORT)(lLines / lLinesPerPage) + 1;
} /* QueryMaxPages() */
/****************************************************************\
* Performs the actual printing of a document
*--------------------------------------------------------------
*
* Name: PrintDoc(hdc)
*
* Purpose: Performs the printing of a file
*
* Usage: called by Print() to actually print the file
*
* Method: This routine prints each line of the MLE. If a
* line is longer than the page width, then the line
* is clipped to the margin; it is not wrapped to
* the next line.
*
* Note: this routine should NOT close the printer DC
*
* Returns: TRUE if successfull, FALSE if not
*
\****************************************************************/
BOOL PrintDoc(hdc)
HDC hdc; /* hdc of the printer */
{
USHORT usSpoolId, usNumCopies;
LONG lOutData, lOffset = 0L, lFileEnd, lLine, lT, lPageLines;
LONG lLinesPerPage, lLineHeight, lStartOffset, lTotalLines;
RECTL rclPrintArea, rclText;
HPS hpsPrinter;
SIZEL szl;
PVOID pvBuf;
FONTMETRICS fm;
POINTL ptl;
BOOL fRet = TRUE;
/*--------------------------------------------------------------*\
* Creates a PS for the DC. Note that you can associate an
* existing HPS to the DC here instead if you are using
* retained graphics in the HPS. The PS must use PU_LOENGLISH
* page units since the CalcPrintArea() routine expects the
* the PS to use them.
\*--------------------------------------------------------------*/
szl.cx = szl.cy = 0L;
hpsPrinter = GpiCreatePS(hab,
hdc,
&szl,
PU_LOENGLISH | GPIT_NORMAL | GPIF_DEFAULT | GPIA_ASSOC);
if(!hpsPrinter)
return FALSE;
if(!CalcPrintArea(hpsPrinter, &rclPrintArea, &fm)) {
fRet = FALSE;
goto PrintDocExit;
}
/*--------------------------------------------------------------*\
* Note: CalcPrintArea() has created the printer font on
* hpsPrinter and selected the font. It has also returned
* the metrics for the font in fm. rclPrintArea is the
* rectangle bounding the print area of the page, excluding
* the header and footer.
\*--------------------------------------------------------------*/
/*--------------------------------------------------------------*\
* rclText is set to the rectangle that will encompass one
* line of output. Its left and right sides are set to the
* margins of the paper, namely the left and right sides of
* the print area as represented by rclPrintArea. Its top
* will be repositioned for each line of output and its
* bottom will always be set to one line's height below the
* top. All text output will be clipped to this rectangle.
\*--------------------------------------------------------------*/
/* set rectangle for each text line to have its left and right sides
equal to the left and right margin of the print area */
rclText.xLeft = rclPrintArea.xLeft;
rclText.xRight = rclPrintArea.xRight;
/* calculate the height of each printed line, including any space
(leading) between the lines */
lLineHeight = fm.lMaxBaselineExt + fm.lExternalLeading;
/* calculate the number of lines that fit on a page */
lLinesPerPage = (rclPrintArea.yTop - rclPrintArea.yBottom) /
lLineHeight;
/* If our page size is too small to print even one line, then
abort the print job */
if(lLinesPerPage < 0L) {
MessageBox(hwndMLE, IDMSG_PRINTAREATOOSMALL, MB_OK | MB_ERROR, FALSE);
goto PrintDocExit;
}
/* get the length of the output file */
lFileEnd = (LONG)WinSendMsg(hwndMLE, MLM_QUERYTEXTLENGTH, NULL, NULL);
/* create a buffer to hold the file */
DosAllocMem((PPVOID) &pvBuf, lFileEnd, fALLOC);
/* set up the buffer for the MLE text */
WinSendMsg(hwndMLE,
MLM_SETIMPORTEXPORT,
MPFROMP(pvBuf),
MPFROMLONG(lFileEnd));
/* if printing all pages, set file offset to beginning of file *\
* and set the last line to the end of the file. If printing
* only certain pages, calculate the beginning and ending line
\* based on the pages */
if(prtd.flFlags & PRTF_ALLPAGES) {
lStartOffset = 0L;
lTotalLines = (LONG)WinSendMsg(hwndMLE,
MLM_QUERYLINECOUNT,
NULL,
NULL);
} else {
/* set lStartOffset to the beginning of the first line of the
first page set to print */
lT = (LONG)(prtd.iFrom - 1L) * lLinesPerPage;
if(lT < 0L); /* lT should never be less than 0, so this is */
lT = 0L; /* purely defensive programming */
/* lT is the number of lines in all pages from the beginning
of the document to the first page printed */
lStartOffset = (LONG)WinSendMsg(hwndMLE,
MLM_CHARFROMLINE,
MPFROMLONG(lT),
NULL);
/* set lTotalLines to the number of lines in the pages between the
starting and ending */
lTotalLines = (LONG)(prtd.iTo - prtd.iFrom + 1) * lLinesPerPage;
lFileEnd = (LONG)WinSendMsg(hwndMLE,
MLM_QUERYLINECOUNT,
NULL,
NULL);
/* if last line is beyond end of file, set the last line to
be the end of file */
if(lTotalLines + lT > lFileEnd)
lTotalLines = lFileEnd - lT;
}
/* print one document for each number of copies */
usNumCopies = (USHORT)prtd.cCopies;
while(usNumCopies--) {
/* set file offset variable to the beginning of the lines
being printed; set line variable to the number of lines
to be printed */
lLine = lTotalLines;
lOffset = lStartOffset;
/* send a DEVESC_STARTDOC to start the printing */
if(DevEscape(hdc,
DEVESC_STARTDOC,
(LONG)MAXNAMEL,
(PBYTE)szAppName,
(PLONG)NULL,
(PBYTE)NULL) != DEV_OK) {
MessageBox(hwndMain,
IDMSG_CANNOTOPENPRINTER,
MB_OK | MB_ERROR,
FALSE);
fRet = FALSE;
goto PrintDocExit;
}
while(lLine) {
/* if we have header text, print the header at the top of the page */
if(pgsd.pszHeader[0]) {
/* set text rectangle to two lines above print area since
we reserved two lines for it in CalcPrintArea() */
rclText.yTop = rclPrintArea.yTop + (lLineHeight * 2L);
rclText.yBottom = rclText.yTop - fm.lMaxBaselineExt;
ptl.x = rclText.xLeft;
ptl.y = rclText.yTop - fm.lMaxAscender;
GpiMove(hpsPrinter, &ptl);
GpiCharStringPos(hpsPrinter,
&rclText,
CHS_CLIP,
pgsd.cchHeader,
pgsd.pszHeader,
NULL);
}
/*==============================================================*\
* Now print each line of text in the print area
\*==============================================================*/
/*--------------------------------------------------------------*\
* The top of rclText is set here to the top of the print
* area while the bottom is set to the height of one line
* below the top. The measurement used to position the
* bottom is not the lLineHeight variable, but rather the
* actual height of the characters in the font as given by
* the lMaxBaselineExt field of the fontmetrics structure.
* Thus, rclText will be the height of the characters in the
* line and will not include any external leading.
\*--------------------------------------------------------------*/
rclText.yTop = rclPrintArea.yTop;
rclText.yBottom = rclText.yTop - fm.lMaxBaselineExt;
/*--------------------------------------------------------------*\
* ptl will be the beginning point of the text string. The
* x coordinate of the point will always be on the left side
* of the print line, as given the rclText.xLeft. The y
* coordinate is set to the baseline of the font. The
* baseline is the line at the bottom of all characters,
* excluding any descenders. The baseline is determined by
* taking the top of the text line and subtracting the height
* of the ascender of the font. Thus:
*
* **** === ===
* * * | |
* * * | ascender |
* * * | | MaxBaselineExt
* baseline --- *****------=== |
* * | |
* * * | descender |
* **** === ===
*
* ptl.y is set to the baseline coordinate by subracting the
* Ascender of the font (fm.lMaxAscender) from the top of
* the text rectangle (rclText.yTop).
*
\*--------------------------------------------------------------*/
ptl.x = rclText.xLeft;
ptl.y = rclText.yTop - fm.lMaxAscender;
/* set lPageLines to the number of lines on the page. If
there are fewer lines left in the document, then set
it to the number of lines left */
lPageLines = min(lLinesPerPage, lLine);
while(lPageLines--) {
ptl.y = rclText.yTop - fm.lMaxAscender; /* re-set baseline */
/* get current line from MLE */
lT = WinSendMsg(hwndMLE,
MLM_QUERYFORMATLINELENGTH,
MPFROMLONG(lOffset),
NULL);
/* lT is the length of the current line, including the
CR/LF pair at the end */
lOutData = lT - 2L; /* length of line minus CR/LF pair */
/* get the text from the MLE */
WinSendMsg(hwndMLE,
MLM_EXPORT,
MPFROMP(&lOffset),
MPFROMP(&lT));
/* print the string */
if(GpiMove(hpsPrinter, &ptl) == GPI_ERROR) {
fRet = FALSE;
goto PrintDocExit;
}
if(GpiCharStringPos(hpsPrinter,
&rclText,
CHS_CLIP,
lOutData,
pvBuf,
NULL) == GPI_ERROR) {
fRet = FALSE;
goto PrintDocExit;
}
/* subtract one from the lines remaining */
lLine--;
/* move offset pointer to the beginning of next line */
lOffset += lT;
/* set the top of the text rectangle to the top of the
next line. Set the bottom to a character's height
below the top */
rclText.yTop = rclText.yBottom - fm.lExternalLeading;
rclText.yBottom = rclText.yTop - fm.lMaxBaselineExt;
} /* while(lPageLines) */
/* add footer if necessary */
if(pgsd.pszFooter[0]) {
/* set text rectangle to two lines below print area since
we reserved two lines for it in CalcPrintArea(). Refer
to the CalcPrintArea() function for an explanation of
the height of the footer */
rclText.yBottom = rclPrintArea.yBottom -
((fm.lMaxBaselineExt * 2L) +
fm.lExternalLeading);
rclText.yTop = rclText.yBottom + fm.lMaxBaselineExt;
/* set ptl to baseline */
ptl.y = rclText.yTop - fm.lMaxAscender;
/* print the footer text */
if(GpiMove(hpsPrinter, &ptl) == GPI_ERROR) {
fRet = FALSE;
goto PrintDocExit;
}
if(GpiCharStringPos(hpsPrinter,
&rclText,
CHS_CLIP,
pgsd.cchFooter,
pgsd.pszFooter,
NULL) == GPI_ERROR) {
fRet = FALSE;
goto PrintDocExit;
}
}
/* send a DEVESC_NEWFRAME to end this page */
if(DevEscape(hdc,
DEVESC_NEWFRAME,
0L,
(PBYTE)NULL,
(PLONG)NULL,
(PBYTE)NULL) != DEV_OK) {
fRet = FALSE;
goto PrintDocExit;
}
} /* while(lLine) */
/* send a DEVESC_ENDDOC to end the printing */
if(DevEscape(hdc,
DEVESC_ENDDOC,
0L,
(PBYTE)NULL,
(PLONG)&lOutData,
(PBYTE)&usSpoolId) != DEV_OK) {
fRet = FALSE;
goto PrintDocExit;
}
} /* while(usNumCopies) */
PrintDocExit:
/* free the text buffer and destroy the PS */
GpiFreeMem((PVOID)pvBuf);
GpiAssociate(hpsPrinter, NULL);
GpiDestroyPS(hpsPrinter);
return fRet;
} /* PrintDoc() */
#endif
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.