![]() A Print Loop That Cares...
Revised by Ingrid Kelly |
CONTENTSThe Old Way of Handling PrintingThe New Way: a C Print Loop Checking For Error Conditions While Printing Error Messages Summary Change History |
This Technote,
originally Technote PR 10 - A Printing Loop That Cares,
discusses how and why your application should add a generic printing
loop in order to be compatible with today's printer drivers.
This revised Technote reflects the current Macintosh Printing Manager
and discusses proper opening and closing of the Macintosh Printing
Manager with calls to |
The Old Way of Handling PrintingIn the past (pre-System 7), Apple recommended that developers call
For instance, the user could open the Chooser at any time and change the current printer driver without the current application's knowledge. If an application followed the old philosophy and a user changed the current printer driver while running the application, the next time the user attempted to print, the wrong driver would be open, the Printing Manager would not be able to find the necessary resources, and the user would get an error. The original Technote described a method of printing that allowed applications to circumvent all of these problems; this revised Note shows you an even better method. |
The New Way: a C Print LoopThe following code snippet,
You should use the error-checking method in Step #3 to make sure the Print Manager closes properly and that all temporary memory is released. |
Note: Apple Developer Technical Support currently recommends that applications open and close the printer driver each time your application uses the Printing Manager. We also highly recommend appropriate error checking, as demonstrated in this snippet of code. |
The PrintStuff Print Loop |
void PrintStuff () { GrafPtr oldPort; short copies, firstPage, lastPage, numberOfCopies, printmgrsResFile, realNumberOfPagesinDoc, pageNumber, PrintError; THPrint thePrRecHdl; TPPrPort thePrPort; TPrStatus theStatus; GetPort(&oldPort); thePrRecHdl = (THPrint) NewHandle (sizeof (TPrint)); /** Check to make sure that the memory manager did not produce an error when it allocated the print record handle and make sure it did not pass back a nil handle. **/ if (thePrRecHdl != NULL && MemError() == noErr) { PrOpen(); if (PrError() == noErr) { /** Save the current resource file (i.e., the printer driver's) so the driver will not lose its resources upon return from the pIdleProc. **/ printmgrsResFile = CurResFile(); PrintDefault(thePrRecHdl); if (PrError() == noErr) { if (PrStlDialog(thePrRecHdl)) { /** DetermineNumberOfPagesinDoc determines the number of pages contained in the document by comparing the size of the document with rPage from the TPrInfo record (Inside Macintosh: Imaging With QuickDraw p.9-46). It returns the number of pages required to print the document for the currently selected printer. **/ realNumberOfPagesinDoc = DetermineNumberOfPagesinDoc ((**thePrRecHdl).prInfo.rPage); if (PrJobDialog(thePrRecHdl)) { /** Get the number of copies of the document that the user wants printed from iCopies of the TPrJob record (Inside Macintosh: Imaging With QuickDraw p.9-47). **/ numberOfCopies = (**thePrRecHdl).prJob.iCopies; /** Get the first and last pages of the document that were requested to be printed by the user from FstPage and iLastPage from the TPrJob record (Inside Macintosh: Imaging With QuickDraw p.9-47). **/ firstPage = (**thePrRecHdl).prJob.iFstPage; lastPage = (**thePrRecHdl).prJob.iLstPage; /** Print "all" pages in the print loop **/ (**thePrRecHdl).prJob.iFstPage = 1; (**thePrRecHdl).prJob.iLstPage = 9999; /** Determine the "real" number of pages contained in the document. Without this test, you would print 9999 pages. **/ if (realNumberOfPagesinDoc < lastPage) lastPage = realNumberOfPagesinDoc; PrintingStatusDialog = GetNewDialog(rPrintingDialogID, nil, (WindowPtr) -1); /** Print the number of copies of the document requested by the user from the Print Job Dialog. **/ for (copies = 1; copies <= numberOfCopies; copies++) { /** Install a pointer to your pIdle proc in my print record. **/ (**thePrRecHdl).prJob.pIdleProc = checkMyPrintDialogButton(); /** Restore the resource file to the printer driver's. **/ UseResFile(printmgrsResFile); thePrPort = PrOpenDoc(thePrRecHdl, nil, nil); if (PrError() == noErr) { /** Print the range of pages of the document requested by the user from the Print Job Dialog. **/ pageNumber = firstPage; while (pageNumber <= lastPage && PrError() == noErr) { /** If we've crossed a 128-page boundary, close the current print file, send it to the printer if necessary, and open a new document. **/ if ((pageNumber - firstPage) % iPFMaxPgs == 0) { if (pageNumber != firstPage) { PrCloseDoc(thePrPort); if (((**thePrRecHdl).prJob.bJDocLoop == bSpoolLoop) && (PrError() == noErr)) PrPicFile(thePrRecHdl, nil, nil, nil, &theStatus); thePrPort = PrOpenDoc(thePrRecHdl, nil, nil); } } PrOpenPage(thePrPort, nil); if (PrError() == noErr) { /** rPage (Inside Macintosh: Imaging With QuickDraw p.9-46) is the printable area for the currently selected printer. By passing the current port to the draw routine, enables your app to use the same routine to draw to the screen and the printer's GrafPort. **/ DrawStuff ((**thePrRecHdl).prInfo.rPage, (GrafPtr) thePrPort, pageNumber); } PrClosePage(thePrPort); pageNumber++; } /** End pageNumber loop **/ } PrCloseDoc(thePrPort); } /** End copies loop **/ } /** The printing job is being canceled by the request of the user from the Print Style Dialog or the Print Job Dialog. PrError will be set to PrAbort to tell the Print Manager to abort the current printing job. **/ else PrSetError (iPrAbort); /** cancel from the job dialog **/ } else PrSetError (iPrAbort); /** cancel from the style dialog **/ } } if (((**thePrRecHdl).prJob.bJDocLoop == bSpoolLoop) && (PrError() == noErr)) PrPicFile(thePrRecHdl, nil, nil, nil, &theStatus); /** Grab the printing error -- once you close the Printing Manager, PrError doesn't return a valid result anymore. **/ PrintError = PrError(); PrClose(); /** You do not want to report any printing errors until you have fallen through the printing loop. This will make sure that ALL of the Print Manager's open calls have their corresponding close calls, thereby enabling the Print Manager to close properly and that all temporary memory allocations are released. **/ if (PrintError != noErr) PostPrintingErrors (PrintError); } if (thePrRecHdl != NULL) DisposeHandle((Handle) thePrRecHdl); if (PrintingStatusDialog != NULL) DisposeDialog(PrintingStatusDialog); SetPort(oldPort); } /** PrintStuff **/ |
Checking For Error Conditions While Printing
Your application should always check for error conditions while
printing. You can do this by calling
As the previous example code demonstrates, your application should call
|
Some General Error-Handling GuidelinesThe following section provides you with some general error-handling guidelines:
|
Cancelling or Pausing the Printing ProcessIf you install a procedure for handling requests to cancel printing, with an option to pause the printing process, beware of timeout problems when printing to network printers. Communication between the Macintosh and a networked printer must be maintained to prevent a job or a wait timeout. If there is no communication for a period of time (roughly 300-600 seconds), the printer times out and the print job terminates due to a wait timeout. Or, if the print job requires more than ten minutes to print, the print job terminates due to a job timeout. Since there is no good method to determine to what type of printer an application is printing, it is probably a good idea to document in your ReadMe the possibility of a network printer timing out for a user who chooses to select "pause" for five minutes or more. |
Error Messages
The Printing Manager reports the error messages covered in this section.
If an error that does not belong to the Printing Manager occurs, the
Printing Manager puts it into low memory, where it can be retrieved with
a call to The most common error encountered is -4101, which is generated if the selected LaserWriter is not available on the network. Since this error is so common, it's a good idea to display a dialog box requesting the user to select a printer from the Chooser when this error is encountered. |
Common Printing Manager and System ErrorsThe following table shows you common printing manager and system error codes.
|
|
struct TGnlData { short iOpCode; short iError; long lReserved; }; |
After each call to PrGeneral
, your application should check the value in
the iError
field. The possible result codes that can be returned are:
Error Code | Constant | Description |
0 | noErr |
No Error |
1 | NoSuchRsl |
Unsupported Resolution |
2 | OpNotImpl |
Unsupported Opcode |
-192 | resNotfound |
The current printer driver does not support PrGeneral . |
For further information on PrGeneral
,
you should read Pete 'Luke' Alexander's article,
"Meet PrGeneral
," in
develop 3.
LaserWriter Driver Family Errors
|
LaserWriter 8 Internal Errors
|
That's all there is to it. Now your application can print properly with the Macintosh Printing Manager by adhering to the rules specified in this Note and by handling error messages appropriately.
|
![]() |
|
Special thanks to Rich Blanchard, Paul Danbold, and Dave Polaschek.