Retired Document
Important: This document may not represent best practices for current development. Links to downloads and other resources may no longer be valid.
Printing Tasks
Chapter 2, Printing Concepts for Carbon Developers, discussed the high-level tasks required to support printing in a Carbon application: setting up the page format, setting up the print settings, and printing the print job. This chapter shows you how to implement each of those tasks for a document-based Carbon application, such as a text editor or drawing application. This chapter describes the following tasks:
Setting Up the Page Format. Your application must make sure valid page format settings exist for a document.
Setting Up the Print Settings. Your application must make sure valid print settings exist for a print job.
Printing the Job. Your application must determine how many pages are in the print job and draw each page.
Handling Errors. Errors can occur at many points during the printing process. Your application should provide a function to post error messages, should they occur.
Saving a Document as a PDF File. Saving a document as a portable document format (PDF) file in Mac OS X uses code similar to the code you need to print a document to a printer. With very little effort on your part, your application can provide users with the ability to save their documents to a file that uses this popular format.
Printing One Copy. Your application can provide a shortcut command (Print One Copy) to allow users to print a single copy of a document using default settings without requiring them to open the Page Setup and Print dialogs.
Printing Multiple Copies. The printing system handles printing multiple copies for you.
This chapter contains sample code to illustrate each task you need to do to support printing. See Printing Carbon Sample Code for the sample application on which the code is based.
If you’ve installed the Mac OS X Developer Tools CD you can find additional sample applications in the following directory that show how to support printing in Mac OS X. You can compile and run the sample applications in Project Builder.
/Developer/Examples/Printing/App/
As you go through the sample code in this chapter, note that any function, data type, or constant your application must supply has the prefix My
, except for global constants, which use the prefix gMy
. Application-defined constants have the prefix kMy
. Carbon Printing Manager functions use the prefix PM.
If a line of code contains a commented number, an explanation for that line of code follows the listing.
Setting Up the Page Format
Most Carbon applications that print allow the user to choose the Page Setup command from the File menu to set options that control the page format for a document. This section shows you how to set up the page format in response to the Page Setup command. It discusses the following tasks that your application needs to perform to set up the page format for a document:
This section also shows how to flatten page format data so you can save it with a document and unflatten saved data to use it. See Saving and Retrieving Page Format Data for information on how to perform these optional tasks.
Responding to the Page Setup Command
When the user chooses Page Setup from the File menu your application needs to perform the following operations:
Create a printing session object.
Make sure there is a valid page format object for the document by performing one of the following actions:
If no page format object exists for the document, your application must create one and set it to default values for this session. (The page format object is not usable until it has default values.)
If the page format object already exists, validate the object against the printing session object. Validation updates any fields in the page format object that need to be calculated, such as the adjusted page and paper rectangles.
If the page format object doesn’t exist, but the document has page format data saved with it, your application needs to unflatten the saved data to create a page format object that contains the saved settings.
Call the function
PMSessionUseSheets
to indicate that the Page Setup dialog should use sheets. Although sheets are not available in Mac OS 9 and earlier, you should call this function if your application runs in Mac OS X as well as Mac OS 9. In Mac OS 9, the function returns a result code that indicates sheets are not available.Call
PMSessionPageSetupDialog
to display the Page Setup dialog so the user can specify settings such as paper size and orientation. In Mac OS X, the dialog is displayed as a sheet as long as you called the functionPMSessionUseSheets
.Preserve page setup information. Users expect page setup information to persist with the document. Your application should save the page format data with the document.
Release the printing session object and handle errors, if any occur.
One of the major design decisions you must make for your application is how to handle the page format object for the document. The page format should persist with the document after the user has dismissed the Page Setup dialog. In other words, the next time the user opens the Page Setup dialog for the document, the settings should be the same as they were when the user last closed the Page Setup dialog for that document. The settings should persist when the user quits the application, launches it again, and then opens the document.
There are a variety of ways you can handle page format data for a document. The sample code in this chapter stores the page format object in a structure and attaches it to a document window as a property. The structure is simple; it contains only two items. Most document-based applications use a larger structure to store all information about the document, including printing-related information. You need to handle the page format data in a way that’s best for your application.
The sample code assumes the following structure is defined:
typedef struct MyDocData |
{ |
PMPageFormat pageFormat; |
PMPrintSettings printSettings; |
}MyDocData; |
The structure MyDocData
contains two fields, one of type PMPageFormat
and another of type PMPrintSettings
. Although it is important for the page format data to persist with the document, it is not recommended that print settings data persist. However, as you’ll see in Setting Up the Print Settings, using the structure MyDocData
provides a convenient way to pass the print settings object from one function to another; the print settings are not saved with the document after the print job is cancelled or sent to a print queue.
The function MyDoPageSetup
in Listing 3-1 shows how your application can respond to the Page Setup command. Following this listing is a detailed explanation for each line of code that has a numbered comment.
Listing 2-1 A function that responds to the Page Setup command
OSStatus MyDoPageSetup (WindowRef documentWindow, |
MyDocData *docDataP)// 1 |
{ |
OSStatus err = noErr; |
if (docDataP)// 2 |
{ |
PMPrintSession printSession; |
PMPageFormat pageFormat = NULL; |
err = PMCreateSession (&printSession);// 3 |
if (!err) |
{ |
Boolean accepted; |
err = MySetupPageFormatForPrinting (printSession, |
docDataP, &pageFormat);// 4 |
if (!err) |
{ |
Boolean sheetsAreAvailable = true;// 5 |
err = PMSessionUseSheets (printSession, documentWindow, |
gMyPageSetupDoneProc); // 6 |
if (err == kPMNotImplemented)// 7 |
{ |
err = noErr; |
sheetsAreAvailable = false; |
} |
if (!err) |
{ |
err = PMSessionPageSetupDialog (printSession, |
pageFormat, &accepted);// 8 |
if (err == noErr && !sheetsAreAvailable) |
MyPageSetupDoneProc (printSession, |
window, accepted);// 9 |
} |
} |
if (err) |
(void) PMRelease (printSession);// 10 |
} |
} |
MyPostPrintingError (err, kMyPrintErrorFormatStrKey);// 11 |
return err; |
} |
Here’s what the code in Listing 3-1 does:
Passes a reference to the document window and a pointer to the data structure that contains the page format object (
PMPageFormat
) for the document. Your application can get the pointer (docDataP
) to pass to yourMyDoPageSetup
function by calling the Window Manager functionGetWindowProperty
. This assumes that you have already set up the data structure and set it as a property of the window using the Window Manager functionSetWindowProperty
.Makes sure the pointer is valid.
Calls the Carbon Printing Manager function
PMCreateSession
to create a printing session object that is used for the page format code that follows.Calls your application’s function to set up a page format object. See Setting Up a Page Format Object. Pass the printing session, the pointer to the structure
MyDocData
, and a pointer to the local page format object.Sheets are not available in Mac OS 8 and 9. If you plan to run your application in Mac OS 8 and 9 as well as in Mac OS X, you need to write your code so it acts properly in both situations. This code demonstrates how you can support each operating system. First, create a variable
sheetsAreAvailable
and set it totrue
.Calls the Carbon Printing Manager function
PMSessionUseSheets
to specify that a printing dialog (in this case the Page Setup dialog) should be displayed as a sheet.You need to pass the current printing session, the window that contains the document to be printed, and a pointer to your page setup done function. When using sheets in Mac OS X, the Carbon Printing Manager calls your function when the user dismisses the Page Setup dialog. This code assumes the application already declared a global variable:
gMyPageSetupDoneProc = NewPMSheetDoneUPP (MyPageSetupDoneProc);
If your application runs in Mac OS 8 or 9, calling the function
PMSessionUseSheets
returns the errorkPMNotImplemented
, and the function has no effect.Checks for the error
kPMNotImplemented
. If it is returned, that means your application is running in Mac OS 8 or 9, and that you are responsible for calling your procedure to handle dismissal of the Page Setup dialog. Set the constantsheetsAreAvailable
tofalse
.Calls the Carbon Printing Manager function
PMSessionPageSetupDialog
to display the Page Setup dialog. Pass the current printing session, the page format object that was set up in a previous step, and a pointer to a Boolean value. You call this function regardless of the version of the operating system. If your application is running in Mac OS X, the dialog appears as a sheet.The following is true if you are using sheets:
When the user dismisses the Page Setup dialog, the Carbon Printing Manager calls the function specified by the constant
gMyPageSetupDoneUPP
in the previous call toPMSessionUseSheets
.When using sheets, the
PMSessionPageSetupDialog
function returns immediately and the Boolean value returned in theaccepted
parameter is irrelevant because it is your Page Setup dialog done function that is called when the dialog is dismissed. If your application needs to perform additional tasks after the user dismisses the Page Setup dialog, it can do so in theMyPageSetupDoneProc
function, which is called when the user dismisses the Page Setup dialog.If the user clicks the OK button in the Page Setup dialog, the page format object is updated with the user’s changes (if any) and the value
true
is returned to theMyPageSetupDoneProc
function. If the user clicks the Cancel button, the page format object is unchanged and the valuefalse
is returned to theMyPageSetupDoneProc
function.
The following is true if you are not using sheets:
The
PMSessionPageSetupDialog
function does not return until the user dismisses the Page Setup dialog.The Boolean value returned in the
accepted
variable istrue
if the user clicks OK andfalse
if the user clicks Cancel.
If there is no error, and sheets are not available, then your application must call its Page Setup dialog done function (
MyPageSetupDoneProc
) to process the results of the Page Setup dialog and do any necessary clean up.If an error is returned from the Page Setup dialog or prior to showing the dialog, then you must release the printing session here. Otherwise you should release the printing session in your Page Setup dialog done function (
MyPageSetupDoneProc
).Calls your application’s function to display an error message. See Handling Errors for more information. If there is no error, the function does nothing.
Setting Up a Page Format Object
The function MySetupPageFormatForPrinting
, shown in Listing 3-2, makes sure there is a valid page format object for a document. The advantage to creating a separate function to take care of the page format object is that your application can call the function when it handles the Page Setup command and when it handles the Print command. Responding to the Print Command describes how to handle the Print command.
Listing 2-2 A function that sets up a page format object
static OSStatus MySetupPageFormatForPrinting ( |
PMPrintSession printSession, |
MyDocData *docDataP, |
PMPageFormat *pageFormatP)// 1 |
{ |
OSStatus status = noErr; |
PMPageFormat pageFormat = docDataP->pageFormat;// 2 |
if (pageFormat == NULL)// 3 |
{ |
status = PMCreatePageFormat (&pageFormat); |
if (status == noErr) |
{ |
status = PMSessionDefaultPageFormat (printSession, |
pageFormat); |
if (status == noErr) |
docDataP->pageFormat = pageFormat; |
else |
{ |
(void) PMRelease (pageFormat); |
pageFormat = NULL; |
} |
} |
} |
else// 4 |
{ |
status = PMSessionValidatePageFormat (printSession, pageFormat, |
kPMDontWantBoolean); |
if (status) |
{ |
docDataP->pageFormat = NULL; |
(void) PMRelease (pageFormat); |
pageFormat = NULL; |
} |
} |
*pageFormatP = pageFormat;// 5 |
return status; |
} |
Here’s what the code does:
Passes the current printing session object (created either in
MyDoPageSetup
orMyDoPrint
), a pointer to theMyDocData
structure, and a pointer to a page format object (created either inMyDoPageSetup
orMyDoPrint
).Declares a local variable to hold a page format object and set its value to the document’s page format.
If the local page format is
NULL
, calls the Carbon Printing Manager functionPMCreatePageFormat
to allocate the page format object, and then do the following:Sets the newly allocated page format object to default values by calling the Carbon Printing Manager function
PMSessionDefaultPageFormat
.If setting up defaults for the local page format object is successful, then sets the document’s page format object to the local page format object. If it isn’t successful, you need to release the local page format object and set it to
NULL
.
If the local page format is not
NULL
, then validates the local page format object within the context of the current printing session. Validating updates any values in the page format object that need to be calculated, such as the adjusted page and paper rectangles.If the validation does not succeed, you need to set the document’s page format object to
NULL
, release the local page format object, and set the local page format object toNULL
.If the code is successful so far, you need to assign the local page format object to the storage (
pageFormatP
) you passed to yourMySetupPageFormatForPrinting
function.
Handling Dismissal of the Page Setup Dialog
You need to provide a function to handle dismissal of the Page Setup dialog. If your application uses sheets, the Carbon Printing Manager calls this function when the user dismisses the Page Setup dialog. Otherwise, your application needs to call this function.
At a minimum this function should release the current printing session object, which shouldn’t be saved after the user dismisses the Page Setup dialog. If your application has more complicated printing needs, it may need to include code to perform other operations here. For example, your application may need to reformat pages to reflect changes the user made to scaling or paper size options in the Page Setup dialog.
The function MyPageSetupDoneProc
in Listing 3-3 shows how you can handle dismissal of the Page Setup dialog in your application. The function takes three parameters: the current printing session object, a reference to the document window, and a Boolean to indicate whether the user accepted or cancelled the Page Setup dialog. The function does two things: releases the printing session object and calls your application’s function to post a printing error, should one occur. See Handling Errors for information on the error-posting function.
Listing 2-3 A function to handle dismissal of the Page Setup dialog
static pascal void MyPageSetupDoneProc (PMPrintSession printSession, |
WindowRef documentWindow, |
Boolean accepted) |
{ |
#pragma unused (documentWindow, accepted) |
OSStatus err = PMRelease (printSession); |
if (err) |
MyPostPrintingError (err, kMyPrintErrorFormatStrKey); |
return; |
} |
Saving and Retrieving Page Format Data
When the user saves a document, most applications save the page format data with it, which consists of choices made by the user in the Page Setup dialog. Because a page format object is an opaque data type, you need to flatten the object before you can save it. When you want to use the data, you need to unflatten it to restore the original page format object.
Listing 3-4 shows an example of how you can flatten a page format object so it can be saved with the document. The MyFlattenAndSavePageFormat
function assumes the caller passes a validated page format object. The function PMFlattenPageFormat
flattens a page format object to a handle before the application writes it to a document or other location.
Listing 2-4 Saving page format data
OSStatus MyFlattenAndSavePageFormat (PMPageFormat pageFormat) |
{ |
OSStatus status = noErr; |
Handle flatFormatHandle = NULL; |
if (pageFormat != kPMNoPageFormat) |
{ |
status = PMFlattenPageFormat (pageFormat, &flatFormatHandle); |
if (status == noErr) |
// In this sample code we simply put it in a global variable. |
// Replace this line with your code to write the data to a file. |
gflatPageFormat = flatFormatHandle; |
} |
return status; |
} |
Listing 3-5 shows a function—MyLoadAndUnflattenPageFormat
—that gets flattened page format data and returns a page format object. The function PMUnFlattenPageFormat
converts the flattened data to a page format object.
Listing 2-5 Retrieving page format data
OSStatus MyLoadAndUnflattenPageFormat (PMPageFormat* pageFormat) |
{ |
OSStatus status; |
Handle flatFormatHandle = NULL; |
// This sample code copies flattened data from a global. |
// Replace this line with your code to obtain the flattened data |
// from your document. |
flatFormatHandle = gflatPageFormat; |
status = PMUnflattenPageFormat (flatFormatHandle, pageFormat); |
return status; |
} |
Setting Up the Print Settings
Most Carbon applications that support printing allow the user to choose Print from the File menu to set options that control how a document is printed. This section shows you how to respond to the Print command issued when the user chooses Print from the File menu. It discusses the following tasks that your application needs to do to set up print settings for a document:
The sample code assumes the following structure is defined:
typedef struct MyDocData |
{ |
PMPageFormat pageFormat; |
PMPrintSettings printSettings; |
}MyDocData; |
See Setting Up the Page Format for more information about this structure.
Responding to the Print Command
When the user chooses Print from the File menu, your application needs to do the following:
Create a printing session object.
Check for a valid page format object; if there isn’t one, create it.
Create a print settings object and set it to default values for this session. A print settings object (
PMPrintSettings
) is an opaque object that stores information such as the number of copies and range of pages. An application creates an instance of this object by calling the functionPMCreatePrintSettings
. Apple recommends that you do not save the print settings object with the document, as it is intended to describe print settings for a specific printing session.Call the function
PMSessionUseSheets
to indicate that the Print dialog should use sheets. Although sheets are not available in Mac OS 9 and earlier, you should call this function if your application runs in Mac OS X as well as Mac OS 9. In Mac OS 9, the function returns a result code that indicates sheets are not available.Call the Carbon Printing Manager function
PMSessionPrintDialog
to display the Print dialog so the user can specify settings such as page range and number of copies before printing. In Mac OS X, the dialog is displayed as a sheet as long as you called the functionPMSessionUseSheets
.Release the printing session object, set the print settings object to
NULL
, and handle errors, if any occur. Apple recommends that your application does not save print settings from one session to the next, which is why you need to set the print settings object toNULL
.
The function MyDoPrint
in Listing 3-6 shows how your application can respond to the Print command. Following this listing is a detailed explanation for each line of code that has a numbered comment.
Listing 2-6 A function that responds to the Print command
OSStatus MyDoPrint (WindowRef documentWindow, |
MyDocData *docDataP)// 1 |
{ |
OSStatus status = noErr;// 2 |
PMPrintSettings printSettings = NULL; |
PMPageFormat pageFormat = NULL; |
UInt32 minPage = 1, maxPage; |
PMPrintSession printSession; |
status = PMCreateSession (&printSession);// 3 |
if (status == noErr) |
{ |
status = MySetupPageFormatForPrinting (printSession, |
docDataP, &pageFormat);// 4 |
if (status == noErr) |
{ |
status = PMCreatePrintSettings (&printSettings);// 5 |
if (status == noErr) |
{ |
status = PMSessionDefaultPrintSettings (printSession, |
printSettings);// 6 |
if (status == noErr) |
{ |
CFStringRef windowTitleRef; |
status = CopyWindowTitleAsCFString (documentWindow, |
&windowTitleRef);// 7 |
if (status == noErr) |
{ |
status = PMSetJobNameCFString (printSettings, |
windowTitleRef);// 8 |
CFRelease (windowTitleRef);// 9 |
} |
} |
} |
if (status == noErr) |
{ |
maxPage = MyGetDocumentNumPagesInDoc (docDataP);// 10 |
status = PMSetPageRange (printSettings, // 11 |
minPage, maxPage); |
} |
if (status == noErr) |
{ |
Boolean accepted; |
Boolean sheetsAreAvailable = true;// 12 |
docDataP->printSettings = printSettings;// 13 |
status = PMSessionUseSheets (printSession, |
documentWindow, |
gMyPrintDialogDoneProc);// 14 |
if (status == kPMNotImplemented)// 15 |
{ |
status = noErr; |
sheetsAreAvailable = false; |
} |
if (status == noErr) |
{ |
status = PMSessionPrintDialog (printSession, |
printSettings, |
pageFormat, |
&accepted);// 16 |
if (status == noErr && !sheetsAreAvailable)// 17 |
MyPrintDialogDoneProc (printSession, |
parentWindow, accepted); |
} |
} |
} |
if (status != noErr)// 18 |
{ |
if (printSettings) |
{ |
docDataP->printSettings = NULL;// 19 |
(void) PMRelease (printSettings); |
} |
(void) PMRelease (printSession); // 20 |
} |
} |
MyPostPrintingError (status, kMyPrintErrorFormatStrKey);// 21 |
return status; |
} |
Here’s what the code in Listing 3-6 does:
Passes a reference to the document window and a pointer to the data structure that contains the page format and print settings structures for the document. Your application can get the pointer (
docDataP
) to pass to yourMyDoPrint
function by calling the Window Manager functionGetWindowProperty
. This assumes that you have already set up the data structure and set it as a property of the window using the Window Manager functionSetWindowProperty
.Sets up the local variables you need for this function. You need to declare local variable to hold print settings and page format objects and set them to
NULL
. You need two variables—minPage
andmaxPage
—for getting and setting the page range. You need to declare a variable to hold a printing session object.Calls the Carbon Printing Manager function
PMCreateSession
to create a printing session object.Calls your application’s function to set up a page format object. See Setting Up a Page Format Object. Pass the printing session, the pointer to the structure
MyDocData
, and a pointer to storage for the local page format object.Calls the Carbon Printing Manager function
PMCreatePrintSettings
to allocate a local print settings object.Sets default values for the print settings object for the current printing session.
Calls the Window Manager function
CopyWindowTitleAsCFString
. This example uses the document’s window title as the name of the print job.Calls the Carbon Printing Manager function
PMSetJobNameCFString
to set the name of the print job.Makes sure you call the Core Foundation Base Services function
CFRelease
to release theCFStringRef
you created for the document’s window title.Calls your application’s function to determine the number of pages in the document. See Calculating the Maximum Number of Pages to Print.
Calls the Carbon Printing Manager function
PMSetPageRange
to specify the actual range of pages in the document. In Mac OS X, the minimum allowable page (minPage
) appears in the From field in the Copies & Pages pane of the Print dialog and the maximum allowable page (maxPage
) appears in the To field. If the user enters a value outside of this range in the Print dialog the Carbon Printing Manager displays an alert message. The page range cannot be enforced automatically in Mac OS 8 and 9.Sheets are not available in Mac OS 8 and 9. If you plan to run your application in Mac OS 8 and 9 as well as in Mac OS X, you need to write your code so it acts properly in both situations. This code demonstrates how you can support each operating system. First, create a variable
sheetsAreAvailable
and set it totrue
.Writes the print settings object to the document’s data structure to allow the object to be accessed by your application’s Print dialog done function.
Calls the Carbon Printing Manager function
PMSessionUseSheets
to specify that a printing dialog (in this case the Print dialog) should be displayed as a sheet.You need to pass the current printing session, the window that contains the document to be printed, and a pointer to your Print dialog done function. The Carbon Printing Manager calls your Print dialog done function when the user dismisses the Print dialog. This code assumes the application already declared a global variable:
gMyPrintDoneProc = NewPMSheetDoneUPP (MyPrintDoneProc);
If your application runs in Mac OS 8 or 9, calling the function
PMSessionUseSheets
returns the errorkPMNotImplemented
, and the function has no effect.Checks for the error
kPMNotImplemented
. If it is returned, this means your application is running in Mac OS 8 or 9, and that you are responsible for calling your procedure to handle dismissal of the Print dialog. SetsheetsAreAvailable
tofalse
.Calls the Carbon Printing Manager function
PMSessionPrintDialog
, to display the Print dialog. Pass the current printing session, the print settings and page format objects that were set up in previous steps, and a pointer to a Boolean value. You call this function regardless of the version of the operating system. If your application is running in Mac OS X, the dialog appears as a sheet.The following is true if you are using sheets:
When the user dismisses the Print dialog, the Carbon Printing Manager calls the function specified by
gMyPrintDialogDoneProc
in the previous call toPMSessionUseSheets.
When using sheets, the
PMSessionPrintDialog
function returns immediately and the Boolean value returned in theaccepted
variable is irrelevant since it is your Print dialog done function that is called when the dialog is dismissed. If your application needs to perform additional tasks after the user dismisses the Print dialog, it can do so in theMyPrintDoneProc
function, which is called when the user dismisses the Print dialog.If the user clicks the OK button in the Print dialog, the print settings object is updated with the user’s changes (if any) and the value
true
is returned to theMyPrintDoneProc
function. If the user clicks the Cancel button, the print settings object is unchanged and the valuefalse
is returned to theMyPrintDoneProc
function.
The following is true if you are not using sheets:
The
PMSessionPrintDialog
function does not return until the user dismisses the Print dialog.The Boolean value returned in the
accepted
variable istrue
if the user clicks OK andfalse
if the user clicks Cancel.
If there is no error, and sheets are not available, then your application must call its Print dialog done function to handle dismissal of the Print dialog. See Handling Dismissal of the Print Dialog.
If an error is returned from the Print dialog, then you must release the printing session and print settings objects here. Otherwise you should release them in your Print dialog done function.
If there is an error, sets the print settings object stored in the document’s data structure to
NULL
.If there is an error, releases the local printing session object.
Calls your application’s function to post a printing error. See Handling Errors for more information. If there is no error, the function does nothing.
Handling Dismissal of the Print Dialog
There are a number of operations your application needs to do when the user dismisses the Print dialog. You should handle these in a Print dialog done function. This function is called by the Carbon Printing Manager if your application uses sheets. Otherwise, your application must call this function after it calls the function PMSessionPrintDialog
.
The function MyPrintDialogDoneProc
in Listing 3-7 shows how you can handle dismissal of the Print dialog in your application. It contains the minimum amount of code necessary to handle the dismissal, including calling the application’s print loop if the user accepts the Print dialog. If your application has more complicated printing needs, it may need to include code to perform other operations here. A detailed explanation for each line of code that has a numbered comment appears following the listing.
Listing 2-7 A function to handle dismissal of the Print dialog
static pascal void MyPrintDialogDoneProc (PMPrintSession printSession, |
WindowRef documentWindow, |
Boolean accepted)// 1 |
{ |
OSStatus status = noErr, tempErr; |
MyDocData *docDataP = MyGetWindowProperty (documentWindow);// 2 |
if (docDataP) |
{ |
if (accepted)// 3 |
status = MyDoPrintLoop (printSession, |
docDataP->pageFormat, |
docDataP->printSettings, |
docDataP); |
tempErr = PMRelease (ourDataP->printSettings);// 4 |
if (status == noErr) |
status = tempErr; |
docDataP->printSettings = NULL;// 5 |
} |
tempErr = PMRelease (printSession);// 6 |
if (status == noErr) |
status = tempErr; |
MyPostPrintingError (status, kMyPrintErrorFormatStrKey);// 7 |
} |
Here’s what the code in Listing 3-7 does:
The function takes three parameters: the current printing session object, a reference to document window, and a Boolean to indicate whether the user accepted or cancelled the Print dialog.
Calls your application’s function to retrieve a pointer to the structure
MyDocData
. If you set the pointer as a property of the document window using the Window Manager functionSetWindowProperty
, you can retrieve it using the Window Manager functionGetWindowProperty
. See the Window Manager documentation for more information.If the user accepts the Print dialog (
accepted
has the valuetrue
), calls your application’sMyDoPrintLoop
function to print the pages in the selected range. See Writing the Print Loop.Calls the Carbon Printing Manager function
PMRelease
to release the print settings object. Releasing an object decrements its reference count, causing it to be deallocated when the count reaches 0. By not saving print settings between calls to the Print dialog, you ensure that the dialog displays with the appropriate default settings, which is the recommended behavior.Sets the value of the print settings object to
NULL
to indicate it is no longer valid.Calls the Carbon Printing Manager function
PMRelease
to release the printing session object.Calls your application’s function to post a printing error. See Handling Errors for information on the error-posting function.
Printing the Job
This section shows you how to write the code that actually creates a print job. Printing can occur only after valid page format and print settings objects are set up. In a document-based application, the printing code is typically called when the user clicks Print in the Print dialog. An application usually calls the printing code from its print dialog done procedure. See the call to the application-defined function MyDoPrintLoop
from the function MyPrintDialogDoneProc
(in Handling Dismissal of the Print Dialog).
This section discusses the following tasks that your application needs to do to print a document:
Writing the Print Loop
An application’s print loop code does most of its work by calling Carbon Printing Manager functions. The code loops over the page range specified by the user. Each pass through the loop, your application needs to call its page drawing function to draw one page. At each step through the print loop, your code should check for errors and take appropriate action if an error occurs.
The function MyDoPrintLoop
in Listing 3-8 shows how your application can implement the print loop. You should be able to adapt this print loop code for applications with more sophisticated printing requirements. Following this listing is a detailed explanation for each line of code that has a numbered comment.
Listing 2-8 A function that implements a print loop
static OSStatus MyDoPrintLoop (PMPrintSession printSession, |
PMPageFormat pageFormat, |
PMPrintSettings printSettings, |
const MyDocData *docDataP)// 1 |
{ |
OSStatus err = noErr;// 2 |
OSStatus tempErr = noErr; |
UInt32 firstPage, lastPage, |
totalDocPages = MyGetDocumentNumPagesInDoc (docDataP);// 3 |
if (!err) |
err = PMGetFirstPage (printSettings, &firstPage);// 4 |
if (!err) |
err = PMGetLastPage (printSettings, &lastPage);// 5 |
if (!err && lastPage > totalDocPages)// 6 |
lastPage = totalDocPages; |
if (!err) |
err = PMSetLastPage (printSettings, lastPage, false);// 7 |
if (!err) |
{ |
err = PMSessionBeginDocument (printSession, printSettings, |
pageFormat);// 8 |
if (!err) |
{ |
UInt32 pageNumber = firstPage; |
while (pageNumber <= lastPage && err == noErr && |
PMSessionError (printSession) == noErr)// 9 |
{ |
err = PMSessionBeginPage (printSession, |
pageFormat, NULL);// 10 |
if (!err) |
{ |
GrafPtr oldPort = NULL; |
void *printingContext = NULL; |
GetPort (&oldPort);// 11 |
err = PMSessionGetGraphicsContext (printSession, |
kPMGraphicsContextQuickdraw, |
(void **) &printingContext);// 12 |
if (!err) |
{ |
Rect pageRect; |
SetPort ((CGrafPtr) printingContext);// 13 |
GetPortBounds (printingContext, &pageRect);// 14 |
err = MyPageDrawProc (docDataP, &pageRect, |
pageNumber)// 15 |
SetPort (oldPort);// 16 |
} |
tempErr = PMSessionEndPage (printSession);// 17 |
if(!err)err = tempErr; |
} |
pageNumber++;// 18 |
} // end while loop |
tempErr = PMSessionEndDocument (printSession);// 19 |
if (!err) |
err = tempErr; |
if (!err) |
err = PMSessionError (printSession);// 20 |
} |
} |
return err; |
} |
Here’s what the code in Listing 3-8 does:
The function takes four parameters: the current printing session object, a page format object, a print settings object, and pointer to the structure
MyDocData
(described in Setting Up the Page Format.Declares two variable to keep track of error codes. This ensures an error that could occur in one part of the print loop won’t overwrite an error that occurs in another part.
Declares variables for the page numbers of the first and last pages to be printed and the number of pages that it is possible to print. Call your application’s function (
MyDetermineNumberOfPagesInDoc
) to figure out the maximum number of pages that can be printed. See Calculating the Maximum Number of Pages to Print for more information.Calls the Carbon Printing Manager function
PMGetFirstPage
to obtain the page number entered by the user in the From field in the Copies & Pages pane of the Print dialog. If the user does not enter a value, the function returns the value of the previous call toPMSetFirstPage
, if any, or the default value.Calls the Carbon Printing Manager function
PMGetLastPage
to obtain the page number entered by the user in the To field in the Copies & Pages pane of the Print dialog. If the user did not enter a value, the function returns the value of the previous call toPMSetLastPage
, if any, or the default value.If the user specified a last page number greater than the number of pages in the document, assigns the actual last page in the document to the variable
lastPage
.Calls the Carbon Printing Manager function
PMSetLastPage
to set the last page of the page range in the print settings object for this print job. Setting the last page provides information used by the progress dialog that is shown during printing.Calls the Carbon Printing Manager function
PMSessionBeginDocument
to establish a new print job.Sets up a
while
loop over the range of pages selected for printing. Note that the loop terminates if any function returns an error (that is, if the variablestatus
has a value other thannoErr
) or if the Carbon Printing Manager functionPMSessionError
returns an error.Calls the Carbon Printing Manager function
PMSessionBeginPage
to inform the printing system that the drawing code which follows is part of a new page.Calls the QuickDraw function
GetPort
to preserve the current graphics port.Calls the Carbon Printing Manager function
PMSessionGetGraphicsContext
to obtain the QuickDraw graphics printing port for the page being printed.Calls the QuickDraw function
SetPort
to set the graphics port to the port obtained in the previous step. You must do this before calling the document’s function to draw one page.Calls the QuickDraw function
GetPortBounds
to get the page rectangle for the current printing context. Your application may prefer to use the functionsPMGetAdjustedPageRect
andPMGetAdjustedPaperRect
for its drawing.Calls your application’s function to draw the current page. See Drawing a Page for more information.
Calls the
SetPort
function again to restore the port to the one you preserved previously.Calls the Carbon Printing Manager function
PMSessionEndPage
to end the current page. You should use a temporary variable (tempErr
) to get the status for this function so it doesn’t overwrite an existing, prior error. This approach ensures that the current page is always finished and that if any error occurs the loop terminates.Increments the page count within the page-printing loop.
On completion of the page-printing loop, call the Carbon Printing Manager function
PMSessionEndDocument
to signal the completion of the print job.Calls the Carbon Printing Manager function
PMSessionError
to determine if any printing error has occurred. If the user cancels the print job, the result code iskPMCancel
.
Calculating the Maximum Number of Pages to Print
The number of pages in a document is likely to depend on a number of factors, including document-specific changes by the user (such as adding text or changing font size), as well as Page Setup and Print dialog settings. Some applications might require a separate version of this function for each kind of document they print. Typically, you’d call the page calculation function from your print function (MyDoPrint
). See Listing 3-6.
When you write your page calculation function, you should consider including code to do the following:
Call the Carbon Printing Manager function
PMGetAdjustedPaperRect
to obtain the paper size, taking into account orientation, application drawing resolution, and scaling settings. Applications can use this information to determine the number of pages in the document based on the current page format.If your application formats pages based on the page rectangle, you should instead call the Carbon Printing Manager function
PMGetAdjustedPageRect
.Call the Carbon Printing Manager function
PMGetAdjustedPageRect
to obtain the page size (the imageable area), in points, taking into account orientation, application drawing resolution, and scaling settings.Return the computed number of pages to print in the document.
Drawing a Page
The code you write to draw a page of a document needs to be tailored to support the specific needs of your application. You can call the Carbon Printing Manager function PMGetAdjustedPageRect
to obtain the page size (the imageable area), in points, taking into account orientation, application drawing resolution, and scaling settings.
Regardless of how you implement page drawing, your application should check for errors after attempting to draw each page, and take the appropriate action should an error occur. Typically, you’d call your page drawing function from your print loop function. See Listing 3-8.
Listing 3-9 shows a function that does some very simple text drawing.
Listing 2-9 A function that draws one page of a document
OSStatus MyPageDrawProc (const MyDocData *docDataP, |
const Rect *drawingRectP, |
UInt32 pageNumber) |
{ |
#pragma unused (docDataP, drawingRectP) |
OSStatus err = noErr; |
Str255 pageNumberString; |
MoveTo (72,72); |
TextFont (kFontIDHelvetica); |
TextSize (24); |
DrawString ("\pDrawing Page Number "); |
NumToString (pageNumber, pageNumberString); |
DrawString (pageNumberString); |
return err; |
} |
Handling Errors
Handling errors is critical to providing printing support in any application. An application should handle any errors it gets, making sure it displays localized error strings to the user. Providing localized strings in Mac OS X is fairly easy if you define them in a separate file named Localizable.strings
and follow the Mac OS X convention to put localized versions of the file into the appropriate language-specific folder.
You can create a Localizable.strings
file in Project Builder by choosing New File from the File menu. Then, create an empty file named Localizable.strings
and add it to the Resources group of your project. See Inside Mac OS X: System Overview for detailed information on localizing strings, string file syntax, functions for retrieving strings, and information on generating a string file automatically.
Listing 3-10 shows a function that displays an alert to notify the user that a printing error has occurred. The alert message includes a localized string and the error number. A detailed explanation for each line of code that has a numbered comment appears following the listing.
The function assumes a Localizable.strings
file exists and contains a formatting string. A typical localized formatting string for a printing application would be:
"Print error format" = "There is an error in the printing code. Error number: %d."; |
The string "Print error format"
is the key. It is the string you pass to the function in Listing 3-10 as the errorFormatStringKey
parameter. The string "There is an error in the printing code. Error number: %d."
is the localized formatting string; it uses printf-style formatting.
Listing 2-10 A function to post a printing error alert
void MyPostPrintingError (OSStatus status, |
CFStringRef errorFormatStringKey)// 1 |
{ |
CFStringRef formatStr = NULL, |
printErrorMsg = NULL; |
SInt16 alertItemHit = 0; |
Str255 stringBuf; |
if ((status != noErr) && (status != kPMCancel)) // 2 |
{ |
formatStr = CFCopyLocalizedString (errorFormatStringKey, NULL);// 3 |
if (formatStr != NULL) |
{ |
printErrorMsg = CFStringCreateWithFormat( |
NULL, NULL, |
formatStr, status);// 4 |
if (printErrorMsg != NULL) |
{ |
if (CFStringGetPascalString (printErrorMsg, |
stringBuf, sizeof (stringBuf), |
GetApplicationTextEncoding()))// 5 |
{ |
StandardAlert(kAlertStopAlert, stringBuf, |
NULL, NULL, &alertItemHit);// 6 |
} |
CFRelease (printErrorMsg); // 7 |
} |
CFRelease (formatStr); // 8 |
} |
} |
} |
Here’s what the code in Listing 3-10 does:
The function takes two parameters: a result code (
status
) and a string (CFStringRef
) that specifies a key associated with the localized format string.Checks whether the passed error should be displayed. Any error except
kPMCancel
, indicating the user cancelled printing, should be displayed.Calls the Core Foundation Bundle Services macro
CFCopyLocalizedString
to search the default strings file (Localizable.strings
) for a localized format string that indicates how the error should be formatted for display. You need to pass the key (errorFormatStringKey
) for the localized string you wish to retrieve. The second parameter is optional—it is a comment (CFStringRef
) to help translators by giving them context or other hints about how the string is used or how to translate it. If you don’t provide a comment, you should passNULL
.If no error occurs, calls the Core Foundation String Services function
CFStringCreateWithFormat
to create a copy of the error string that includes the error number. The first parameter is a reference to an allocator to be used to create theCFString
object. You can passNULL
to request the default allocator. The second parameter refers to an undocumented feature, so passNULL
. The third parameter is a reference to aCFString
object that contains a string with printf-style specifiers. The remaining parameters are arguments for the printf-style string. In this case there is only one, a status code.Calls the Core Foundation String Services function
CFStringGetPascalString
to get a copy of the string in a format that can display in a standard alert dialog.Calls the Dialog Manager function
StandardAlert
to display the error message.Calls the Core Foundation Base Services function
CFRelease
to release the string (printErrorMsg
) created by the call toCFCopyLocalizedString
.Calls the function
CFRelease
to release the string (formatStr
) created by the call toCFStringCreateWithFormat
.
Saving a Document as a PDF File
This section provides information on what you need to do in your code to implement a command to save a document as a PDF file. In Mac OS X saving a document as a PDF file is simple. Your application needs to set the printing destination to a file location instead of to a printer. You set the destination by calling the function PMSessionSetDestination
.
Listing 3-11 shows a code fragment that sets the destination to a PDF file. You need to supply the Carbon Printing Manager constants kPMDestinationFile
to specify that the destination is a file and kPMDocumentFormatPDF
that the document format is PDF. The parameter saveURL
specifies the location to save the PDF file.
When you call the function PMSessionSetDestination
you must
call the function
PMSessionSetDestination
after a call toPMCreateSession
and before you release the printing session object.use the same printing session object for the function
PMSessionSetDestination
as you use when you set defaults for the print settings object (printSettings
).set the destination before you call your print loop code.
Listing 2-11 Setting the destination as a PDF file
if (status == noErr){ |
status = PMSessionSetDestination (printSession, |
printSettings, |
kPMDestinationFile, |
kPMDocumentFormatPDF, |
saveURL); |
} |
In addition to setting the destination as a PDF file, you need to decide how your application handles printing dialogs (Page Setup and Print) and whether you want the printing system to display the printing status dialog.
When users save a document, they expect to see a dialog in which they can specify a filename and file location. They do not expect to see Page Setup or Print dialogs. In most cases, your application should not display either of the printing dialogs to the user in response to the Save As PDF command. You need to write your printing code so that the print settings object is set up programmatically rather than by the user. Your application should use the current page format that is associated with the document.
The printing status dialog is automatically shown by the printing system when an application calls these functions: PMSessionBeginDocument
, PMSessionEndDocument
, PMSessionBeginPage
, and PMSessionEndPage
. The printing status message informs the user of the page being printed (Printing Page 1, Printing Page 2, and so forth). A printing status message is probably not appropriate to show for file saving so you should suppress the printing status dialog.
To suppress the printing status dialog, your application needs to use the “No Status Dialog” versions of these functions. Specifically, you call
PMSessionBeginDocumentNoDialog
instead ofPMSessionBeginDocument
PMSessionEndDocumentNoDialog
instead ofPMSessionEndDocument
Otherwise, the code you use to implement the print loop is the same as what you’d use to print a print job triggered by a user clicking Print in the Print dialog.
Listing 3-12 shows a code fragment that assigns function names to an application-defined structure based on whether the status dialog should be shown. Then, the application passes a pointer to the structure to its print loop function (MyDoPrintLoop
). You would need to modify your application’s print loop function to require a parameter that specifies a pointer to the application-defined structure containing the function names and call the appropriate function from the structure.
Listing 2-12 Setting up a print loop to use the No Dialog functions
if (status == noErr) |
{ |
#if NO_STATUS_DIALOG |
PrintingProcs myPrintingProcs = {PMSessionBeginDocumentNoDialog, |
PMSessionEndDocumentNoDialog, |
PMSessionBeginPageNoDialog, |
PMSessionEndPageNoDialog}; |
#else |
PrintingProcs myPrintingProcs = {PMSessionBeginDocument, |
PMSessionEndDocument, |
PMSessionBeginPage, |
PMSessionEndPage}; |
#endif |
status = MyDoPrintLoop(printSession, |
pageFormat, |
printSettings, |
documentDataP, |
&myPrintingProcs); |
} |
Printing One Copy
The Print One Copy command is often provided by applications as a shortcut to allow a user to print a single copy of a document using default settings. Because default print setting values are used, there is no need for your application to display the Print dialog. Instead your application should set up the print settings objects programmatically and should use the current page format that is associated with the document. Otherwise, the code you use to implement the print loop is the same as what you’d use to print a print job triggered by a user clicking Print in the Print dialog.
Printing Multiple Copies
The Mac OS X printing system automatically handles printing multiple copies. Your application does not need to perform any tasks other than specifying the number of copies in the printing session object.
Copyright © 2001, 2004 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2004-08-31