Printing is one of the basic functions provided by most applications. When printing a document, a user may first wish to specify settings such as the page size and orientation, the scaling percentage, the page range, and the number of copies. A user may also wish to specify a printer from a list of available printers and choose settings specific to that printer, such as paper tray, duplex printing, or print quality. The application itself may supply additional printing options, such as whether to print odd or even numbered pages or registration marks. If the user doesn’t specifically choose any setting, the application should supply reasonable default values if possible.
Because printing involves many variables, your application may have a lot of work to do to deliver robust printing support. However, Mac OS X provides features to help your application gather and work with printing information. These features involve interaction that takes place between the user, the application, the operating system, and the printer:
Your application can call on Mac OS X to display the Page Setup dialog, shown in Figure 9.1. The Page Setup dialog lets the user choose page format settings, such as paper size, orientation, and scale, before printing. You can save these settings with a document and use them again when the document is reopened.
When a user chooses to print a document, your application calls on Mac OS X to display the Print dialog, shown in Figure 9.2. The Print dialog lets the user choose print settings, such as the printer, page range, and number of copies. Applications can extend both the Page Setup and Print dialogs to add application-specific options, and printer manufacturers can also extend the Print dialog with printer-specific options.
Mac OS X provides printing data types to keep track of user selections in the Page Setup and Print dialogs. Your application uses instances of these data types in calls to printing functions. You can extend some of these types with your own data, save them with documents, and use them when the document is opened and printed.
In this chapter you’ll learn about the Carbon Printing Manager, which can help your application supply powerful printing support with a consistent user experience. You’ll learn key concepts for printing with the Carbon Printing Manager, including:
How to organize a printing session
How to set up a print loop to print a range of pages
How to handle printing errors
You’ll also write printing code for the Moon Travel Planner application. Once you’re familiar with the printing concepts described in this chapter, you should be able to adapt them for printing in other Carbon applications you write.
Mac OS X includes a flexible new printing system based on a modular client/server architecture. The printing system:
Uses PDF-based rendering, providing PDF capability for all printers, including inexpensive raster printers
Allows applications to draw in “virtual pages” and map those pages to “physical pages” at print time, breaking the connection between the drawing page and the printing page
Provides applications and printer drivers with control over individual user interface elements in the system’s printing dialog boxes, so applications and drivers don’t need to completely replace the standard Print or Page Setup dialog boxes with custom versions
Includes robust support for Carbon applications through the Carbon Printing Manager
In Mac OS X, Carbon applications can initiate multiple, simultaneous printing tasks. Each printing task is referred to as a printing session, and each printing session is independent of other printing sessions. Printing sessions can run in separate threads, or you can create multiple sessions within a single-threaded application. You’ll read more about threads in Chapter 14.
The Carbon Printing Manager was designed to let Carbon applications take advantage of new features in Mac OS X, while still working correctly in previous versions of the Mac OS. For example, the Carbon Printing manager supports printing sessions on Mac OS 8 and 9, although each application can have only one printing session running in a single thread, due to limitations of the underlying Classic printing architecture.
This book doesn’t cover how to create a Carbon application that can run on versions of the Mac OS earlier than Mac OS X, although the printing code in this chapter should compile and run on either platform. If you want to write an application that can run and print on both platforms, see the Carbon Porting Guide in Carbon Help (available in the Project Builder Help menu).
Carbon Printing Manager functions can conceptually be divided into three groups:
Session functions. These can create and manage printing sessions.
Nonsession functions. These don’t support sessions. They operate identically on all Carbon platforms, but inherit some of the limitations of the Classic Printing Manager.
Universal functions. These are available with both session and nonsession printing functions.
The rest of this chapter will deal only with the universal and session functions, which are strongly recommended for all Carbon applications.
For any printing session, an application will need to:
Instantiate a Carbon Printing Manager printing session object for the session
Instantiate a Carbon Printing Manager page format object to store information from the Page Setup dialog, such as paper size and orientation
Instantiate a Carbon Printing Manager print settings object to store information from the Print dialog, such as page range and number of copies
Instantiate an application-defined data type (call it the AppPrintInfo
structure)
to store printing information, including references to the three
previous objects, plus additional information such as pointers to
application printing data and functions
Provide certain printing functions, such as a function to draw one page of a document
The data types for printing session, page setup, and print settings objects are described in detail in Section 9.1.3 and Carbon Printing Manager functions are described in more detail in Section 9.1.4.
The main Carbon Printing Manager data types
you’ll use are PMPrintSession
, PMPageFormat
,
and PMPrintSettings
. The Carbon Printing Manager provides
functions to extend these opaque data types to contain data you
supply, but you won’t do that here. Although instances of these types
are sometimes referred to as objects, that doesn’t mean you’ll
have to write any object-oriented code. The Carbon Printing Manager
hides the underlying implementation and provides procedural functions
to access the internal data of these structures. But of course you
can freely call these functions from object-oriented code.
As you work through this sample printing session, you’ll
see a number of data objects and functions. Functions that start
with PM
are Carbon Printing
Manager functions; those that start with App
must
be defined by your application. Later sections will provide additional
information about the Carbon Printing Manager functions and data
types mentioned here. To see a full implementation of a printing
session for a working application, see Section 9.2.
Suppose a user opens a text document in the application. Your application would perform steps similar to the following:
Create a window to display the document information.
Create an instance of the AppPrintInfo
structure and
store it with the window. If your application stores page format
information with the document, extract it, call PMUnflattenPageFormat
to
create a page format object, and store a reference to the object in
the AppPrintInfo
structure; otherwise, store
an empty reference for now. Store empty references for
a printing session object and for a print settings object in the AppPrintInfo
structure.
Your application will create the objects when needed for printing. Store
references to
various application functions that will be called during printing,
such as the AppDrawPage
function, in the AppPrintInfo
structure.
If the user chooses Page Setup from the File menu, call PMCreateSession
to
create a printing session object. Also call PMCreatePageFormat
to
create a page format object, if one hasn’t already been created,
and call PMSessionDefaultPageFormat
to
set it to default values for this session. If the page
format object already exists, call PMSessionValidatePageFormat
to
ensure that the object is valid within the context of the printing
session. Call PMSessionUseSheets
to
indicate the Page Setup dialog should use sheets (a Mac OS X feature
in which a printing dialog is attached to the window of the document being
printed). Call PMSessionPageSetupDialog
to
display the Page Setup dialog. Preserve page setup information in
the page format object. When the Page Setup dialog is
dismissed, call PMRelease
to release the session object.
If the user chooses Print from the File menu, create a session
object and set up a page format object (creating it if necessary)
as in the previous step. Call PMCreatePrintSettings
to
create a print settings object and call PMSessionDefaultPrintSettings
to
set it to default values for this session. Call PMSessionUseSheets
to
indicate the Print dialog should use sheets. Call the
Carbon Printing Manager function PMSessionPrintDialog
to
display the Print dialog.
If the user accepts the Print dialog, call a print loop function,
passing the AppPrintInfo
structure to provide
the data needed for printing. Call PMGetFirstPage
and PMGetLastPage
to
get the user-specified page range, and call
AppPagesInDoc
to verify
the legal page range. Call PMSessionBeginDocument
to
begin the print job. Loop over the user-specified page
range, calling PMSessionBeginPage
and calling
your application’s AppDrawPage
function
to print each page and PMSessionEndPage
on completion. After
printing, or after the user cancels the print job, call PMSessionEndDocument
to end
the print job and PMRelease
to release
the print settings and printing session objects.
When the user saves the document, most applications save the
page format data (choices made by the user on the Page Setup dialog)
with the document. Your application can use the PMSetPageFormatExtendedData
function
to store application-specific information with the page format data.
The next section takes a more detailed look at the print loop function and the Carbon Printing Manager functions it typically calls.
The print loop is simply the piece of code that calls all the necessary functions to print a selected page range of a document. By the time the print loop function is called, your application has performed some preparation for printing, as described in the previous section: your application has created and initialized its own print information structure; it has created Carbon Printing Manager objects for the printing session, page format, and print settings; and your application’s print information structure has references to these objects.
The user may have used the Page Setup dialog to change page format options. Finally, the user has displayed the Print dialog and decided to print all or a range of pages. Your application calls the print loop function, passing a pointer to the print information structure. The following pseudocode shows the calls a typical print loop might make, with the calls divided among Carbon Printing Manager functions, other Carbon functions, and application-defined functions. Not all error handling is shown, but the print loop should check for errors after each call that may return one:
AppPagesInDoc (application function to verify legal page range) PMGetFirstPage (get the selected page range, first to last) PMGetLastPage (adjust if selected range outside legal range) (now know how many pages to print in the print loop) PMSetFirstPage (set page range that will show up in progress dialog) PMSetLastPage PMSessionBeginDocument (begin a new print job) (for each page to be printed) PMSessionError (check for an error before starting a new page; if error, break out of loop) PMSessionBeginPage (prepare to print the current page) PMSessionGetGraphicsContext (get printing port) SetPort (Carbon function to set current drawing port to printing port) AppDrawPage (application function to draw one page) SetPort (Carbon function to restore saved port) PMSessionEndPage (finish printing current page) PMSessionEndDocument (end the print job) AppPostPrintingErrors (application function to display any error to user) PMRelease (release print settings and printing session objects)
This print loop adheres to requirements described in Section 9.1.2 as well as to the error handling described in Section 9.1.1.2. You’ll notice that very few of the functions called in the print loop are application-defined functions—most are Carbon Printing Manager functions or other Carbon functions. Your application supplies functions only to determine the maximum number of pages in its document, to print one page, and to display errors (if any).
For a full-scale print loop from a working application, see
the MTPDoPrintLoop
function in Section 9.2.
Your application should always check for error conditions
while printing. As usual, you should check for an error from
every function that can return one. Within a print loop that uses
Carbon Printing Manager session functions, as described in this
chapter, you should also check for errors by calling PMSessionError
before
beginning each new page. If PMSessionError
returns
a value other than noErr
, you should
quit printing and close the page and document (depending on the
location of the error). If the error value is anything other than kPMCancel
(which
indicates the user canceled printing), you should also display an
error alert to the user.
For a simplified implementation of this type of error handling,
see the descriptions for the MTPDoPrintLoop
and MTPPostPrintingErrors
functions
in Section 9.2.
The application code that prints a range of pages is called
a print loop because it loops to print each page in the range. The
Carbon Printing Manager enforces a sequence of steps in a print
loop and defines a valid scope for each printing function. This
means that your application must call certain functions before calling
others. Functions used out of sequence generate an error value of kPMOutOfScope
. The
rules for calling sequence and scope are different for session and
nonsession printing functions. This section provides a brief overview
of the requirements for session printing. For additional details,
see the Carbon Printing Manager documentation in Carbon Help (available
in the Project Builder Help menu).
The following list of functions shows the calling sequence and scope requirements for some of the most commonly used session functions:
PMCreateSession PMSessionDefaultPageFormat PMSessionValidatePageFormat PMSessionDefaultPrintSettings PMSessionValidatePrintSettings PMSessionUseSheets PMSessionPageSetupDialog PMSessionPrintDialog PMSessionBeginDocument PMSessionBeginPage PMSessionGetGraphicsContext PMSessionEndPage PMSessionEndDocument PMRelease
In general, functions may be called in any order with respect
to other functions at the same or lower scope level (represented
in the list by indentation). For example, you can call PMSessionGetGraphicsContext
only
within the scope of a call to PMSessionBeginPage
,
which in turn must be within the scope of a call to PMSessionBeginDocument
.
But within the scope of a call to PMCreateSession
,
you can call PMSessionDefaultPageFormat
and PMSessionDefaultPrintSettings
in
any order.
The series of steps between the function calls to
PMSessionPrintDialog
and
PMSessionEndDocument
(that is, from the display of the
Print dialog until the last page is printed in the print loop) is commonly
refered to as a print job.
Naturally “begin” functions (such as PMSessionBeginPage
)
must be called before their corresponding “end” functions (PMSessionEndPage
).
And some calls make sense only in a certain order within a given
scope. For example, if you are using sheets, as the code in this chapter
does, you must call PMSessionUseSheets
before
calling the PMSessionPageSetupDialog
or PMSessionPrintDialog
function
to display a dialog as a sheet.
The following list shows some of the universal printing functions, which can be called at any time:
PMCreatePageFormat PMCreatePrintSettings PMFlattenPageFormat PMUnflattenPageFormat PMGetPageFormatExtendedData PMSetPageFormatExtendedData PMGetAdjustedPaperRect PMGetAdjustedPageRect PMGetOrientation PMSetOrientation PMFlattenPrintSettings PMUnflattenPrintSettings PMGetPageRange PMSetPageRange
Although these functions are designed to be called at any
time, it is currently required that you call PMSessionValidatePageFormat
before
calling PMGetAdjustedPaperRect
or PMGetAdjustedPageRect
.
Many of these functions are used in the sample code in this
chapter. Two functions that are not used, but that may be of interest,
are PMFlattenPageFormat
and PMUnflattenPageFormat
. You
can use these functions to save and restore information from the
opaque PMPageFormat
data structure, which stores
information displayed in the Page Setup dialog (shown in Figure 9.1). You
use PMFlattenPageFormat
, for example,
to flatten a page format object before writing it to a handle, document,
or other location, and PMUnflattenPageFormat
to recreate a PMPageFormat
object
after reading flattened data.
Because you can use the PMSetPageFormatExtendedData
function
to store application data with a page format object and the PMGetPageFormatExtendedData
function
to retrieve the data, the PMFlattenPageFormat
and PMUnflattenPageFormat
functions
provide a convenient way to save and restore additional application-specific
data along with the standard page format data.
The functions PMFlattenPrintSettings
and PMUnflattenPrintSettings
perform
a similar duty for saving and restoring information from the opaque PMPrintSettings
data structure,
which stores information displayed in the Print dialog (shown in Figure 9.2). However,
most applications will not need to save settings information. An application
can extend a print settings object with the PMSetPrintSettingsExtendedData
and PMGetPrintSettingsExtendedData
functions.
An application can extend an instance of the PMPrintSession
data
type with the PMSessionSetDataInSession
and PMSessionGetDataFromSession
functions,
but the Carbon Printing Manager does not supply functions to flatten
and unflatten an instance of this type. Therefore, you can use the
extended data structure only within the lifetime of the printing
session object (before releasing it).
Don’t look for direct access to global data structures in the Carbon Printing Manager—that’s so 20th century. Instead, the Carbon Printing Manager relies primarily on the opaque data structures shown in Table 9.1. The table also shows the functions you use to create these data types and, for those that can be extended to store application-specific data, the functions for extending them. Each of the creation functions returns a reference to an instance of the specified data type.
Table 9-1. Printing Session Data Types
Previous sections provided examples of how to use these data types. The next section provides information on functions that work with these types.
Because the primary printing data types for printing sessions
are opaque, you cannot directly access any of their internal data.
Instead, the Carbon Printing Manager defines accessor functions
for manipulating the data in these structures. Table 9.2 shows some of the accessors
and other functions available to work with an instance of the opaque
data type PMPrintSession
.
The Carbon Printing Manager doesn’t supply functions for saving an instance of a printing session because it’s not recommended that you save one. An application typically creates an instance of a printing session as needed (such as before displaying the Page Setup or Print dialog) and releases it when it is no longer needed (as when the print loop has completed).
Table 9-2. Functions for Working with the PMPrintSession Data Type
Function | Description |
---|---|
PMCreateSession
|
Initializes a |
PMRelease
|
Decrements the reference count for a printing object (such as an instance of a session, page setup, or print settings data type). When an object’s reference count reaches 0, the object is deallocated. |
PMSessionBeginDocument
|
Establishes a new print job. |
PMSessionEndDocument
|
Closes the print job started with |
PMSessionBeginPage
|
Informs the printing system that the drawing that follows is part of a new page. |
PMSessionEndPage
|
Finishes printing the current page. |
PMSessionGetCurrentPrinter
|
Obtains a reference to the |
|
Obtains (or sets) the data the application previously stored in a printing session object. |
PMSessionGetGraphicsContext
|
Obtains the graphics context associated with the current page. |
PMSessionPageSetupDialog
|
Displays the Page Setup dialog and records the user’s
selections in a |
PMSessionPrintDialog
|
Displays the Print dialog and records the user’s selections
in a |
PMSessionUseSheets
|
Specifies that a printing dialog be displayed as a sheet (that is, attached to the window of the document being printed). |
Table 9.3 shows some of the accessors and other functions available
to work with an instance of the opaque data type PMPageFormat
,
which is used to store information displayed in the Page Setup dialog.
Applications typically store page format information with documents
and also maintain it between printing sessions, because users expect changes
made in the Page Setup dialog to persist with the document. The
table describes functions for creating an instance of this data
type, setting it to default values, validating it for the current
printing session, extracting information from it, and so on.
Table 9-3. Functions for Working with the PMPageFormat Data Type
Function | Description |
---|---|
PMCreatePageFormat
|
Creates a new |
PMSessionDefaultPageFormat
|
Assigns default parameter values for the specified printing
session to an existing |
PMSessionValidatePageFormat
|
Ensures that a |
PMFlattenPageFormat
|
Flattens a |
PMUnflattenPageFormat
|
Creates a |
|
Obtains (or sets) extended page format data for the application. |
|
Obtains (or sets) the page size, taking into account orientation, application drawing resolution, and scaling settings. |
|
Obtains (or sets) the paper size, taking into account orientation, application drawing resolution, and scaling settings. |
|
Obtains (or sets) the scaling factor currently applied to the page and paper rectangles. |
Table 9.4 shows some of the accessors available to work with
an instance of the opaque data type PMPrintSettings
,
which is used to store information displayed in the Print dialog.
Applications don’t typically store print settings information,
because users expect the Print dialog to show current default values.
The table describes functions for creating an instance of this data
type, setting it to default values, validating it for the current printing
session, extracting information from it, and so on.
The Print dialog contains a Saved Settings pop-up menu that lets users select from various saved settings, so most applications shouldn’t need to worry about saving settings themselves.
Table 9-4. Functions for Working with the PMPrintSettings Data Type
Function | Description |
---|---|
PMCreatePrintSettings
|
Creates a new |
PMSessionDefaultPrintSettings
|
Assigns default parameter values for the specified printing
session to an existing |
PMSessionValidatePrintSettings
|
Ensures that a |
PMFlattenPrintSettings
|
Flattens a |
PMUnflattenPrintSettings
|
Creates a |
|
Obtains (or sets) extended print settings data for the application. |
|
Obtains (or sets) the number of copies that the user has requested to be printed. |
|
Obtains (or sets) the number of the first page to be printed. |
|
Obtains (or sets) the number of the last page to be printed. |
|
Obtains (or sets) the valid range of pages that can be printed. |