Previous Book Contents Book Index Next

Inside Macintosh: Programmer's Guide to MacApp / Part 1 - MacApp Theory and Architecture
Chapter 8 - Displaying, Manipulating, and Printing Data


MacApp applications use view objects both to display documents and to print them. You associate a print handler with a view and the print handler converts the view's drawing instructions into printable output. Print-handler objects also manage the page setup and print options dialogs, and handle printing-related menu commands, including Print, Print One, and Show Page Breaks.

To add customized page layout for your documents, you subclass one of the print-handling classes provided by MacApp and override the appropriate methods, as described in "Behind the Scenes" on page 232. For additional information on printing, see Chapter 23, "Working With Printing."

Print-Handling Classes

Printing in MacApp is managed by associating a print-handler object with a view object. The view object draws the view and the print handler converts the drawn image into printable output.

MacApp provides two print-handling classes:

The TPrintHandler class defines the minimal print-handler interface.
The TStdPrintHandler class, a subclass of TPrintHandler, adds capabilities to print a view and handle the Page Setup and Print menu commands.
Because print-handling classes descend from MacApp's TBehavior class, they can handle events, including menu commands. When the user selects the Print command from the File menu, the print handler takes care of all the details of printing, including spooling and communicating with the Macintosh Print Manager. The print-handling object calls drawing methods in its associated view object, then directs the drawn output to the printer.

Since a print handler is always associated with a view, it is usually created at the same time as its view, in the DoMakeViews method of a document object. The print handler prints the view and all of the associated subviews, so you only need to create one print handler to print an entire view hierarchy.

A great deal of flexibility is built into the standard print handler, so you may not need to define a print-handling subclass. If you do need to provide custom printing, you normally do so by overriding methods of TStdPrintHandler. Less commonly, you may define a subclass of the simpler TPrintHandler class.

Recordable Printing

MacApp's TPrintCommand class supports recordable printing. You initialize a print command by passing it a command number and a reference to a print-handler object. The print command's DoIt method calls the Print method of its print handler. Its MakeAppleEvent method creates an Apple event to describe the printing operation. If the print handler has a document object reference, the Apple event's specified object refers to the document; otherwise, it refers to the window containing the print handler's view.

Creating Views for Displaying and Printing

When you create a window and its full view hierarchy by calling NewTemplateWindow (page 217), you can use the window for displaying as well as printing.

The situation is slightly different when a user prints your application's documents from the Finder and the application is launched to do the printing. Since MacApp printing is based on views, you can create a view to print the document without the additional overhead of creating a window. To create a view hierarchy for printing without also creating a window, you call the DoCreateViews method instead of the NewTemplateWindow method.

Behind the Scenes

Printing is a complicated process that includes page setup, page printing, maintenance of page description areas, and interaction with the Macintosh Print Manager. MacApp can handle all of these operations, but if you want to customize your printing support, you'll want to know more about how MacApp implements printing.


Your application calls the InitUPrinting method to initialize printing. InitUPrinting creates a printer object of type TMacPrintPrinter (a subclass of TPrinter) and stores a reference to it in the global variable gDefaultPrinter. MacApp's global printer object works with the CInMacPrint print utility class to optimize performance by minimizing calls to the printer driver.

The TPrinter class is a generic superclass that encapsulates a printer driver. The TMacPrintPrinter subclass works with normal (Macintosh Print Manager) printing. In the future, additional classes may provide drivers to support QuickDraw GX or other technologies.

The TPrintInfo class encapsulates a print info handle for printing with the Macintosh Print Manager. Again, a similar class (though not necessarily with a common superclass) may be added in the future to encapsulate a GXJobinfo record.

In addition to preparing the way for future compatibility with QuickDraw GX and minimizing calls to the printer driver, the TPrinter, TMacPrintPrinter, and TPrintInfo classes improve performance by caching information such as page size, rotation, resolution, and so on.

Page Setup

The TStdPrintHandler class handles the Page Setup menu command and the page setup dialog. To make changes to the page setup dialog undoable, TStdPrintHandler creates a TPrintStyleChangeCommand object when the user clicks OK in the page setup dialog box. The DoIt method of this class sends information to the Macintosh Print Manager to configure the printer for the specified setup.

Different Setups Required for Print Commands

A user can print a document directly by choosing the application's Print or Print One command from the File menu. A user can also select an application document in the Finder and choose the Print menu command, causing the Finder to send the application a Print Documents event. The application may also receive a Print event from a script. In each case, MacApp calls the DoMenuCommand method of the print handler for the view to be printed, or of a TPrintMenuBehavior object attached to the document to be printed. Each of these DoMenuCommand methods calls the DoPrintCommand method of the print-handler object, which handles each case in a slightly different manner:

The Print Documents event is different from most Apple events, which don't put up a dialog box, in that it may cause MacApp to display a print dialog box. This is a result of the design of the Print event, not a limitation in MacApp.

Overview of a Print Job

The Print method of a TStdPrintHandler object controls printing. This method determines how many pages are to be printed, then enters a loop, calling the OneSubJob method as many times as necessary to completely print the view. If the printed output is not spooled, the entire job is accomplished with one subjob. However, for spooled output it may be necessary to use several subjobs, depending on the amount of disk space available.

The Print method first creates a CInMacPrint object. The constructor method of the CInMacPrint class calls the Open method of its printer. For the TMacPrintPrinter class, Open calls the Toolbox routine PrOpen, which opens the printer driver and printing resource file and initializes the Printing Manager.

The print handler's OneSubJob method calls the Toolbox routine PrOpenDoc, which initializes a graphics port to be used in subsequent calls to the Print Manager and makes it the active port. The print handler's OneSubJob method calls the PrintPage method for each page in the subjob. After printing all of the pages, the OneSubJob method calls the FinishJob method of its printer object, which in turn calls the Toolbox routine PrCloseDoc. The destructor method for the CInMacPrint object calls its Cleanup method. It in turn calls the Toolbox method PrClose, which releases the Print Manager dialog box and other resources.

Page Printing

The PrintPage method of TStdPrintHandler prints one page of a view by performing these steps:

  1. It calls its own SetPage method to compute state information for the page.
  2. It calls fPrinter->StartPage, which in turn calls the Toolbox routine PrOpenPage to ready printing of the page.
  3. It calls its own FocusOnInterior method, which focuses on the portion of the view that represents the page. The area to be drawn is stored in the view's fQDRectToClipTo field.
  4. It calls its own DrawPageInterior method, which then converts fQDRectToClipTo to view coordinates and passes them to the HandleDraw method of the view. The HandleDraw method calls the Draw method to draw the specified portion of the view.
  5. It calls its own FocusOnBorder method in anticipation of drawing page adornments that may be outside of the page interior.
  6. It calls its own AdornPage method, which draws any page adornments such as page numbers and borders.
  7. It calls fPrinter->FinishPage, which in turn calls the Toolbox routine PrClosePage.

Page Definition Areas

The dimensions of the physical page are maintained by the print-handler object in four VRect fields:

The printable part of the page, defined as a rectangle. Its upper-left corner is always (0,0), and its lower-right corner is (y,x), where y is the maximum printable page height, and x is the maximum printable page width.
The entire physical page, in the coordinate system of fInkRect. The physical page is almost always larger than its printable part; thus, its upper-left corner usually has negative coordinate values, and its lower-right corner usually has coordinate values greater than those of fInkRect.
The part of the page in which the print handler draws its view image, again in the coordinate system of fInkRect. (The value of fInteriorRect is equal to fPaperRect minus fMarginsRect.)
The top, left, bottom, and right paper margins. Although stored as a rectangle, these are really four values that define the distances from the physical page edges to the page interior, so the fInteriorRect rectangle can be computed by adding fMarginsRect to fPaperRect. As a result, the upper-left point of the fMarginsRect rectangle is positive and the lower-right point is negative.
Figure 8-8 shows the relationships between the page definition areas. The values are automatically recomputed whenever the user chooses a new printer. You can also call the InstallMargins method of the print handler to modify the margin values, which also results in changing the value of fInteriorRect, as described in "Installing New Margins," beginning on page 536.

Figure 8-8 Page dimensions

Customizing the Page

The TStdPrintHandler class implements several methods you can use to change the default page setup. You can modify the margin settings (described in "Recipe--Changing Default Margin Settings," beginning on page 535), customize the way in which the view is separated into page strips, or add custom handling of page breaks. Page strips and page breaks are described in the next section.

Page Strips and Page Breaks

The extent of a view can be larger than one page, in which case it becomes necessary to divide it into pages for printing purposes. MacApp uses a model that divides a view into vertical or horizontal page strips. You can think of the view as being divided by horizontal and vertical lines, called page breaks, which produce a "checkerboard" of pages. A vertical column of these pages, one page wide, is called a vertical page strip. A horizontal row of these pages, one page high, is called a horizontal page strip.

Most word-processing programs print a single vertical page strip, whereas many graphical applications print multiple page strips in both dimensions. The size of these page strips can be uniform or variable. Page strips of varying sizes are used when a different portion of a view is printed on different pages, for example, to ensure that page breaks occur on line boundaries.

Your application has a great deal of control over how pages are divided into strips and over what happens when a page break is generated. For example, MacApp's TTEView class implements methods to facilitate mapping a view to pages and page strips. The DoCalcViewPerPage method of the TTEView class determines the standard page strip size. Unless it is overridden, this method calls the CalcViewPerPage method of the print handler, which simply returns the size of the page interior.

The number of page strips is determined by calling the DoCalcPageStrips method of the view object. Unless overridden, this method calls the CalcPageStrips method of the print handler. CalcPageStrips uses one of two techniques to determine the number of page strips in each direction:

To determine where a page break occurs, the DoBreakFollowing method of the view object is called. This method returns the view coordinate at which the next page break occurs. Unless overridden, it calls the BreakFollowing method of the print handler, which simply adds the strip size to the last page break. To implement a view with variable-size page strips, you override the DoBreakFollowing method of your view class.

If each page is the same size (in view coordinates), but the page size may need to be smaller than fInteriorRect, it makes sense to override the DoCalcViewPerPage method. But if each page could have a different amount of view printed on it, you should override DoBreakFollowing instead.

Whenever a page break does occur, the print handler calls its own EachBreak method. The TStdPrintHandler implementation of this method computes the number of page strips for variable size strips and draws page breaks on
the screen.

You can also call the GetBreakCoord method of the print handler. This method returns the location in your view of an arbitrary page break. For fixed-size page strips, the computation is simple--the strip size is multiplied by the page break number. For variable-size page strips, the computation involves calculating all of the breaks up to the specified one.

Screen Feedback

Many applications need to give the user screen feedback to show where page breaks occur, to display page numbers and headers, and so on.

Screen feedback of page breaks is handled by cooperation between the print handler and the view. Recall that when the print handler is attached to the view, a print adorner is also added to the view. The feedback process starts when the view's DrawContents method is called:

  1. The DrawContents method of the view calls the HandleDraw method of
    the view.
  2. The view's HandleDraw method manages drawing of the view's contents. After all subviews have had a chance to draw, HandleDraw calls the view's HighlightAdorners method.
  3. The HighlightAdorners method calls the DoHighlightSelection method of each adorner. For the print adorner, DoHighlightSelection calls the DoDrawPrintFeedback method of its view.
  4. The view's DoDrawPrintFeedback method calls its print handler's DrawPrintFeedback method.
  5. The print handler's DrawPrintFeedback method checks its fShowBreaks instance variable. If it is TRUE, the print handler calls the DoDrawPageBreak method of the view object.
  6. The view's DoDrawPageBreak method calls the DrawPageBreak method of the print handler.
  7. The DrawPageBreak method draws a 2-pixel-wide gray line along the page break in the view.

Although this seems like a circuitous route to provide feedback, it does provide maximum flexibility, because you can fine tune the screen feedback in either the print handler or the view.

If the gDebugPrinting variable is TRUE, the print handler provides debugging help by also drawing page numbers at the intersections of the page breaks.

The user can control whether page breaks are displayed on the screen by choosing either the Show Page Breaks or Hide Page Breaks menu item. When one of these items is chosen, the print handler calls its InvalPageFeedback method. This method changes the screen feedback status and invalidates the view, which results in the view being redrawn with the new setting.

Previous Book Contents Book Index Next

© Apple Computer, Inc.
25 JUL 1996