Creating PDF Documents


Creating a PDF with Quartz 2D is a straightforward task. The first step is to create a graphics context using the methods of the CGPDFContext opaque data type. Any graphics drawn into this context will be recorded into the PDF file.

The CGPDFContext is different from other graphics contexts. Just as Quartz 2D draws pages of a PDF, not documents, it also draws in pages of a PDF Context and not the context alone. The routine CGPDFContextBeginPage creates a page in the PDF context. After drawing onto that page, a call to CGPDFContextEndPage tells the context that the page is complete.

Another behavior peculiar to the CGPDFContext is the fact that releasing the context is a vital part of the drawing process. Quartz 2D will not finish writing the PDF data collected by a context to its destination until it ensures that all the pages have been added. Releasing the context is the signal that tells Quartz 2D no more graphics are forthcoming. The library will finish generating and writing the PDF data.

Creating PDF Contexts

When reading PDF documents and images, Quartz 2D relies on a CGDataProvider to supply PDF data. When writing PDF data, the library uses an analogous object known as a CGDataConsumer. A data consumer is an abstract representation of some mechanism that accepts a stream of data and (presumably) stores it. The routine CGDataConsumerCreate makes the most generic data, one that invokes callback routines whenever the system generates PDF data. The callback routines can store the PDF data in any way they like.

Most applications will want to store PDF data into a file or into a block of memory. Quartz 2D provides two utility routines that create data consumers for each case. CGDataConsumerCreateWithURL allows you to create a data consumer whose destination is any particular URL. If you use a file URL, then Quartz 2D will write the PDF data into a file. You can use network URLs, but as was mentioned before, you will have no way to trap networking errors when Quartz 2D is handling your networking code. If you want to create PDF data in memory, the data consumer API offers the CGDataConsumerCreateWithCFData routine. This data consumer will write the PDF data into an instance of the CFMutableData opaque data type.

One you have a data consumer to catch the PDF data, the routine CGPDFContextCreate returns a new graphics context. In addition to the data consumer, this routine accepts a rectangle and a dictionary as arguments. The rectangle is the default mediaBox for the PDF document. If the mediaBox parameter is NULL, Quartz will use a US Letter page by default. The default page size is 612 x 792 points, or 8.5 x 11 inches.

The dictionary that you pass to CGPDFContextCreate allows you to specify metadata about the PDF file. This metadata is stored inside the PDF so that it is available to other PDF processing systems. Adding dictionary entries with the keys kCGPDFContextTitle, kCGPDFContextAuthor, and kCGPDFContextCreator will add information about the origin of the document. Keys like kCGPDFContextOw nerPassword, kCGPDFContextUserPassword, kCGPDFContextAllowsPrinting, and kCGPDFContextAllowsCopying let you inject access control information into the PDF. There are additional keys that control professional printing and PDF/X options that pre-press applications use to manage PDF documents. All of these keys are documented in the CGPDFContext.h header file and in the developer documentation.

Adding Pages

Quartz 2D has two routines for adding pages to a PDF context, CGContextBeginPage, and CGPDFContextBeginPage. CGContextBeginPage is the older of the two routines and has fewer options than CGPDFContextBeginPage.

As indicated by its name, CGContextBeginPage is a part of the abstract CGCon-text API. This routine takes a rectangle as a parameter. The rectangle represents the mediaBox of the new page. This rectangle supersedes the mediaBox that the context was created with. If the parameter is NULL, page's mediaBox will match the mediaBox of the context.

The CGPDFContextBeginPage offers more control over the attributes of the page it creates. This routine accepts a dictionary of information about the page. One obvious key this dictionary might contain is kCGPDFContextMediaBox. Naturally, this key specifies the mediaBox for the page and overrides the mediaBox supplied by the context. The page information dictionary can also include keys that specify the other PDF page rectangles like the cropBox, or the artBox.

As you finish drawing each page, you should notify Quartz using CGContextEndPage or CGPDFContextEndPage. Be sure to pair each routine with the corresponding function that you used to begin the page. These routines are essential because they give Quartz the chance to update its internal data structures.

Drawing the PDF

Once the context contains a page, the same routines that draw in any other context will generate PDF data. Quartz records and encodes each drawing command into the PDF data that is sent to the data consumer.

There are some simple drawing techniques for PDF contexts that can help Quartz optimize the contents of a PDF file. For example, if the PDF will contain multiple copies of the same image, the resulting file will be smaller if each call to CGImageDraw uses the same CGImageRef. This gives the system the opportunity to take note of the fact that the same image is drawn multiple times. Quartz can optimize the output stream so that only one copy of the image's data will be written out.

In a similar fashion, repeatedly drawing a CGLayerRef into the context allows the system to store a single copy of the graphic in the file and reuse it every time it appears.

Release the Context

After all the pages have been drawn, a call to CGContextRelease prompts Quartz 2D to collect all the information about the pages and write the document's data. As mentioned in the overview at the beginning of this chapter, releasing the context is a vital part of successfully creating a PDF. If the context is not released and the system writes anything to the destination, it might not contain a complete PDF.

An Example That Creates a PDF

Listing 14.2 is a short code sample that creates a PDF file using Quartz 2D. Many of the illustrations in this book were created by sending Quartz 2D graphics to PDF files using similar code.

Listing 14.2. Creating a Simple PDF

void CreatePDFFile() {     // Ask the user where we should save the file.     CFURLRef saveLocation = CopySaveLocation();     if(NULL != saveLocation) {             CGRect mediaBox = CGRectMake(0, 0, 576.0, 576.0);             // Create a dictionary to store our document attributes             CFMutableDictionaryRef attributes =                     CFDictionaryCreateMutable(                            NULL, 3,                            &kCFTypeDictionaryKeyCallBacks,                            &kCFTypeDictionaryValueCallBacks);             CFDictionaryAddValue(attributes, kCGPDFContextAuthor,                     CFSTR("Scott Thompson"));             CFDictionaryAddValue(attributes, kCGPDFContextTitle,                     CFSTR("Sample PDF"));             CFDictionaryAddValue(attributes, kCGPDFContextCreator,                     CFSTR("CreatePDF Sample Code"));             // Create a PDF Context that we will draw the graphics into             CGContextRef pdfContext =                     CGPDFContextCreateWithURL(saveLocation,                            &mediaBox, attributes);             CFRelease(attributes);             // Begin a PDF page             CGPDFContextBeginPage(pdfContext, NULL);             DrawImageToExport(pdfContext, mediaBox.size);             // End the PDF page             CGPDFContextEndPage(pdfContext);             // Finalize the PDF document             CGContextRelease(pdfContext);     } } 

The code sample begins by calling the CopySaveLocation routine. This routine puts up a standard system dialog asking the user to choose a destination for the PDF file. The value returned is a file URL. You create a CFDictionary and fill out three of the attribute keys. These keys will become part of the metadata contained in the PDF file.

Using the URL that was selected, you create a PDF document at that location using CGPDFContextCreateWithURL. Naturally, you pass in the metadata dictionary. You also supply a mediaBox rectangle for the PDF file. This box will also serve as the default page size. You then release the attributes dictionary at this point because it is not used again.

To draw in the PDF file, you have to add a page and the sample calls CGPDFContextBeginPage to do just that. Next you pass in NULL for the page info dictionary, which tells Quartz 2D that this page needs to inherit its mediaBox from the document.

At this point any drawing commands sent to the PDF context will be recorded on the first page. The routine DrawImageToExport is used to create a simple graphic on the PDF page. After drawing, the code simply ends the page using CGPDFContextEndPage.

Before leaving the routine, the code is very careful to release the PDF context so that Quartz 2D can finalize the PDF data and ensure that it is all written into the file.




Quartz 2D Graphics for Mac OS X Developers
Quartz 2D Graphics for Mac OS X Developers
ISBN: 0321336631
EAN: 2147483647
Year: 2006
Pages: 100

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net