Subject:     Source Code: QuickDraw GX printing support with WASTE
Sent:        12/07  12:00 PM
Received:    12/07  1:32 PM
From:        Xiaolin Zhao, xlz@Chem.LSA.umich.edu
To:          Waste, waste@umich.edu

This is the GDGX printing code that I have been working on. Right now it seems all working. The code is based on the "more printing code" on the WASTE webpage, the QuickDraw part is especially so. But the GX printing is somewhat different because of the GX architecture. 

For a full implementation of GX printing, (apple event, initialization, etc. ) please look at Apple's developer CD or the GX printing webpage. 

The printing structure is added in the DocumentRecord which is initialized in the CreateWindow(). also refer the Apple's developer CD for details. I did not add the pageformat, page, number of pages, etc with the DocumentRecord, because my application does not support custom formating for separate pages. For printing, the printing record is enough, but if you want to add the custom formating for each page, you have to add more code to the WASTE demo (repagenation, etc). 

struct DocumentRecord
{
   WindowRef      owner;               // the window
   ScrollBarPair     scrollBars;          // its scroll bars
   WEReference       we;                  // its WASTE instance
   Handle         fileAlias;              // alias to associated file
   // xlz: addition for printing record
   gxJob       documentJob;            // Job bound to this document.
   THPrint        documentPrintHdl;       // Print Record bound to this document.
   // xlz: end
};  // DocumentRec

Cheers,

Xiaolin Zhao

Updated 12/12/95:
	Changes made: in the QD printing, there is a minor problem with 
printing more than one page, this is fixed;  in the GX printing, the 
screen redraw while printing is disabled by moving the window port off 
screen temporerily. These 2 printing loop has been tested on a C660av 
with/without GX 1.1.2. There is no signs of the "printing line" problems. 


*****************

#define     kGraphicsHeapSize ((long) 300 * 1024)

Boolean           gGXIsPresent;                 // Using QuickDraw GX is available?
Boolean           gInPrintDialog;                  // Is a QDGX print dialog displayed?
gxGraphicsClient     gClient;                   // Our app's QDGX graphics client.

typedef struct MySpoolDataRec {
      gxRectangle    pageArea;               // Page rectangle.
      gxViewPort     printViewPort;          // ViewPort we're printing with.
} MySpoolDataRec, *MySpoolDataPtr;

/************************************************************
 DoPrint - This routine displays the QuickDraw GX
  or non-QuickDraw GX "Print" dialog, and then prints the
  document.

*************************************************************/

void DoPrint( void )
{
   OSErr          err = noErr;
   gxEditMenuRecord     editMenuRec;
   gxDialogResult    result;
   WindowRef         window = FrontWindow();
   DocumentHandle       hDocument;
   THPrint     printHandle;
   gxJob    printJob;
   
   hDocument = (DocumentHandle) GetWRefCon(window);

   if (gGXIsPresent)
   {
      printJob = (*hDocument)->documentJob;
/*
   If QuickDraw GX is present, fill in the location of the application's
   Edit menu items, enable/disable appropriate menu items, and display
   the Print dialog.  If the user clicks the Print button, print using
   QuickDraw GX.
*/
      editMenuRec.editMenuID  = kMenuEdit;
      editMenuRec.cutItem     = kItemCut;
      editMenuRec.copyItem = kItemCopy;
      editMenuRec.pasteItem   = kItemPaste;
      editMenuRec.clearItem   = kItemClear;
      editMenuRec.undoItem = kItemUndo;
   
      MyAdjustMenusForPrintDialogs(true);
      result = GXJobPrintDialog(printJob, &editMenuRec);
      MyAdjustMenusForPrintDialogs(false);

      if (result == gxOKSelected)
         err = MyGXPrintLoop(hDocument);
   }
   else
   {
      printHandle = (*hDocument)->documentPrintHdl;
/*
   If QuickDraw GX is NOT present, open the printer driver, and put up the
   old Print dialog.  If the user clicks the Print button, print using
   the Printing Manager.
*/
      PrOpen();
      PrValidate(printHandle);
      if (PrJobDialog(printHandle))
         err = MyQDPrintLoop(hDocument);  // Print using QuickDraw.
      PrClose();
   }

   return;
}

/************************************************************
  DoPrintOneCopy - This routine sets up job collection items
  so that we'll print one copy of the passed document, then
  it jumps into our printing routine.  This function is only
  called when QuickDraw GX is running.

*************************************************************/

void DoPrintOneCopy(void)
{
   OSErr             err;
   Collection           jobCollection;
   gxCopiesInfo         copiesInfo;
   gxFileDestinationInfo   destInfo;
   gxPageRangeInfo         pageRangeInfo;
   Ptr                  oldCopiesInfo = nil, oldPageRangeInfo = nil, oldDestInfo = nil;
   long              oldCopiesSize, oldPageRangeInfoSize, oldDestInfoSize;
   WindowRef         window = FrontWindow();
   DocumentHandle    hDocument = (DocumentHandle) GetWRefCon(window);
   THPrint           printHandle=nil;

   if (gGXIsPresent) {
      // Get the job collection and set it up to print one copy

      jobCollection = GXGetJobCollection((*hDocument)->documentJob);

      // Set number of copies to 1.
   
      copiesInfo.copies = 1;
      err = MyReplaceCollectionItem(&copiesInfo, sizeof(gxCopiesInfo),
                          gxCopiesTag, gxPrintingTagID,
                          jobCollection, &oldCopiesInfo, &oldCopiesSize);
      nrequire(err, ReplaceCopies_error);

   // Set page range to "all".

      pageRangeInfo.simpleRange.optionChosen = gxDefaultPageRange;
      pageRangeInfo.minFromPage = 1;
      pageRangeInfo.simpleRange.fromPage = 1;
      pageRangeInfo.maxToPage = 9999;
      pageRangeInfo.simpleRange.toPage = 9999;
      pageRangeInfo.simpleRange.printAll = true;
      err = MyReplaceCollectionItem(&pageRangeInfo, sizeof(gxPageRangeInfo),
                             gxPageRangeTag, gxPrintingTagID,
                             jobCollection, &oldPageRangeInfo, &oldPageRangeInfoSize);
      nrequire(err, ReplacePageRange_error);
   
   // Set destination to "printer".
   
      destInfo.toFile = false;
      err = MyReplaceCollectionItem(&destInfo, sizeof(gxFileDestinationInfo),
                             gxFileDestinationTag, gxPrintingTagID,
                             jobCollection, &oldDestInfo, &oldDestInfoSize);
      nrequire(err, ReplaceDestination_error);
   
   // Print one copy of our document.
      
      err = MyGXPrintLoop(hDocument);
   
   /*
      Restore original number of copies, page range, and output
      destination in case anybody uses that info.
   */
      
ReplaceDestination_error:
      MyReplaceCollectionItem(oldDestInfo, oldDestInfoSize,
                     gxFileDestinationTag, gxPrintingTagID,
                     jobCollection, nil, nil);
ReplacePageRange_error:
      MyReplaceCollectionItem(oldPageRangeInfo, oldPageRangeInfoSize,
                     gxPageRangeTag, gxPrintingTagID,
                     jobCollection, nil, nil);
ReplaceCopies_error:
      MyReplaceCollectionItem(oldCopiesInfo, oldCopiesSize,
                     gxCopiesTag, gxPrintingTagID,
                     jobCollection, nil, nil);

// Dispose of the pointers that MyReplaceCollectionItem created.

      if (oldCopiesInfo)
         DisposePtr(oldCopiesInfo);

      if (oldPageRangeInfo)
         DisposePtr(oldPageRangeInfo);

      if (oldDestInfo)
         DisposePtr(oldDestInfo);
   }
   else {
      printHandle = (*hDocument)->documentPrintHdl;
      (*printHandle)->prJob.iFstPage = 1;
      (*printHandle)->prJob.iLstPage = 9999;
      (*printHandle)->prJob.iCopies = 1;
      err = MyQDPrintLoop(hDocument);
   }

   return;
}



/************************************************************
  MyQDPrintLoop - This is our non-QuickDraw GX printing
  routine.

*************************************************************/

OSErr MyQDPrintLoop(DocumentHandle whichDocument)
{
	OSErr		err = noErr;
	THPrint		hPrint;
	TPPrPort		printPort;
	short		copy, iFstPage, iLstPage, gDotPerInch;
        long       lineCount;
        long       pageHeight, topLine=0, bottomLine=0, height, 
pageHandled=1, pageNumber;
        short      printError, lineHeight;
        Boolean    donePrinting, firstPage = true;
        GrafPtr    oldTextPort, oldPort;
        Boolean    oldActiveFlag;
        LongRect   pageRect;
        LongRect   oldDestRect, oldViewRect, viewRect;
        DialogRef  dialog;
        WindowRef window;
        WEReference text;
        float insetLeft = (*gDefault)->printmargin.Left, insetRight = 
(*gDefault)->printmargin.Right;
        float insetTop = (*gDefault)->printmargin.Top, insetBottom = 
(*gDefault)->printmargin.Bottom;
        FontInfo		fontInfo;

	hPrint = (*whichDocument)->documentPrintHdl;
	text = (*whichDocument)->we;
	window = (*whichDocument)->owner;

	GetPort(&oldPort);
	SetPortWindowPort(window);
	// save old variables so they can be restored after printing.
	WEGetInfo(wePort, &oldTextPort, text);
	WEGetDestRect(&oldDestRect, text);
	WEGetViewRect(&oldViewRect, text);
	oldActiveFlag = WEIsActive(text);

	WEFeatureFlag(weFOutlineHilite, weBitClear, text);
	WEFeatureFlag(weFDrawOffscreen, weBitClear, text);

	if (oldActiveFlag) {
		SetPort(oldTextPort);
		WEDeactivate(text);
		// Make the screen look all nice. 
		WEUpdate(GetWindowPort(window)->visRgn, text);
	}

	// repagenate according printing pref.
	iFstPage = (*hPrint)->prJob.iFstPage;
	if (iFstPage < 1)
		iFstPage = 1;

	iLstPage = (*hPrint)->prJob.iLstPage;
		
	(*hPrint)->prJob.iFstPage = 1;
	(*hPrint)->prJob.iLstPage = 9999;

	// the pixel per inch.
	gDotPerInch = 2*((**hPrint).prInfo.iHRes >> 1);
	
	// Optional: this part is optional, it is to make space my header 
and footer printing. 
	TextFont((*gDefault)->fontandstyle.FontOption);
	TextSize(12);
	TextFace(0);
	TextMode(srcCopy);
	GetFontInfo(&fontInfo);  // only for default font and size 12
	lineHeight = fontInfo.ascent + fontInfo.descent + fontInfo.leading;
	// xlz: end of the header and footer pre-calculation.

	// make the print rect as the WE rect
	WERectToLongRect(&((**hPrint).prInfo.rPage), &pageRect);
	// set the destination and viewing rect as the page setup rect 
through pref printing magin setting.
	pageRect.left += insetLeft * gDotPerInch;
	pageRect.top += insetTop * gDotPerInch;
	pageRect.right -= insetRight * gDotPerInch;
	pageRect.bottom -= insetBottom * gDotPerInch;
	
	// Optional: inset the rect to make space for the header and 
footer. 
	if ((*gDefault)->printmargin.DocTitle || 
(*gDefault)->printmargin.TimeStamp) {
		// the top of the printing region is scaled down. 
		pageRect.top +=  2*lineHeight + 8;
	}
	if ((*gDefault)->printmargin.PageNumbered) {
		// the bottom of the printing region is scaled up. 
		pageRect.bottom -= 2*lineHeight - 4;
	}
	
	// set the WE view and dect rect.
	WESetDestRect(&pageRect, text);
	WESetViewRect(&pageRect, text);
	// recalculate the WE instanst. 
	WECalText(text);
	// get the total line number of the Text WE instant.
	lineCount = WECountLines(text);
	// calculate the printer rect height from the page setup record.
	pageHeight = pageRect.bottom - pageRect.top;
	// prepare for next step counting of the bottomLine.
	
	// loop to designated page for print.
	while (pageHandled < iFstPage ) {
		while (WEGetHeight(topLine, bottomLine, text) <= 
pageHeight && bottomLine <= lineCount) {
			bottomLine++;
		}
		if (bottomLine-topLine > 1) // wešve overshot, unless we 
have only 1 line.
			bottomLine--;
		// exactly the pixel height for the text page based 
previous calculation.
		height = WEGetHeight(topLine, bottomLine, text);
		// set the view rect according above calculation.
		WEGetViewRect(&viewRect, text);
		viewRect.bottom = viewRect.top + height;
		WESetViewRect(&viewRect, text);
		// redraw the whole viewRect on the printer
		WEUpdate(NULL, text);
		// now let scroll to next page
		WEScroll(0, -height, text);
		// now assign the topLine to the bottomLine so that next 
page printing will start with new line.
		topLine = bottomLine;
		pageHandled++;
	}  // while (pageHandled < iFstPage ) 


	// get a dialog to inform user printing is in process and wait or 
cancel by command-period
	// use dialog so that it can be moved around while wait for 
print. 
	dialog = GetNewDialog(kDialogPrintInProcess, NULL, (WindowRef) 
-1); 
	// xlz: 
	if (dialog == NULL) {
		Alert(kAlertPrintMemError, GetMyStandardDialogFilter() ); 
		PrClose(); // clean up.
		return kAlertPrintMemError;
	}
	// xlz: end
	DrawDialog(dialog);

	//Loop for the number of copies we need to make, opening a 
document and a page for each.

	for (copy = 1; !err && (copy <= (*hPrint)->prJob.iCopies); 
copy++) 
	{
		pageNumber = pageHandled;
		
		printPort = PrOpenDoc(hPrint, nil, nil);

		if (!(err = PrError())) {
			// Set up the waste record for the new port.
			SetPort((GrafPtr) printPort);
			donePrinting = false;
			while (pageNumber <= iLstPage && !donePrinting)
			{
				PrOpenPage(printPort, NULL);
				if (PrError() == noErr)
				{
					// now set the print port as the 
grafport to draw text on.
					if (firstPage) {
						firstPage = false;
						SetPort((GrafPtr) printPort);
						SetFractEnable(TRUE);  // 
for purty printin'
						WESetInfo(wePort, 
&printPort, text);
						// fill the printing rect 
with empty back ground.
						EraseRect(&((*hPrint)->prInfo.rPage));
					}
					
					// Optional:  print headers: 
document title and time stamps. 
					if 
((*gDefault)->printmargin.DocTitle || (*gDefault)->printmargin.TimeStamp) {
						DrawHeader(whichDocument, (*hPrint)->prInfo.rPage);
					}
					
					// calculate how many line/page 
if there is more than one page or the total line if there is only one 
page. 
					while (WEGetHeight(topLine, 
bottomLine, text) <= pageHeight && bottomLine <= lineCount) {
						bottomLine++;
					}
					if (bottomLine-topLine > 1) // 
wešve overshot, unless we have only 1 line.
						bottomLine--;
					// exactly the pixel height for 
the text page based previous calculation.
					height = WEGetHeight(topLine, 
bottomLine, text);
					// set the view rect according 
above calculation.
					WEGetViewRect(&viewRect, text);
					viewRect.bottom = viewRect.top + height;
					WESetViewRect(&viewRect, text);
					// redraw the whole viewRect on 
the printing port
					WEUpdate(NULL, text);

					// Optional: draw footer: page numbered.
					if 
((*gDefault)->printmargin.PageNumbered) {
						DrawFooter(pageNumber, (*hPrint)->prInfo.rPage);
					}

					// now assign the topLine to the 
bottomLine so that next page printing will start with new line.
					topLine = bottomLine;

					pageNumber++;
				}		
				PrClosePage(printPort);
				if (PrError() != noErr)
					break;
	
				// are we reaching the last line yet?
				if (topLine >= lineCount) {
					donePrinting = TRUE;
				}
				else {
					// now scroll to next page
					WEScroll(0, -height, text);
				}
			}
		} // end if (!(err = PrError()))
		PrCloseDoc(printPort);
	}
	
OnExit:
	// reset old variables in the waste record
	WESetInfo(wePort, &oldTextPort, text);
	WESetDestRect(&oldDestRect, text);
	WESetViewRect(&oldViewRect, text);

	if (PrError() == noErr)
	{
		// Check if print job is spooled
		if ((*hPrint)->prJob.bJDocLoop == bSpoolLoop)
		{
			TPrStatus  printing_status;
                                        
				PrPicFile(hPrint, NULL, NULL, NULL, &printing_status);
		}
	}

	SetFractEnable(FALSE);
	DisposeDialog(dialog);
	SetPort(oldPort);
	if (oldActiveFlag)
		WEActivate(text);
	WEFeatureFlag(weFOutlineHilite, weBitSet, text);
	WEFeatureFlag(weFDrawOffscreen, weBitSet, text);
	WECalText(text);
                
        printError = PrError();

        if (printError != noErr && printError != iPrAbort)
        {
                PrintErrorAlert (printError);
        }
        
	WEUpdate (NULL, text);
	DoUpdate( window );
	
	return err;
} // tested without QX on. It works

/************************************************************
  MyGXPrintLoop - This is our QuickDraw GX printing
  routine.

*************************************************************/

OSErr MyGXPrintLoop(DocumentHandle hDocument)
{
	OSErr			err;
	long				firstPage, lastPage, numPages = 
0, pg, pageHandled =1;
	short			gDotPerInch;
	gxViewPort		printViewPort;
	Point			patStretch = {1,1};
	gxFormat			pageFormat;
	Rect				everywhereRect;
	MySpoolDataRec	spoolData;
	gxJob			printJob;
	Rect				repaginateRect;
	gxRectangle		pageArea, paperArea;
	float insetLeft = (*gDefault)->printmargin.Left, insetRight = 
(*gDefault)->printmargin.Right;
	float insetTop = (*gDefault)->printmargin.Top, insetBottom = 
(*gDefault)->printmargin.Bottom;
	LongRect   pageRect;
        DialogRef  dialog;
        long       lineCount;
        long       pageHeight, topLine=0, bottomLine=0, height;
	LongRect   oldDestRect, oldViewRect, viewRect;
        GrafPtr    oldTextPort, oldPort;
        Boolean    oldActiveFlag, donePrinting, firstPagePrinting;
        WEReference text;
        WindowRef	window;
        Str255		documentTitle;
        FontInfo		fontInfo;
        short 		lineHeight, oldPortLeft, oldPortTop;
        Point		portLeftTop, portRightBottom;

	printJob = (*hDocument)->documentJob;
	text = (*hDocument)->we;
	window = (*hDocument)->owner;
	
	GetPort(&oldPort);
	SetPortWindowPort(window);
	// save old variables so they can be restored after printing.
	WEGetInfo(wePort, &oldTextPort, text);
	WEGetDestRect(&oldDestRect, text);
	WEGetViewRect(&oldViewRect, text);
	oldActiveFlag = WEIsActive(text);

	WEFeatureFlag(weFOutlineHilite, weBitClear, text);
	WEFeatureFlag(weFDrawOffscreen, weBitClear, text);

	if (oldActiveFlag) {
		SetPort(oldTextPort);
		WEDeactivate(text);
		// Make the screen look all nice. 
		WEUpdate(GetWindowPort(window)->visRgn, text);
	}

	// Repaginate our document using the format's page area
	
	pageFormat = GXGetJobFormat(printJob, 1);
			
	GXGetFormatDimensions(pageFormat, &pageArea, &paperArea);

	SetRect(&repaginateRect,
					pageArea.left >>16,
					pageArea.top >>16,
					pageArea.right >>16,
					pageArea.bottom >>16);

	// for QDGX printing, the printing is actually the redraw in the 
viewrect of the screen window port
	// so the gDotPerInch is in fact the screen resolution. QDGX uses 
fixed axis resolution, 72 dpi, says someone.
	// the pixel per half inch.
	gDotPerInch = 72;
	// Optional:  precalculation of the header and footer. 
	TextFont((*gDefault)->fontandstyle.FontOption);
	TextSize(12);
	TextFace(0);
	TextMode(srcCopy);
	GetFontInfo(&fontInfo);  // only for default font and size 9
	lineHeight = fontInfo.ascent + fontInfo.descent + fontInfo.leading;
	
	// make the print rect as the WE rect
	WERectToLongRect(&repaginateRect, &pageRect);
	// set the destination and viewing rect as the page setup rect 
through pref printing magin setting.
	pageRect.left += insetLeft * gDotPerInch;
	pageRect.top += insetTop * gDotPerInch;
	pageRect.right -= insetRight * gDotPerInch;
	pageRect.bottom -= insetBottom * gDotPerInch;
	
	// Optional: inset the WE rect with the previous calculation. 
	if ((*gDefault)->printmargin.DocTitle || 
(*gDefault)->printmargin.TimeStamp) {
		// the top of the printing region is scaled down. 
		pageRect.top +=  2*lineHeight + 8;
	}
	if ((*gDefault)->printmargin.PageNumbered) {
		// the bottom of the printing region is scaled up. 
		pageRect.bottom -= 2*lineHeight - 4;
	}
	
	// set the WE view and dect rect.
	WESetDestRect(&pageRect, text);
	WESetViewRect(&pageRect, text);
	// recalculate the WE instanst. 
	WECalText(text);
	// get the total line number of the Text WE instant.
	lineCount = WECountLines(text);
	// calculate the printer rect height from the page setup record.
	pageHeight = pageRect.bottom - pageRect.top;
	
	// how many pages do we have here?
	while (bottomLine <= lineCount) {
		while (WEGetHeight(topLine, bottomLine, text) <= 
pageHeight && bottomLine <= lineCount) {
			bottomLine++;
		}
		if (bottomLine-topLine > 1) // wešve overshot, unless we 
have only 1 line.
			bottomLine--;
		topLine = bottomLine;
		numPages++;
	}
	// reset for future use. 
	topLine = bottomLine = 0;

	// Calculate the total number of pages to print, and begin 
printing if there are no errors.

	GXGetJobPageRange(printJob, &firstPage, &lastPage);
	err = GXGetJobError(printJob);
	nrequire(err, PageRangeError);

	if (numPages < firstPage) { 
		return -1;
	}
	
	// loop to the designated page number.
	while (pageHandled < firstPage ) {
		while (WEGetHeight(topLine, bottomLine, text) <= 
pageHeight && bottomLine <= lineCount) {
			bottomLine++;
		}
		if (bottomLine-topLine > 1) // wešve overshot, unless we 
have only 1 line.
			bottomLine--;
		// exactly the pixel height for the text page based 
previous calculation.
		height = WEGetHeight(topLine, bottomLine, text);
		// set the view rect according above calculation.
		WEGetViewRect(&viewRect, text);
		viewRect.bottom = viewRect.top + height;
		WESetViewRect(&viewRect, text);
		// redraw the whole viewRect on the printer
		WEUpdate(NULL, text);
		// now let scroll to next page
		WEScroll(0, -height, text);
		// now assign the topLine to the bottomLine so that next 
page printing will start with new line.
		topLine = bottomLine;
		pageHandled++;
	}  

	// get a dialog to inform user printing is in process and wait or 
cancel by command-period
	// use dialog so that it can be moved around while wait for 
print. 
	dialog = GetNewDialog(kDialogPrintInProcess, NULL, (WindowRef) 
-1); 
	// xlz: 
	if (dialog == NULL) {
		Alert(kAlertPrintMemError, GetMyStandardDialogFilter() ); 
		nrequire(kAlertPrintMemError, StartJobFailed);
	}
	// xlz: end
	DrawDialog(dialog);
                
	// Create a new viewport for printing, and set our translator rects
	// to "wide open" so they include all data we're drawing.  For each
	// page we print, call GXStartPage, draw, and call GXFinishPage.

	GetWTitle(window, documentTitle);
	
	GXStartJob(printJob, documentTitle, numPages);
	err = GXGetJobError(printJob);
	nrequire(err, StartJobFailed);

	// Create a new viewport for printing, and set our translator rects
	// to "wide open" so they include all data we're drawing.  For each
	// page we print, call GXStartPage, draw, and call GXFinishPage.

	SetRect(&everywhereRect, 0, 0, 32767, 32767);
	printViewPort = GXNewViewPort(gxScreenViewDevices);

	donePrinting = false;
	firstPagePrinting=true;
	
	SetFractEnable(TRUE);  // for purty printin'

	for (pg = firstPage; (err == noErr) && (pg <= lastPage) 
&&!donePrinting; pg++)
	{
		// Get the page's format and start printing the page.
		GXStartPage(printJob, pg, pageFormat, 1, &printViewPort);								
		err = GXGetJobError(printJob);

		// If there were no errors, set up the Translator, draw 
the QuickDraw
		// data for the current page and remove the Translator.
	
		// Note that, although it looks like it takes a GrafPtr,
		// GXInstallQDTranslator actually requires a pointer to a CGrafPort,
	  	// and will crash if you pass it a true GrafPtr.
		nrequire(err, StartPageFailed);
		spoolData.printViewPort = printViewPort;
		GXGetFormatDimensions(pageFormat, &spoolData.pageArea, nil);

		GXInstallQDTranslator(window,
						  gxDefaultOptionsTranslation,
						  &everywhereRect, &everywhereRect,
						  patStretch, 
						  NewgxShapeSpoolProc(MyPrintAShape),
						  &spoolData);
				
		SetPort(window);

		if (firstPagePrinting) {
			firstPagePrinting = false;
			portLeftTop.h = ((GrafPort*) window)->portRect.left;
			portLeftTop.v = ((GrafPort*) window)->portRect.top;
			portRightBottom.h = ((GrafPort*) window)->portRect.right;
			portRightBottom.v = ((GrafPort*) window)->portRect.bottom;
			LocalToGlobal (&portLeftTop);
			LocalToGlobal (&portRightBottom);
			oldPortLeft = portLeftTop.h;  // in global coord. 
			oldPortTop = portLeftTop.v;
			// To avoid the port redraw while printing, move 
the drawing port out of screen (to the left). 
			MovePortTo(-portRightBottom.h, portLeftTop.v);
		}

		// Optional: Doc title and time stamp printing. 
		if ((*gDefault)->printmargin.DocTitle || 
(*gDefault)->printmargin.TimeStamp) {
			DrawHeader(hDocument, repaginateRect);
		}
		
		// calculate how many line/page if there is more than one 
page or the total line if there is only one page. 
		while (WEGetHeight(topLine, bottomLine, text) <= 
pageHeight && bottomLine <= lineCount) {
			bottomLine++;
		}
		if (bottomLine-topLine > 1) // wešve overshot, unless we 
have only 1 line.
			bottomLine--;
		// exactly the pixel height for the text page based 
previous calculation.
		height = WEGetHeight(topLine, bottomLine, text);
		// set the view rect according above calculation.
		WEGetViewRect(&viewRect, text);
		viewRect.bottom = viewRect.top + height;
		WESetViewRect(&viewRect, text);
		// redraw the whole viewRect on the printing port
		WEUpdate(NULL, text);

		// Optional: draw the page number;
		if ((*gDefault)->printmargin.PageNumbered) {
			DrawFooter(pg, repaginateRect);
		}

		GXRemoveQDTranslator(window, nil);
		// Finish the page.

		GXFinishPage(printJob);

		// now assign the topLine to the bottomLine so that next 
page printing will start with new line.
		topLine = bottomLine;
		// are we reaching the last line yet?
		if (topLine >= lineCount) {
			donePrinting = true; 
		}
		else {
			// now let scroll to next page: this function 
must be after the GX translater, 
			// it will crash  and cause bus error if it is 
with QDGX translator on. 
			WEScroll(0, -height, text);
		}
	}

// Finish printing.
	// restore drawing port position. 
	MovePortTo(oldPortLeft, oldPortTop);

StartPageFailed:

	GXFinishJob(printJob);
	err = GXGetJobError(printJob);
		
	GXDisposeViewPort(printViewPort);

	// reset old variables in the waste record
	WESetInfo(wePort, &oldTextPort, text);
	WESetDestRect(&oldDestRect, text);
	WESetViewRect(&oldViewRect, text);

	SetFractEnable(FALSE);
	DisposeDialog(dialog);
	SetPort(oldPort);
	if (oldActiveFlag)
		WEActivate(text);
	WEFeatureFlag(weFOutlineHilite, weBitSet, text);
	WEFeatureFlag(weFDrawOffscreen, weBitSet, text);
	WECalText(text);
                
	WEUpdate (NULL, text);
	DoUpdate( window );
	
StartJobFailed:
PageRangeError:
	return err;
} // tested with QDGX on, it works!
