Important: The information in this document is obsolete and should not be used for new development.
Using the Printing Manager
The Printing Manager defines routines that give your application device-independent control over the printing process. You can use these routines to print documents, to display and alter the print dialog boxes, and to handle printing errors.To use the Printing Manager, you must first initialize QuickDraw, the Font Manager, the Window Manager, the Menu Manager, TextEdit, and the Dialog Manager. The first Printing Manager routine to call, when you are ready to print, is
PrOpen
; the last routine to call isPrClose
.All of the Printing Manager routines described in this chapter are available on both basic QuickDraw and Color QuickDraw systems using system software version 4.1 or later. However, not all printer drivers support all features provided by the
PrGeneral
procedure. (When you call thePrGeneral
procedure, described in "Getting and Setting Printer Information" beginning on page 9-26, it in turn calls the current printer driver to get or set the desired information.) After callingPrGeneral
and passing it a particular opcode, you should call thePrError
function and test whether it returns theopNotImpl
result code, which indicates that the printer driver does not support that particular opcode.All printable documents must have a
TPrint
record. EachTPrint
record contains information about page size, number of copies requested, and the range of pages the user wants printed. Although only the information the user specifies through the style dialog box should be preserved each time the user prints the document, you can save the entireTPrint
record when you save the document. The next time the user opens the document, you can retrieve the user's preferences as saved in theTPrint
record and then use thePrValidate
function to validate the fields of theTPrint
record.To print a user's document, you must first create or validate a
TPrint
record for the document. You can use thePrintDefault
procedure to initialize the values in aTPrint
record. You can use thePrValidate
function to check that an existingTPrint
record is compatible with the current printer and its driver. Your application should include a printing loop that handles printing and checks for printing errors at every step.You should never assume the type of printer that has been selected; your application should always be able to print to any type of printer. However, for some special features that are not supported by QuickDraw (notably rotated text and graphics, dashed lines, and hairlines), you may want to create two versions of your drawing code: one that uses picture comments to take advantage of the features, and another that provides QuickDraw-based implementations of these features. Using picture comments, your application can instruct printer drivers to perform operations that QuickDraw does not support. For more information, see Appendix B, "Using Picture Comments for Printing," in this book.
The rest of this section describes how you can
Be aware that the burden of maintaining backward compatibility with early Apple printer models--as well as maintaining compatibility with over a hundred existing printer drivers--requires extra care on your part. When the Printing Manager was initially designed, it was intended to support ImageWriter printers directly attached to Macintosh computers with only a single floppy disk and 128 KB of RAM. Later, the Printing Manager was implemented on PostScript LaserWriter printer drivers for more powerful Macintosh computers sharing LaserWriter printers on networks. Since then, the Printing Manager has been implemented on a substantial--and unanticipated--number of additional Apple and third-party printer drivers, each in its own, slightly unique way. When you use Printing Manager routines and data structures, you should be especially wary of and defensive about possible error conditions. Because Apple has little control over the manner in which third parties support the Printing Manager in their printer drivers, you should test your application's printing code on as many printers as possible.
- create and use a
TPrint
record- structure your printing loop to print a document
- use the
PrGeneral
procedure to determine printer characteristics- alter the style and job dialog boxes
- write an idle procedure that runs during printing
- handle printing errors
Creating and Using a TPrint Record
To print a document, you need a validTPrint
record that is formatted for the current versions of the Printing Manager and the printer driver.To create a new
TPrint
record, you must first create a handle to it with a Memory Manager function such asNewHandle
orNewHandleClear
. You then must use thePrintDefault
procedure to set the fields of the record to the default values for the current printer driver, as illustrated in the following code fragment.
VAR prRecHdl: THPrint; {allocate handle to a TPrint record} prRecHdl := THPrint(NewHandleClear(SizeOf(TPrint))); IF prRecHdl <> NIL THEN PrintDefault(prRecHdl) {sets appropriate default values } { for current printer driver} ELSE ; {handle error here}You can also use an existingTPrint
record (for instance, one saved with a document). If you use an existingTPrint
record, be sure to call thePrValidate
function before using theTPrint
record to make sure it's valid for the current version of the Printing Manager and for the current printer driver.Listing 9-1 shows an application-defined routine that reads a
TPrint
record that the application has saved as a resource of type'SPRC'
with the document. (The Resource Manager routinesCurResFile
,UseResFile
,Get1Resource
, andDetachResource
that are shown in this listing are described in the chapter "Resource Manager" in Inside Macintosh: More Macintosh Toolbox.)Listing 9-1 Reading a document's
TPrint
record
FUNCTION MyGetPrintRecordForThisDoc (refNum: Integer; VAR prRecHdl: THPrint; VAR prRecChanged: Boolean): OSErr; VAR saveResFile: Integer; BEGIN saveResFile := CurResFile; {save the resource file for the document} UseResFile(refNum); prRecHdl := THPrint(Get1Resource('SPRC', kDocPrintRec)); IF prRecHdl <> NIL THEN BEGIN DetachResource(Handle(prRecHdl)); prRecChanged := PrValidate(prRecHdl); {validate TPrint record} MyGetPrintRecordForThisDoc := PrError; END ELSE MyGetPrintRecordForThisDoc := kNILHandlePrintErr; UseResFile(saveResFile); END;You should save theTPrint
record when the user saves the document. By doing this, you can save any preferences that the user has selected for printing that document, such as orientation of the page or page size. See the chapter "Resource Manager" in Inside Macintosh: More Macintosh Toolbox for information about saving data such asTPrint
records in resources.Every printer driver uses the fields of the
TPrint
record differently. To maintain compatibility with the Printing Manager, you should follow these guidelines:
- Do not test for the contents of undocumented fields.
- Do not set fields in the
TPrint
record directly.- Use the print dialog boxes provided by the printer drivers or, if you want to customize these dialog boxes, alter them only as recommended in "Altering the Style or Job Dialog Box" on page 9-32.
Printing a Document
When writing an application, the code you provide that handles printing is referred to as the printing loop. A printing loop calls all the Printing Manager routines necessary to print a document. In general, a printing loop must do the following tasks:
Listing 9-2 shows an extremely broad example of a printing loop--the code does not optimize for the type of printer being used or for the material being printed (text, graphics, or a mixture of both). However, this sample routine, called
- It must unload unused code segments to ensure that you have as much memory as possible in which to print.
- It must open the Printing Manager and the current printer driver by using the
PrOpen
procedure.- It must set up a valid
TPrint
record for the document (using any values the user previously specified through the style dialog box) by using thePrintDefault
procedure or thePrValidate
function. (When the user is printing from the Finder, it is best not to display the style dialog box, but rather to use saved or default settings for the document.)- It must display the job dialog box as appropriate by using the
PrJobDialog
function or, for a customized job dialog box, thePrDlgMain
function. (When the user is printing from the Finder, display the job dialog box only once, and then use thePrJobMerge
procedure to apply the information from this dialog box to any other documents selected by the user.)- It must determine the number of pages required to print the requested range of pages by examining the fields of the
TPrint
record. (Depending on the page rectangle of the current printer, the amount of data you can fit on a physical page of paper may differ from that displayed on the screen, although it is usually the same.)- It must determine the number of copies to print by examining the
TPrint
record.- It may display a status dialog box indicating to the user the status of the current printing operation by using the Dialog Manager function
GetNewDialog
(described in the chapter "Dialog Manager" in Inside Macintosh: Macintosh Toolbox Essentials).- If it displays a status dialog box, it must install an idle procedure in the
pIdleProc
field of theTPrJob
record (which is contained in theTPrint
record) to update information in the status dialog box and to check whether the user wants to cancel the operation. (The default idle procedure also performs this check, but if you update information in your status dialog box you must provide your own idle procedure.)- It must print the requested range of pages for each requested copy by
- using the
PrOpenDoc
function to open a printing graphics port if the current page number is the first page or a multiple of the value represented by the constantiPFMaxPgs
- opening a page for printing by using the
PrOpenPage
procedure- printing the page by drawing into the printing graphics port with the QuickDraw routines described in the rest of this book
- closing the page by using the
PrClosePage
procedure- using the
PrCloseDoc
procedure to close the printing graphics port and begin printing the requested range of pages- checking whether the printer driver is using deferred printing and, if so, using the
PrPicFile
procedure to send the spool file to the printer
- Finally, the printing loop must close the Printing Manager by using the
PrClose
procedure.
MyPrintLoop
, does cover the major aspects of a printing loop: how to balance calls to the open and close routines, how to determine page count, and how to provide support for documents exceeding the maximum page length specified by the constantiPFMaxPgs
.Listing 9-2 A sample printing loop
PROCEDURE MyPrintLoop(docToPrint: MyDocRecHnd; displayJob: Boolean); VAR copies, numberOfCopies: Integer; firstPage, lastPage: Integer; pageNumber, numberOfPages: Integer; doPrint, changed: Boolean; oldPort: GrafPtr; theStatus: TPrStatus; printError: Integer; BEGIN GetPort(oldPort); MyUnLoadTheWorld; {swap out those segments of code not needed to print} PrOpen; {open Printing Manager and the current printer driver} IF (PrError = noErr) THEN BEGIN gPrintResFile := CurResFile; {save the current resource file} gPrintRec := docToPrint^^.docPrintRecHdl; {set to this doc's print rec} changed := PrValidate(gPrintRec); {verify TPrint record} IF (PrError = noErr) THEN BEGIN {determine the number of pages required to print the document} numberOfPages := MyDetermineNumOfPages(gPrintRec^^.prInfo.rPage); {display job dialog box if requested, else use previous settings} IF displayJob THEN doPrint := PrJobDialog(gPrintRec) ELSE doPrint := MyDoJobMerge(gPrintRec); IF doPrint THEN BEGIN numberOfCopies := gPrintRec^^.prJob.iCopies; firstPage := gPrintRec^^.prJob.iFstPage;{save first page number} lastPage := gPrintRec^^.prJob.iLstPage; {save last page number} gPrintRec^^.prJob.iFstPage := 1; {reset to 1} gPrintRec^^.prJob.iLstPage := iPrPgMax; {reset to maximum} IF (numberOfPages < lastPage) THEN lastPage := numberOfPages; {to prevent printing past last } { page} {display a "Print Status" dialog box (optional)-- } { first, deactivate front window} MyDoActivateFrontWindow(FALSE, oldPort); gPrintStatusDlg := GetNewDialog(kPrintStatus, NIL, Pointer(-1)); {set up dialog items (insert name of document being printed)} MySetUpDBoxItems(docToPrint); ShowWindow(gPrintStatusDlg); {display the dialog box} {set up idle procedure (for later use)} gPrintRec^^.prJob.pIdleProc := @MyDoPrintIdle; {print the requested number of copies} FOR copies := 1 TO numberOfCopies DO BEGIN UseResFile(gPrintResFile);{restore driver's resource file} {print the requested range of pages of the document} FOR pageNumber := firstPage TO lastPage DO BEGIN {check current page number against iPFMaxPgs} IF (pageNumber - firstPage) MOD iPFMaxPgs = 0 THEN BEGIN IF pageNumber <> firstPage THEN {if max size of spool file has been reached (and this } { isn't the first page), then close the document, } { initiate printing, then reopen the document} BEGIN PrCloseDoc(gPrinterPort); {next line tests for deferred printing} IF (gPrintRec^^.prJob.bJDocLoop = bSpoolLoop) AND (PrError = noErr) THEN PrPicFile(gPrintRec, NIL, NIL, NIL, theStatus); END; {if this is the first page or a multiple of iPFMaxPgs, } { then open the document for printing} gPrinterPort := PrOpenDoc(gPrintRec, NIL, NIL); END; {of check current page number} IF (PrError = noErr) THEN BEGIN {print a page} PrOpenPage(gPrinterPort, NIL); IF (PrError = noErr) THEN {draw (print) a page in the printable area for the } { current printer (indicated by the rPage field)} MyDrawStuff (gPrintRec^^.prInfo.rPage, docToPrint, GrafPtr(gPrinterPort), pageNumber); PrClosePage(gPrinterPort); END; {of print a page} END; {of print the requested range of pages} PrCloseDoc(gPrinterPort); IF (gPrintRec^^.prJob.bJDocLoop = bSpoolLoop) AND (PrError = noErr) THEN PrPicFile(gPrintRec, NIL, NIL, NIL, theStatus); END; END; END; END; printError := PrError; PrClose; IF (printError <> noErr) THEN DoError(ePrint, printError); DisposeDialog(gPrintStatusDlg); SetPort(oldPort); MyDoActivateFrontWindow(TRUE, oldPort); {activate window} END;TheMyPrintLoop
procedure starts by getting a pointer to the current graphics port. Then it calls an application-defined routine,MyUnloadTheWorld
, that swaps out code segments not required during printing. Then it opens the Printing Manager and the current printer driver and its resource file by callingPrOpen
.The
MyPrintLoop
procedure saves the current resource file (after callingPrOpen
, the current resource file is the driver's resource file) so that, if its idle procedure changes the resource chain in any way, it can restore the current resource file before returning; thus the driver does not lose access to its resources. TheMyPrintLoop
procedure then uses thePrValidate
function to change any values in theTPrint
record associated with the document to match those specified by the current printer driver; these values can be changed later by the printer driver as a result of your application's use of thePrStlDialog
andPrJobDialog
functions. (Your application passes a handle to aTPrint
record to thePrStlDialog
andPrJobDialog
functions, and these procedures modify theTPrint
record according to the user's interaction with the style and job dialog boxes.) TheMyPrintLoop
procedure callsPrValidate
rather thanPrintDefault
to preserve any values that the user might have previously set through the style dialog box.To print a document, you must divide the data into sections that fit within the page rectangle dimensions stored in the
rPage
field of theTPrJob
record, which is contained in theTPrint
record. (This information is stored in therPage
field when you call thePrintDefault
,PrValidate
, orPrStlDialog
routine.) The application-defined functionMyDetermineNumOfPages
is specific to the application, because the way the application divides up the data depends on the type of text and graphics in the document. TheMyDetermineNumOfPages
function determines the number of pages required to print the document by comparing the size of the document with the printable area for the current printer, which is specified by the value in therPage
field of theTPrJob
record in theTPrint
record.After determining the number of pages required to print the document,
MyPrintLoop
displays the job dialog box if the calling routine requested it to do so. If the user prints multiple documents at once, the calling routine sets thedisplayJob
parameter toTRUE
for the first document andFALSE
for subsequent documents. This allows the user to specify values in the job dialog box only once when printing multiple documents. It also provides an application with the ability to print documents in the background (for example, as the result of responding to the Apple event Print Documents) without requiring the application to display the job dialog box.The user's responses in the job dialog box provide such information as the number of copies and the page numbers of the first and last pages requested. The
MyPrintLoop
procedure stores these values in the local variablesfirstPage
andlastPage
. It then resets the value of the first page in theTPrJob
record as 1 and resets the value of the last page to the value represented by the constantiPrPgMax
.The
MyPrintLoop
procedure compares the values of the number of pages in the document with the last page the user requested and changes the last page number as necessary. For example, if the user asks to print page 50 of a two-page document,MyPrintLoop
resets the value of the last page to 2.At this point,
MyPrintLoop
is about to begin the process of sending the pages off to be printed. So it displays its own status dialog box to inform the user of the current status of the printing operation. If your status dialog box provides a button or reports on the progress of the printing operation, you need to handle events in the dialog box by providing an idle procedure. Your idle procedure should update the items in your status dialog box to show the current progress of the printing operation, and it should determine whether the user has canceled the printing operation. The printer driver calls the idle procedure periodically during the printing process. For more information on idle procedures, see "Writing an Idle Procedure" on page 9-35.After installing its idle procedure, the
MyPrintLoop
procedure then begins the printing operation by performing a number of steps for each requested copy. First,MyPrintLoop
restores the current resource file to the printer driver's resource file.
MyPrintLoop
then begins the process of printing each page. The maximum number of pages that can be printed at a time is represented by the constantiPFMaxPgs
. If the file is larger than the value represented byiPFMaxPgs
, your application can print the number of pages represented byiPFMaxPgs
and then begin the printing loop again with the next section of the document. In this way, you can print any number of pages.Next,
MyPrintLoop
opens a page for printing and draws the page in the printing graphics port with the application-definedMyDrawStuff
procedure, the details of which are specific to the application. The parameters toMyDrawStuff
are the size of the page rectangle, the document containing the data to print, the printing graphics port in which to draw, and the page number to be printed. This allows the application to use the same code to print a page of a document as it uses to draw the same page on screen.When
MyPrintLoop
is finished printing (or has printed a multiple of the value represented by the constantiPFMaxPgs
), it closes the printing graphics port for the document. By testing for thebSpoolLoop
constant in thebJDocLoop
field of theTPrJob
record,MyPrintLoop
determines whether a printer driver is using deferred printing; if so,MyPrintLoop
calls thePrPicFile
procedure, which sends the spool file to the printer.Some QuickDraw printer drivers (in particular, those for the ImageWriter and ImageWriter LQ printers) provide two methods of printing documents: deferred and draft-quality. Typically, the printer driver uses deferred printing when a user chooses Best in the job dialog box, and it uses draft-quality printing when the user chooses Draft.
Deferred printing was designed to allow ImageWriter printers to spool a page image to disk when printing under the low memory conditions of the original 128 KB Macintosh computer. With deferred printing, a printer driver records each page of the document's printed image in a structure similar to a QuickDraw picture, which the driver writes to a spool file. For compatibility with printer drivers that still support deferred printing, use the
PrPicFile
procedure to instruct these printer drivers to turn the QuickDraw pictures into bit images and send them to the printer. (Draft-quality printing, on the other hand, is a method by which a printer driver converts into drawing operations calls only to QuickDraw's text-drawing routines. The printer driver sends these routines directly to the printer instead of using deferred printing to capture the entire image for a page in a spool file.)
The
- Note
- Do not confuse background printing with deferred printing. While printer drivers supporting background printing also create spool files, you do not need to use the
PrPicFile
procedure to send these spool files to the printer. In fact, there is no reliable way for you to determine whether a printer driver is using a spool file for background printing.MyPrintLoop
procedure concludes by closing the Printing Manager, reporting any Printing Manager errors, and resetting the current graphics port to the original port.In your printing loop, you should balance all calls to Printing Manager open routines to the equivalent Printing Manager close routines. This is extremely important, even if you stop printing because of an error. Failure to call the matching close routines can cause the Printing Manager to perform incorrectly.
Note that
MyPrintLoop
callsPrError
after each Printing Manager routine. If an error is found, the loop calls a close routine (PrClose
,PrClosePage
, orPrCloseDoc
) for any Printing Manager open routines (PrOpen
,PrClosePage
, orPrOpenDoc
) before informing the user of the error. You should use this approach in your own application to make sure the Printing Manager closes properly and all temporary memory is released.
- WARNING
- Some applications use a method of printing that prints out each page of a spooled document as a separate print job in order to avoid running out of disk space while spooling the document. You should not use this method, known as "spool a page, print a page." It is appropriate only for a printer directly connected to the user's computer (that is, not to a network) and therefore creates device dependence--and also it's extremely slow. If the printer is a remote or shared device (such as a LaserWriter printer connected by an AppleTalk network), another application could print a document between the pages of your user's document. At worst, if both applications printing to the shared printer use the "spool a page, print a page" method, the printed documents could end up interleaved. The pages for one of the documents could be out of order, even when printed by itself on a shared, network printer.
Printing From the Finder
Typically, users print documents that are open on the screen one at a time while the application that created the document is running. Alternatively, users can print one or more documents from the Finder. To print documents from the Finder, the user selects one or more document icons and chooses the Print command from the File menu. When the Print command is chosen, the Finder starts up the application and passes it an Apple event--the Print Documents event--indicating that the documents are to be printed rather than opened on the screen.As explained in Inside Macintosh: Interapplication Communication, your application should support the required Apple events, which include the Print Documents event. In response to a Print Documents event, your application should do the following:
Figure 9-10 How the
- Your application should not open windows for the documents.
- For style information, your application should use saved or default settings instead of displaying the style dialog box to ask this information from the user.
- Your application should use the
PrJobDialog
function (described on page 9-59) or thePrDlgMain
function (described on page 9-60) to display the job dialog box only once. When the user clicks the OK button in the job dialog box, you can then use thePrJobMerge
procedure (described on page 9-63) to apply the information specified by the user to all of the documents selected from the Finder.For example, if the user has selected three documents to print, you can display the job dialog box only once and then apply the same information supplied by the user to all three documents. Figure 9-10 shows a situation where, through the job dialog box, the user has specified the number of copies and the range of pages to print. In this example, the application applies this job information to the
TPrint
record of the three documents by callingPrJobMerge
. Note thatPrJobMerge
preserves the fields of theTPrint
record that are specific to each document (that is, the fields that are set by the user through the style dialog box).- Your application should remain open until the Finder sends your application a Quit event. If appropriate, the Finder sends your application this Apple event immediately after sending it the Print Documents event.
PrJobMerge
procedure works
See Inside Macintosh: Interapplication Communication for more information about how to handle the Print Documents and Quit events.Providing Names of Documents Being Printed
Some printer drivers (usually those for printers such as LaserWriter printers that are shared among many users) provide the names of the users who are printing and the documents that are being printed to others interested in using the printer. Providing the names of users and documents is a courtesy to other users sharing the printer on a network. The printer driver gets the name of the document being printed from the title of the frontmost window on the user's screen. ThePrOpenDoc
andPrValidate
functions call the Window Manager procedureFrontWindow
to get the document's name.Printer drivers can't get a document name if your application doesn't display windows while printing. For example, applications should not open windows for their documents when the user prints from the Finder. If there is no front window, or if the window's title is empty, the printer driver sets the document name to "Unspecified" or "Untitled."
You can ensure that the document name is available by displaying a printing status dialog box and setting the window's title to the document's name. If the dialog box is one that doesn't have a title bar (like that of type
dBoxProc
), this title is not displayed but the current printer driver can still use the title as the document's name. If you don't want to put up a visible window, you can create a tiny window (for instance, typeplainDBox
) and hide it behind the menu bar by giving it the global coordinates of (1,1,2,2). See the chapter "Window Manager" in Inside Macintosh: Macintosh Toolbox Essentials for information about thedBoxProc
andplainDBox
window types.
- Note
- Do not set the document name in the
TPrint
record directly. Not all printer drivers support this field, and Apple does not guarantee that internal fields of the Printing Manager's data structures will remain the same.Printing Hints
QuickDraw is the primary means you use to print, and in general you can use QuickDraw in the printing graphics port exactly as you would for a screen's graphics port. There are a few things to note when drawing to the printing graphics port:
- Don't depend on values in a printing graphics port remaining identical from page to page. With each new page, you usually get reinitialized font information and other characteristics for the printing graphics port.
- Don't make calls that don't do anything on the printer. For example, QuickDraw erase routines such as
EraseRect
are quite time-consuming and normally aren't needed on the printer. An erase routine takes time because every bit (90,000 bits per square inch on a 300 dpi LaserWriter) has to be cleared. Paper does not need to be erased the way the screen does. Also avoid using theTextBox
procedure, which makes calls to theEraseRect
procedure. You might want to use a different method of displaying text (for example,DrawString
orDrawText
) or write your own version ofTextBox
. See the chapter "QuickDraw Text" in Inside Macintosh: Text.- Don't use clipping to select text to be printed. There are a number of subtle differences between how text appears on the screen and how it appears on the printer; you can't count on knowing the exact dimensions of the rectangle occupied by the text.
- Don't use fixed-width fonts to align columns. Because spacing is adjusted on the printer, you should explicitly move the pen to where you want it.
- Don't use the outline font style to create white text on a black background.
- Avoid changing fonts frequently.
- Because of the way rectangle intersections are determined, you slow printing substantially if your clipping region falls outside of the rectangle given by the
rPage
field of theTPrInfo
record of theTPrint
record.
Getting and Setting Printer Information
You can determine the resolution of the printer, set the resolution you want, find out if the user has selected landscape printing, or force enhanced draft-quality printing by using thePrGeneral
procedure. You call thePrGeneral
procedure with one of five opcodes:getRslDataOp
,setRslOp
,getRotnOp
,draftBitsOp
, ornoDraftBitsOp
. These opcodes have data structures associated with them.When you call the
PrGeneral
procedure, it in turn calls the current printer driver to get or set the desired information. Not all printer drivers support all features provided by thePrGeneral
procedure, however, so your application can't depend on its use.Listing 9-3 shows an application-defined routine,
DoIsPrGeneralThere
, that checks whether the current printer driver supports thePrGeneral
procedure. First,DoIsPrGeneralThere
sets theopcode
field of theTGetRotnBlk
record to thegetRotnOp
opcode--the opcode used to determine whether the user has chosen landscape orientation. ThenDoIsPrGeneralThere
passes the address of theTGetRotnBlk
record to thePrGeneral
procedure. It then callsPrError
to get any errors that result from callingPrGeneral
. If the error isresNotFound
, the printer driver does not supportPrGeneral
.Listing 9-3 Checking whether the current printer driver supports the
PrGeneral
procedure
FUNCTION DoIsPrGeneralThere: Boolean; VAR getRotRec: TGetRotnBlk; myPrintErr: OSErr; BEGIN myPrintErr := 0; getRotRec.iOpCode := getRotnOp; {set the opcode} getRotRec.hPrint := gMyPrRecHdl; {TPrint record this operation applies to} PrGeneral(@getRotRec); myPrintErr := PrError; PrSetError(noErr); IF (myPrintErr = resNotFound) THEN {the current driver doesn't support } DoIsPrGeneralThere := FALSE; { PrGeneral} ELSE DoIsPrGeneralThere := TRUE; {current driver supports PrGeneral} END;After determining that the current printer driver supportsPrGeneral
, you can usePrGeneral
to
As an alternative to testing for
- determine and set the resolution of the current printer
- determine the current page orientation
- force enhanced draft-quality printing
PrGeneral
, your application can callPrGeneral
and then test whetherPrError
error returns theopNotImpl
result code, which indicates that the printer driver either does not supportPrGeneral
or does not support that particular opcode.These operations are discussed in the following sections.
Determining and Setting the Resolution of the Current Printer
Some printer drivers support only one of the two possible kinds of resolution: discrete or variable. You can use thePrGeneral
procedure to determine the kind of resolution supported by the current printer and then use the highest resolution desired by your application or the user.Each printer has its own imaging capabilities. When you call
PrGeneral
with the valuegetRslDataOp
in theiOpCode
field of theTGetRslBlk
record,PrGeneral
returns the resolutions that the printer supports. Figure 9-11 showsTGetRslBlk
records (described on page 9-50) returned by the drivers for a 300-dpi LaserWriter PostScript printer and a QuickDraw ImageWriter printer. Because it supports variable resolutions, theTGetRslBlk
record for the LaserWriter driver specifies minimum and maximum resolutions in the x and y directions. Because it uses discrete resolutions, theTGetRslBlk
record for the ImageWriter driver specifies no minimum or maximum resolutions in the x and y directions, but instead specifies the four discrete resolutions it supports.Figure 9-11 Sample resolutions for a PostScript printer and a QuickDraw printer
ATPrint
record contains the x and y resolutions that the printer uses in printing the data associated with theTPrint
record. For eachTPrint
record you use, you can either use the default values or you can specify the particular imaging resolution that you want to use. To do this, you can callPrGeneral
, specifying the valuesetRslOp
in theiOpCode
field and specifying the x and y resolutions in theiXRsl
andiYRsl
fields of theTSetRslBlk
record (which is described on page 9-51). ThePrGeneral
procedure returns thenoErr
result code if it has updated theTPrint
record with this new resolution, or it returns thenoSuchRsl
result code if the current printer doesn't support this resolution.Listing 9-4 illustrates how to use the
PrGeneral
procedure to determine the possible resolutions for the current printer and then set aTPrint
record to the desired resolution.Listing 9-4 Using the
getRslDataOp
andsetRslOp
opcodes with thePrGeneral
procedure
FUNCTION DoSetMaxResolution (thePrRecHdl: THPrint): Integer; VAR maxDPI: Integer; resIndex: Integer; getResRec: TGetRslBlk; setResRec: TSetRslBlk; BEGIN maxDPI := 0; getResRec.iOpCode := getRslDataOp;{get printer resolution info} PrGeneral(@getResRec); IF (getResRec.iError = noErr) AND (PrError = noErr) THEN BEGIN {the TGetRslBlk record contains an array of possible resolutions-- } { so loop through each resolution range record looking for } { the highest resolution available where x and y are equal} FOR resIndex := 1 TO (getResRec.iRslRecCnt) DO BEGIN IF (getResRec.rgRslRec[resIndex].iXRsl = getResRec.rgRslRec[resIndex].iYRsl) AND (getResRec.rgRslRec[resIndex].iXRsl > maxDPI) THEN maxDPI := getResRec.rgRslRec[resIndex].iYRsl; END; {set the resolution to the maximum supported resolution} IF maxDPI <> 0 THEN BEGIN WITH setResRec DO BEGIN iOpCode := setRslOp; hPrint := thePrRecHdl; iXRsl := maxDPI; iYRsl := maxDPI; END; PrGeneral(@setResRec); END; {end of maxDPI <> 0} IF (setResRec.iError = noErr) AND (PrError = noErr) AND (maxDPI <> 0) THEN DoSetMaxResolution := maxDPI; END ELSE DoSetMaxResolution := 0; END;You can reset the original resolutions by calling thePrGeneral
procedure with thesetRslOp
opcode a second time. To do so, you should save the values contained in theiVRes
andiHRes
fields of theTPrInfo
record before making the first call toPrGeneral
. You can also reset the original resolutions by calling thePrintDefault
procedure with theTPrint
record, which sets all of the fields of theTPrint
record to the default values of the current printer resource file. However, if you usePrintDefault
you lose all of the user's selections from the last style dialog box. (You may want to reset the original resolution because that may be the printer's best resolution, though not its highest.)Based on the information you get with a call to
PrGeneral
using thegetRslDataOp
opcode, you may decide to change the resolution with a call toPrGeneral
using thesetRslOp
opcode. If so, the printer driver may need to change the appearance of the style and job dialog boxes by disabling some items. Therefore, you should determine and set the resolution before you use thePrStlDialog
andPrJobDialog
functions (or thePrDlgMain
function) to present the print dialog boxes to the user.Note that the style dialog boxes for some printers, such as the StyleWriter, may offer the user a choice of printing in Best or Normal modes, which sets the printing at 360 or 180 dpi, respectively. Your application has no control over this setting. The printer driver converts your drawing accordingly.
Determining Page Orientation
At times it can be useful for your application to determine which page orientation the user selects in the style dialog box. For instance, if an image fits on a page only if it is printed in landscape orientation (theprInfo
field of theTPrint
record defines a smaller horizontal value for the paper rectangle than for the image rectangle) and the user has not selected landscape orientation, your application can remind the user to select this orientation before printing. Otherwise, the user gets a clipped image.If you call the
PrGeneral
procedure with thegetRotnOp
opcode in theTGetRotnBlk
record (described on page 9-53), the printer driver returns in thefLandscape
field of this record a Boolean variable that indicates whether or not theTPrint
record specifies landscape orientation. The user selects the type of orientation through the style dialog box, and the printer driver updates the fields of theTPrint
record accordingly.Listing 9-5 shows an application-defined function,
DoIsLandscapeModeSet
, that returns a Boolean value indicating whether the user has selected landscape orientation for the current document.Listing 9-5 Using the
getRotnOp
opcode with thePrGeneral
procedure to determine page orientation
FUNCTION DoIsLandscapeModeSet (thePrRecHdl: THPrint): Boolean; VAR getRotRec: TGetRotnBlk; BEGIN getRotRec.iOpCode := getRotnOp; {set opcode} getRotRec.hPrint := thePrRecHdl; {specify TPrint record} PrGeneral(@getRotRec); {get landscape orientation} IF (getRotRec.iError = noErr) AND (PrError = noErr) AND getRotRec.fLandscape THEN DoIsLandscapeModeSet := TRUE ELSE DoIsLandscapeModeSet := FALSE; END;Enhancing Draft-Quality Printing
When the user selects faster, draft-quality printing from a job dialog box from some printer drivers, the printer driver handles the printing operation appropriately.However, you can force users to use an enhanced form of draft-quality printing on ImageWriter printers (as well as on other printers that may support enhanced draft-quality printing) by calling the
PrGeneral
procedure, specifying thedraftBitsOp
opcode in aTDftBitsBlk
record (described on page 9-52), and specifying theTPrint
record for the operation. If your application produces only text, bitmaps, or pixel maps, this can increase performance and save disk space, because the printer driver prints the document immediately, rather than spooling it to disk as with deferred printing. ThedraftBitsOp
opcode has no effect if the printer driver does not support draft-quality printing or does not support deferred printing. If the driver does not support thedraftBitsOp
opcode, thePrGeneral
procedure returns theopNotImpl
result code.With draft-quality printing, a printer driver like the ImageWriter printer driver converts into drawing operations calls only to QuickDraw's text-drawing routines. The printer driver sends these text-drawing routines directly to the printer instead of using deferred printing to capture the entire image for a page in a spool file. Draft-quality printing produces quick, low-quality drafts of text documents that are printed straight down the page, from top to bottom and left to right.
Using the
PrGeneral
procedure, it's possible to produce enhanced draft-quality printing on some printers--such as ImageWriter printers. Normally, draft-quality printing renders output consisting only of text. However, enhanced draft-quality printing prints the bitmaps and pixel maps that your application draws using theCopyBits
procedure (described in the chapter "QuickDraw Drawing" in this book) without using deferred printing to write to and read from a spool file.Because it's supported by so few printer drivers, and because it offers little in the way of extra capability, enhanced draft-quality printing has limited usefulness.
To use enhanced draft-quality printing, call
PrGeneral
with thedraftBitsOp
opcode before using thePrStlDialog
andPrJobDialog
functions or thePrDlgMain
function to present the style dialog box and job dialog box to the user. The use of thedraftBitsOp
opcode may cause items in the print dialog boxes to become inactive. For the ImageWriter printer driver, for example, the use of thedraftBitsOp
opcode makes the landscape icon in the style dialog box and the Best and Faster options in the job dialog box inactive.
Listing 9-6 illustrates how to implement enhanced draft-quality printing.
- IMPORTANT
- If you call
PrGeneral
with thedraftBitsOp
opcode after using thePrJobDialog
orPrDlgMain
function, and if the user chooses draft printing from the job dialog box, the ImageWriter printer does not print any bitmaps or pixel maps contained in the document.Listing 9-6 Using the
draftBitsOp
opcode with thePrGeneral
procedure for enhanced draft-quality printing
FUNCTION DoDraftBits (thePrRecHdl: THPrint): Boolean; VAR draftBitsBlk: TDftBitsBlk; BEGIN draftBitsBlk.iOpCode := draftBitsOp;{set the opcode} draftBitsBlk.hPrint := thePrRecHdl; {specify the TPrint record} PrGeneral(@draftBitsBlk); {use enhanced draft quality} IF (draftBitsBlk.iError = noErr) AND (PrError = noErr) THEN DoDraftBits := TRUE {this TPrint record specifies } { enhanced draft printing} ELSE DoDraftBits := FALSE; {this TPrint record does not } { specify enhanced draft printing} END;You should keep one additional point in mind when using thedraftBitsOp
opcode: all of the data that is printed must be sorted along the y axis, because reverse paper motion is not possible on the ImageWriter printer when printing in draft-quality mode. This means that you cannot print two objects side by side; that is, the top boundary of an object cannot be higher than the bottom boundary of the previous object. To get around this restriction, you should sort your objects before print time.You can call
PrGeneral
with thenoDraftBitsOp
opcode to use regular draft-quality printing again. If you callPrGeneral
withnoDraftBitsOp
without first callingdraftBitsOp
, the procedure does nothing. As with thedraftBitsOp
opcode, you should callPrGeneral
with thenoDraftBitsOp
opcode before you present the style and job dialog boxes to the user.Altering the Style or Job Dialog Box
Each printer resource file includes definitions of the standard style and job dialog boxes that are specific to the type of printer managed by its printer driver. ThePrStlDialog
andPrJobDialog
functions display the style and job dialog boxes defined by the resource file of the current printer.For example, the standard style and job dialog boxes for the LaserWriter printer driver are shown in Figure 9-3 on page 9-6 and Figure 9-5 on page 9-7, respectively. The standard dialog boxes provided by the StyleWriter printer driver are shown in Figure 9-2 on page 9-6 and Figure 9-4 on page 9-6. Each dialog box has options that the user can set. If you want to use the standard style or job dialog box provided by the printer driver for the current printer, call the
PrStlDialog
function or thePrJobDialog
function.You may wish to add some additional options to these dialog boxes so that the user can customize the printing process even further. For example, Figure 9-12 illustrates a print job dialog box with two additional checkboxes: Print Selection Only and Skip Blank Pages.
Figure 9-12 A print job dialog box with additional checkboxes
You must follow these guidelines if you alter the style or job dialog boxes:
You can customize a style or job dialog box by undertaking the following steps:
- Add additional options below the standard ones in the dialog box and don't change the standard ones--that is, don't delete, rearrange, or add new items in the existing list.
- Don't count on an item retaining its current position on the screen or in the dialog item list.
- Don't use more than half the smallest screen height for your items. (The smallest screen height is the 9-inch Macintosh Classic screen.) Printer drivers are allowed to expand the items in the standard dialog boxes to fill the top half of a 9-inch screen.
- If you want to add a lot of items to the dialog boxes, be aware this may confuse users. You should consider having your own separate dialog box in addition to the existing style and job dialog boxes.
The event filter function pointed to in the
- Use the
PrOpen
procedure to open the Printing Manager.- Use the
PrStlInit
orPrJobInit
function to initialize aTPrDlg
record. This record, described on page 9-47, contains the information needed to set up the style or job dialog box.- Define an initialization routine that appends items to the printer driver's style or job dialog box. Your initialization routine should
- use the Dialog Manager procedure
AppendDITL
to add items to the dialog box whoseTPrDlg
record you initialized withPrStlInit
orPrJobInit
- install two functions in the
TPrDlg
record: one--in thepFltrProc
field--for handling events (such as update events for background applications) that the Dialog Manager doesn't handle in a modal dialog box, and another--in thepItemProc
field--for handling events in the items added to the dialog box (for example, when the user clicks a checkbox that your application adds)- return a pointer to the
TPrDlg
record
- Pass the address of your initialization routine to the
PrDlgMain
function to display the dialog box.- Respond to the dialog box as appropriate.
- Use the
PrClose
procedure when you are finished using the Printing Manager.
pFltrProc
field of theTPrDlg
record extends the Dialog Manager's ability to handle events. When your application displays the style or job dialog box, you can use an event filter function to handle events that the Dialog Manager doesn't handle for modal dialog boxes. The chapter "Dialog Manager" in Inside Macintosh: Macintosh Toolbox Essentials describes how to write an event filter function for the Dialog Manager.The routine you supply in the
pItemProc
field of theTPrDlg
record should handle events in the items you add to the dialog box. Sometimes called a dialog hook, this routine typically responds to clicks in the radio buttons or checkboxes that you add to the dialog box.Listing 9-7 shows an application-defined routine called
DoPrintDialog
that specifies its own initialization function, calledMyPrDialogAppend
.Listing 9-7 Installing an initialization function to alter the print job dialog box
FUNCTION DoPrintDialog: OSErr; {display print job dialog box} BEGIN PrOpen; {open the Printing Manager} gPrintRec := THPrint(NewHandle(sizeof(TPrint)));{create a TPrint record} PrintDefault(gPrintRec); {use default values for the TPrint record} gPrJobDialogBox := PrJobInit(gPrintRec); {get a pointer to the } { invisible job dialog box} {use PrDlgMain to display the altered job dialog box} IF (PrDlgMain(gPrintRec, @MyPrDialogAppend)) THEN MyPrintDoc; PrClose; {close the Printing Manager} END;The application-defined routineMyPrDialogAppend
is shown in Listing 9-8. It uses the Resource Manager functionGetResource
to get a handle to an item list ('DITL'
) resource containing the two extra checkboxes shown in Figure 9-12 on page 9-33. Using the Dialog Manager procedureAppendDITL
,MyPrDialogAppend
appends the items in this item list resource to the print job dialog box. ThenMyPrDialogAppend
installs the application's event filter function for modal dialog boxes. Finally,MyPrDialogAppend
installs it own routine, calledHandleMyAppendedItems
, to handle clicks in the two newly installed checkboxes.Listing 9-8 Adding items to a print job dialog box
FUNCTION MyPrDialogAppend (hPrint: THPrint): TPPrDlg; VAR MyAppendDITLH: Handle; BEGIN IF gDITLAppended = FALSE THEN BEGIN {first, get item list resource containing checkboxes} MyAppendDITLH := GetResource('DITL', kPrintingCheckBoxes); {next, append this item list resource to job dialog box} AppendDITL(DialogPtr(gPrJobDialogBox), MyAppendDITLH, appendDITLBottom); gDITLAppended := TRUE; END; gFltrItemProc := LongInt(gPrJobDialogBox^.pFltrProc); {put an event filter function (to handle events that Dialog } { Manager doesn't handle in modal dialog boxes) } { in the pFltrProc field of the TPrDlg record} gPrJobDialogBox^.pFltrProc := ProcPtr(@MyEventFilter); gPrItemProc := LongInt(gPrJobDialogBox^.pItemProc); {put a dialog hook to handle clicks in appended items } { in the pItemProc field of the TPrDlg record} gPrJobDialogBox^.pItemProc := ProcPtr(@HandleMyAppendedItems); MyPrDialogAppend := gPrJobDialogBox; END;See the chapter "Dialog Manager" in Inside Macintosh: Macintosh Toolbox Essentials for information about item list resources, event filter functions for modal dialog boxes, and theAppendDITL
procedure. See the chapter "Resource Manager" in Inside Macintosh: More Macintosh Toolbox for information about theGetResource
function.Writing an Idle Procedure
The printer driver for the current printer periodically calls an idle procedure while it sends a document to the printer. TheTPrJob
record contained in theTPrint
record contains a pointer to an idle procedure in thepIdleProc
field. If this field contains the valueNIL
, then the printer driver uses the Printing Manager's default idle procedure. The default idle procedure checks for Command-period keyboard events and sets theiPrAbort
error code if one occurs, so that your application can cancel the print job at the user's request. However, the default idle procedure does not display a print status dialog box. It is up to the printer driver or your application to display a print status dialog box.Most printer drivers display their own status dialog boxes. However, your application can display its own status dialog box that reports the current status of the printing operation to the user. If it does, your status dialog box should allow the user to press Command-period to cancel the printing operation, and it may also provide a button allowing the user to cancel the printing operation. To handle update events in your status dialog box, Command-period keyboard events, and clicks in your Cancel button (if you provide one), you should provide your own idle procedure. (See Figure 9-9 on page 9-12 for an example of an application-defined status dialog box.)
Here are several guidelines you must follow when writing your own idle procedure.
Listing 9-9 shows an application-defined idle procedure.
- If you designate an idle procedure, you must set the
pIdleProc
field of theTPrJob
record after presenting the style and job dialog boxes, validating theTPrint
record, and initializing the fields in theTPrint
record (because the routines that perform these operations may reset thepIdleProc
field toNIL
). TheTPrJob
record is described on page 9-44.- You must install your idle procedure in the
TPrint
record before calling thePrOpenDoc
function. Otherwise, some printer drivers do not give the idle procedure any time to run.- Do not attempt any printing from within the idle procedure, because the Printing Manager is not reentrant.
- Do not reference global variables unless you set up your own A5 world (as described in Inside Macintosh: Processes).
- If you use a modal dialog box to display printing status information, you must call the Event Manager function
WaitNextEvent
(described in the chapter "Event Manager" in Inside Macintosh: Macintosh Toolbox Essentials) to capture mouse events or the Command-period keyboard event that signals that the user wants to cancel printing. Do not call theWaitNextEvent
function unless you display a modal dialog box.- So that your application doesn't draw into a printing port, don't call the QuickDraw
OpenPicture
function or theDrawPicture
procedure from your idle procedure without changing the current graphics port.- Upon entry to the idle procedure, you must save the printing graphics port, and you must restore it upon exit if you draw anything within the idle procedure. If you don't save and restore the printing graphics port, upon return the printer driver draws into the graphics port of your dialog box instead of its own printing graphics port. To save the printer's printing graphics port, call the
GetPort
procedure when entering the idle procedure. Before you exit, call theSetPort
procedure to set the port back to the printer driver's printing graphics port. (TheGetPort
andSetPort
procedures are described in the chapter "Basic QuickDraw" in this book.)- If your idle procedure changes the resource chain, you should save the reference number of the printer driver's resource file by calling the
CurResFile
function at the beginning of your idle procedure. (Any routine that changes the value of the global variableTopMapHdl
, such as theOpenResFile
function or theUseResFile
procedure, changes the resource chain. Some printer drivers assume the resource chain does not change, and you may get an error if you change it.) When you exit from the idle procedure, restore the resource chain using theUseResFile
procedure. If you are not changing the resource chain, you do not need to save the resource chain. (TheCurResFile
,OpenResFile
, andUseResFile
routines are described in the chapter "Resource Manager" in Inside Macintosh: More Macintosh Toolbox.)- Avoid calling the
PrError
function within the idle procedure. Errors that occur while it is executing are usually temporary and serve only as internal flags for communication within the printer driver, not for the application. If you absolutely must callPrError
within your idle procedure and an error occurs, do not cancel printing. Wait until the last called printing procedure returns and then check to see if the error still remains.
PROCEDURE MyDoPrintIdle; VAR oldPort: GrafPtr; cursorRgn: RgnHandle; event: EventRecord; gotEvent: Boolean; itemHIt: Integer; handled, canceled: Boolean; BEGIN GetPort(oldPort); SetPort(gPrintStatusDlg); cursorRgn := NIL; gotEvent := WaitNextEvent(everyEvent, event, 15, cursorRgn); IF gotEvent THEN BEGIN handled := MyStatusHandleEvent(gPrintStatusDlg, event, itemHit); canceled := MyUserDidCancel; IF canceled THEN itemHit := kStopButton; handled := MyDoHandleHitsInStatusDBox(itemHit); END; MyUpdateStatusInformation(canceled); {update status } { information in dialog box} SetPort(oldPort); END;The application displays a modal dialog box for its status dialog box, and then installsMyDoPrintIdle
, which calls the Event Manager functionWaitNextEvent
to capture events while the modal dialog box is displayed.The
MyDoPrintIdle
procedure first saves the current graphics port (so that it can later restore it) and then sets the current port to the graphics port of the status dialog box. Your idle procedure should save, set, and restore the graphics port in this manner to avoid accidentally drawing in the printing graphics port.The
MyDoPrintIdle
procedure then callsWaitNextEvent
to get the current event and then calls its own routine to handle the event. (For example, theMyStatusHandleEvent
function handles update and activate events.) By callingWaitNextEvent
,MyDoPrintIdle
gives background applications a chance to handle update events in their windows while the application in this example displays a modal dialog box. (The Dialog Manager does not give background applications a chance to handle update events in their windows when a modal dialog box is displayed.)
MyDoPrintIdle
then calls another application-defined procedure to determine whether the user wishes to cancel the printing operation. TheMyUserDidCancel
function scans the event queue for keyboard events and mouse events. If it finds a Command-period keyboard event, it returnsTRUE
. TheMyUserDidCancel
function also returnsTRUE
if it finds mouse events indicating that the user clicked the Stop Printing button (that is, it uses the Dialog Manager functionFindDialogItem
to determine whether the mouse location specified in a mouse event is in the Stop Printing button). If the user clicks the Stop Printing button,MyUserDidCancel
highlights the button appropriately.To handle hits in the status dialog box, the
MyDoHandleHitsInStatusDBox
function simply checks the item number passed to it. For the Stop Printing button,MyDoHandleHitsInStatusDBox
callsPrSetError
, specifying the error codeiPrAbort
. For all other items,MyDoHandleHitsInStatusDBox
sets the cursor to a spinning wristwatch cursor.Finally, the
MyDoPrintIdle
procedure updates the items in the status dialog box that report status to the user.Handling Printing Errors
You should always check for error conditions while printing by calling thePrError
function. Errors returned may include AppleTalk and Operating System errors in addition to Printing Manager errors.
If you determine that an error has occurred after the completion of a printing routine, stop printing. Call the close routine that matches any open routine you have called. For example, if you call
- Note
- Don't call
PrError
from within your idle procedure. See "Writing an Idle Procedure" on page 9-35 for more information.PrOpenDoc
and receive an error, skip to the next call toPrCloseDoc
; if you callPrOpenPage
and get an error, skip to the next calls toPrClosePage
andPrCloseDoc
. Remember that, if you have called some open routine, you must call the corresponding close routine to ensure that the printer driver closes properly and that all temporary memory allocations are released and returned to the heap.If you are using the
PrError
function and thePrGeneral
procedure (described in "Getting and Setting Printer Information" beginning on page 9-26), be prepared to receive the following errors:noSuchRsl
,opNotImpl
, andresNotFound
. In all three cases, your application should be prepared to continue to print without using the features of that particular opcode.The
noSuchRsl
error means that the currently selected printer does not support the requested resolution. TheopNotImpl
error means that the currently selected printer does not support the particularPrGeneral
opcode that you selected. TheresNotFound
error means the current printer driver does not support thePrGeneral
procedure at all. This lack of support should not be a problem for your application, but you need to be prepared to deal with this error. If you receive aresNotFound
result code fromPrError
, clear the error with a call toPrSetError
with a value ofnoErr
as the parameter; otherwise,PrError
might still contain this error the next time you check it, which would prevent your application from printing.Do not display any alert or dialog boxes to report an error until the end of the printing loop. Once at the end, check for the error again; if there is no error, assume that the printing completed normally. If the error is still present, then you can alert the user. This technique is important for two reasons.
- First, if you display a dialog box in the middle of the printing loop, it could cause errors that might terminate an otherwise normal printing operation. For example, if the printer is connected to an AppleTalk network, the connection might be terminated abnormally because the printer driver would be unable to respond to AppleTalk requests received from the printer while the dialog box was waiting for input from the user. If the printer does not hear from the Macintosh Operating System within a short period of time (anywhere from 30 seconds to 2 minutes, depending on the driver), it assumes that the Macintosh computer is no longer connected to the printer and times out. The timeout results in a prematurely broken connection, causing another error, to which the application must respond.
- Second, the printer driver may have already displayed its own dialog box in response to an error. In this instance, the printer driver posts an error to let the application know that something went wrong and it should cancel printing. For example, when a LaserWriter printer driver detects that the user has canceled printing, the driver posts an error to let the application know that it needs to cancel printing. Because the driver has already taken care of the error by displaying a dialog box, the error is reset to 0 before the printing loop is complete. The application should check for the error again at the end of the printing loop, and, if appropriate, the application can then display a dialog box.
Subtopics
- Creating and Using a TPrint Record
- Printing a Document
- Printing From the Finder
- Providing Names of Documents Being Printed
- Printing Hints
- Getting and Setting Printer Information
- Determining and Setting the Resolution of the Current Printer
- Determining Page Orientation
- Enhancing Draft-Quality Printing
- Altering the Style or Job Dialog Box
- Writing an Idle Procedure
- Handling Printing Errors