Sep 99 Getting Started
Volume Number: 15
Issue Number: 9
Column Tag: Getting Started
More Printing
by Dan Parks Sydow
How a Mac program opens and prints a document
In last month's Getting Started article you were introduced to printing. In that article
we described the basics of the Printing Manager and the basics of getting an application
to print. In that article's PrintPict example program you saw how a program could
load PICT resource data to memory and then print the resulting picture. While that
example served the purpose of demonstrating how to send data to a printer, it didn't
provide an entirely realistic environment in which to implement printing. This month
we'll remedy that. Here we'll dig a little deeper into printing techniques to see how a
program provides the user with the ability to open any PICT file (one of the most
common formats for Macintosh picture files), display the file's picture in a window,
and then print that picture. This month's PrintDoc example is a menu-driven program
that provides you with all the code you need to easily add a document-printing feature
to your own program.
Printing Basics Recap
Last month you saw how to write a program that displays the two standard printing
dialog boxes found in most Mac programs. Figures 1 and 2 provide examples of the
Printing Style dialog box and the Printing Job dialog box. Recall that the exact look of
these dialog boxes is dependent on the printer connected to the user's Mac.
Figure 1. A typical Printing Style dialog box.
Figure 2. A typical Printing Job dialog box.
To display the Printing Style dialog box your program calls the Printing Manager
function PrStlDialog(). To display the Printing Job dialog box your program makes a
call to the Printing Manager function PrJobDialog(). Before calling either function
your program must declare a THPrint variable and call NewHandle() or
NewHandleClear() in order to create a new print record and to receive a handle to that
record. A call to PrintDefault() then fills the record with default values. User-entered
values originating in either the Printing Style or Printing Job dialog boxes can alter
some or all of the values in this record.
THPrint printRecord;
Boolean doPrint;
printRecord = (THPrint)NewHandleClear( sizeof (TPrint) );
PrintDefault( printRecord );
PrStlDialog( printRecord );
doPrint = PrJobDialog( printRecord );
For printing a document, the display of the Printing Style and Printing Job dialog
boxes are important, user-friendly steps. However, printing can be accomplished
without calls to PrStlDialog() and PrJobDialog(). Printing can't take place, though,
without making calls to a few other very important Printing Manager functions.
A call to PrOpen() prepares the current printer resource file (the one associated with
the printer the user has selected from the Chooser) for use. PrOpenDoc() initializes a
printing graphics port-the port to which all subsequent QuickDraw commands are to
be sent. PrOpenPage()begins the definition of a single page to be printed.
PrClosePage() signals the end of the current page. PrCloseDoc() closes a printing
graphics port. PrClose() closes the Printing Manager and the printer resource file.
TPPrPort printerPort;
PrOpen();
printerPort = PrOpenDoc( printRecord, nil, nil );
PrOpenPage( printerPort, nil );
// QuickDraw calls that define what's to be printed
PrClosePage( printerPort );
PrCloseDoc( printerPort );
PrClose();
Printing a Document
When last month's example program is run, the Printing Style and Printing Job dialog
boxes mysteriously appear and a picture from an unknown (to the user) source
prints. No window opens, so to the user, there's no apparent origin of the graphics that
get printed. A more realistic use of printing would be for an application to bring up the
Printing Style and Printing Job dialog boxes only when requested by the user. When
the user opts to print, then the contents of the frontmost window should be sent to the
printer.
To implement a useful printing scheme, begin by creating a print record. Use a global
variable to keep track of the record. Call PrintDefault() to fill the record with default
values.
THPrint gPrintRecord;
gPrintRecord = (THPrint)NewHandleClear( sizeof(TPrint) );
PrOpen();
PrintDefault( gPrintRecord );
PrClose();
In the above code, the call to the Printing Manager function PrintDefault() is nested
between calls to PrOpen() and PrClose(). Last month's example called PrOpen() near
the start of the program and PrClose() near the end. That worked fine for our short,
simple program. For a more sophisticated program, it's wiser to open and close the
printing resource file at each use. Recall that when an application launches, its
resource fork is opened by the system. When PrOpen() is called, the printer resource
file is opened. At that point, the program has two resource files open. When making
certain Toolbox calls, this has to be kept in mind (as it's possible for two resource
forks to each include a resource of the same type and ID). If your program closes the
printing resource after using it, you won't have to be concerned with issues such as
which resource file is considered the current file.
Your printing-ready program should include a Page Setup item in the File menu.
Handling a Page Setup choice involves little more than calling PrStlDialog(). A File
menu-handling routine would include a case section similar to this one:
case iPageSetup:
PrOpen();
PrStlDialog( gPrintRecord );
PrClose();
break;
Your print-capable program will of course include a Print item in the File menu.
Handling a Print choice begins with a determination of the front window. A call to the
Toolbox routine FrontWindow() takes care of that task. Next, a call to PrJobDialog()
displays the Printing Job dialog box. If from this dialog box the user cancels printing,
there's nothing your program needs to do. If the user instead clicks the Print button,
you'll invoke an application-defined function that carries out the printing.
WindowPtr window;
Boolean doPrint;
case iPrint:
window = FrontWindow();
PrOpen();
doPrint = PrJobDialog( gPrintRecord );
if ( doPrint == true )
DoPrintChoice( window );
PrClose();
break;
Regardless of the contents of the window, your print-handling routine should look
similar to the DoPrintChoice() function shown below. That's because this function does
the busy work of getting ready for printing and cleaning up after printing. What
actually gets printed is determined in the application-defined DrawToPort() routine.
void DoPrintChoice( WindowPtr window )
TPPrPort printerPort;
printerPort = PrOpenDoc( gPrintRecord, nil, nil );
PrOpenPage( printerPort, nil );
DrawToPort( window );
PrClosePage( printerPort );
PrCloseDoc( printerPort );
}
PrintDoc
This month's program is PrintDoc. When you run PrintDoc, you see a menu bar that
holds the File menu pictured in Figure 3. Choosing the Open item displays the standard
Open dialog box pictured in Figure 4. From this dialog box you can select and open any
picture file (any file of type PICT) you have available.
Figure 3. The File menu from the PrintDoc program.
Figure 4. Choosing a picture file to open.
After selecting a file, PrintDoc opens it, loads the picture data to memory, and displays
the resulting picture in its own window. Figure 5 shows that PrintDoc sizes the
window to match the size of the picture. A window can be dragged and closed. PrintDoc
allows for any number of windows to be open (memory permitting). When one or more
windows are on the screen, the program enables the File menu items that are initially
disabled. As shown in Figure 3, these are the Close, Page Setup, and Print items.
Closing all windows again disables those same items.
Figure 5. A typical picture displayed in the PrintDoc program.
Choosing Page Setup from the File menu displays the Printing Style dialog box.
Choosing Print from the File menu displays the Printing Job dialog box. If you click
the Print button, the program prints the picture that's in the frontmost window.
Because the program doesn't handle any type of editing, all of the items in the Edit
menu are disabled.
Creating the PrintDoc Resources
Start the project by creating a new folder named PrintDoc in your CodeWarrior
development folder. Start up ResEdit, then create a new resource file named
PrintDoc.rsrc. Make sure to specify the PrintDoc folder as the resource file's
destination. The resource file will hold resources of the types shown in Figure 6.
Figure 6. The PrintDoc resources.
Figure 6 shows the three MENU resources. After creating these resources, create a
single MBAR resource that includes MENU IDs 128, 129, and 130.
The one ALRT and one DITL resource are used to define the program's error-handling
alert. The only other resource needed is a single WIND. This resource serves as the
window that's to display a picture. Because the program resizes the window to match
the size of the picture that's to be displayed, the WIND boundaries you specify are
unimportant.
Creating the PrintDoc Project
Launch CodeWarrior and choose New Project from the File menu. As usual, choose the
MacOS:C_C++:MacOS Toolbox:MacOS Toolbox Multi-Target project stationary for the
new project. You already have a project folder created, so uncheck the Create Folder
check box before clicking the OK button. Name the project PrintDoc.mcp, and make
sure the project's destination is the PrintDoc folder.
Add the PrintDoc.rsrc resource file to the project. Remove the SillyBalls.rsrc file.