To bring PDF data back into your application you retrieve the PDF data from the pasteboard, create a Quartz data provider that copies the PDF data into a Quartz buffer, and use the provider to create a CGPDFDocumentRef object. You can call CGContextDrawPDFDocument to draw the PDF version of your picture in any graphics context. The PasteboardContainsPDF routine in Listing 4-7 is taken from the CarbonSketch sample application.
The routine checks whether the pasteboard provided to it contains PDF data. If it does, the PDF data is returned as CFData in the pdfData parameter. A detailed explanation for each numbered line of code appears following the listing.
Listing 4-7 A routine that gets PDF data from the pasteboard (Clipboard)
static Boolean PasteboardContainsPDF (PasteboardRef inPasteboard, |
CFDataRef* pdfData) |
{ |
Boolean gotPDF = false; |
OSStatus err = noErr; |
ItemCount itemCount; |
UInt32 itemIndex; |
err = PasteboardGetItemCount (inPasteboard, &itemCount);// 1 |
require_noerr(err, PasteboardGetItemCount_FAILED); |
for (itemIndex = 1; itemIndex <= itemCount; ++itemIndex)// 2 |
{ |
PasteboardItemID itemID; |
CFArrayRef flavorTypeArray; |
CFIndex flavorCount; |
CFIndex flavorIndex; |
err = PasteboardGetItemIdentifier (inPasteboard, // 3 |
itemIndex, &itemID ); |
require_noerr( err, PasteboardGetItemIdentifier_FAILED ); |
err = PasteboardCopyItemFlavors (inPasteboard, itemID,// 4 |
&flavorTypeArray ); |
require_noerr( err, PasteboardCopyItemFlavors_FAILED ); |
flavorCount = CFArrayGetCount( flavorTypeArray );// 5 |
for (flavorIndex = 0; flavorIndex < flavorCount; ++flavorIndex) |
{ |
CFStringRef flavorType; |
CFComparisonResult comparisonResult; |
flavorType = (CFStringRef)CFArrayGetValueAtIndex (// 6 |
flavorTypeArray, flavorIndex ); |
comparisonResult = CFStringCompare(flavorType,// 7 |
kUTTypePDF, 0); |
if (comparisonResult == kCFCompareEqualTo) |
{ |
if (pdfData != NULL) |
{ |
err = PasteboardCopyItemFlavorData( inPasteboard,// 8 |
itemID, flavorType, pdfData ); |
require_noerr (err, |
PasteboardCopyItemFlavorData_FAILED ); |
} |
gotPDF = true;// 9 |
break; |
} |
PasteboardCopyItemFlavorData_FAILED: |
PasteboardGetItemFlavorFlags_FAILED: |
} |
CFRelease(flavorTypeArray);// 10 |
PasteboardCopyItemFlavors_FAILED: |
PasteboardGetItemIdentifier_FAILED: |
; |
} |
PasteboardGetItemCount_FAILED: |
return gotPDF;// 11 |
} |
Here’s what that code does:
Gets the number of items on the pasteboard.
Iterates through each item on the pasteboard.
Gets the unique identifier for this pasteboard item.
Copies the flavor types for that item ID into an array. Note that the flavor type array is a CFArrayType that you need to release later.
Gets a count of the flavor types in the array. You need to iterate through these to find the PDF flavor.
Gets the flavor type stored in a specific location in the array.
Checks for the PDF flavor type. Note that in Mac OS X v10.4 you should use the universal type kUTTypePDF, as shown here, instead of CFSTR('com.adobe.pdf').
Copies the PDF data, if any is found.
Sets the gotPDF flag to true.
Releases the array.
Returns true if successful.
After you get the PDF data from the pasteboard, you can draw it in your application, using a routine similar to the DrawPDFData routine shown in Listing 4-8. The routine takes a CFDataRef data type (which is what you get from the routine in Listing 4-7 when you copy data from the pasteboard), a graphics context, and a destination rectangle. A detailed explanation for each numbered line of code appears following the listing.
Listing 4-8 A routine that draws PDF data
static void MyPDFDataRelease (void *info, const void *data, size_t size) |
{ |
if(info != NULL) |
CFRelease((CFDataRef)info); |
} |
static void DrawPDFData (CGContextRef ctx, CFDataRef pdfData, |
CGRect dstRect) |
{ |
CGDataProviderRef provider; |
CGPDFDocumentRef document; |
CGPDFPageRef page; |
CGRect pageSize; |
CFRetain (pdfData); |
provider = CGDataProviderCreateWithData (pdfData, // 1 |
CFDataGetBytePtr(pdfData), |
CFDataGetLength(pdfData), MyPDFDataRelease); |
document = CGPDFDocumentCreateWithProvider (provider);// 2 |
CFRelease(provider);// 3 |
page = CGPDFDocumentGetPage (document, 1);// 4 |
pageSize = CGPDFPageGetBoxRect (page, kCGPDFMediaBox);// 5 |
CGContextSaveGState(ctx);// 6 |
MySetupTransform(ctx, pageSize, dstRect); // 7 |
// Scale pdf page into dstRect, if the pdf is too big |
CGContextDrawPDFPage (ctx, page);// 8 |
CGContextRestoreGState(ctx);// 9 |
CFRelease(document);// 10 |
} |
Here’s what the code does:
Creates a data provider to read PDF data provided to your application from a CGDataRef data source. Note that you need to supply a release function for Quartz to call when it frees the data provider.
Creates a CGPDFDocument object using data supplied by the data provider you just created.
Releases the data provider. You should release a data provider immediately after using it to create the CGPDFDocument object.
Gets the first page of the newly created document.
Gets the media box rectangle for the PDF. You need this to determine how to scale the content later.
Saves the graphics state so that you can later restore it.
Calls an application-defined routine to set a transform, if necessary. This routine (which you would need to write) determines whether the PDF is too big to fit in the destination rectangle, and transforms the context appropriately.
Draws the PDF document into the graphics context that is passed to the DrawPDFData routine.
Restores the graphics state.
Releases the PDF document object.
Last updated: 2006-09-05