Dynamically Generating Previews
If a textual document can readily be converted from its native format into an appropriate Quick Look format (HTML, RTF, PDF, and plain text), your generator can perform that conversion for previews of that document. In addition, if you can generate HTML data for your preview, you can also include attachments for such items as images, QuickTime movies, and Flash animations.
An important difference between HTML previews and other kinds of textual previews is that in the former case, the Web Kit handles the layout of the preview for you. For previews in other textual formats, your generator must handle the layout of the text.
“Creating Textual Representations On the Fly” discusses how you might dynamically create a preview for a textual document (in this case, RTF). “Generating Enriched HTML” describes the HTML data-plus-attachments approach.
Creating Textual Representations “On the Fly”
The code example in Listing 6-1 illustrates how a generator might create and return an RTF version of a document as a preview. Although most of the generator code is related to methods of a private framework, there are two important things to point out:
The generator uses a private CSS parser object to assist in the layout of the preview.
The native format of the document is XML, which the generator then converts (using private methods) to RTF.
The important aspect of this code from a Quick Look perspective is the call to QLPreviewRequestSetDataRepresentation after the RTF data has been created. As parameters to this function, the generator provides the RTF data and a UTI constant that indicates the native Quick Look type of the provided data.
Listing 6-1 Generating a preview in RTF format
OSStatus GeneratePreviewForURL(void *thisInterface, QLPreviewRequestRef preview, CFURLRef url, CFStringRef contentTypeUTI, CFDictionaryRef options) |
{ |
NSMutableDictionary *props, *imgProps; |
NSManagedObject *occasion = nil; |
NSMutableString *html; |
NSString *momPath; |
NSData *image; |
@autoreleasepool { |
// Initializes the Core Data stack to read from the file and returns a managed object |
occasion=InitializeCoreDataStackWithURL(url); |
// Before proceeding make sure the user didn't cancel the request |
if (QLPreviewRequestIsCancelled(preview)) { |
return noErr; |
} |
if (occasion != NULL) { |
props = [[NSMutableDictionary alloc] init]; |
[props setObject:@"UTF-8" forKey:(NSString *)CFBridgingRelease(kQLPreviewPropertyTextEncodingNameKey)]; |
[props setObject:@"text/html" forKey:(__bridge NSString *)kQLPreviewPropertyMIMETypeKey]; |
html = [[NSMutableString alloc] init]; |
[html appendString:@"<html><body bgcolor=white>"]; |
[html appendString:@"<img src=\"cid:tabs.png\"><br>"]; |
[html appendString:@"<h1>Occasion:"]; |
[html appendString:[occasion valueForKey:@"name"]]; |
[html appendString:@"</h1><br><br><h2>Description:</h2><br>"]; |
[html appendString:[occasion valueForKey:@"detailDescription"]]; |
[html appendString:@"<br><h2>Start Date:</h2><br>"]; |
[html appendString:[[occasion valueForKey:@"startDate"] description]]; |
[html appendString:@"<br><h2>End Date:</h2><br>"]; |
[html appendString:[[occasion valueForKey:@"endDate"] description]]; |
[html appendString:@"</body></html>"]; |
image = [NSData dataWithContentsOfFile:[NSString stringWithFormat:@"%@%@", |
[[NSBundle bundleWithIdentifier:@"com.apple.eventsmanager.qlgenerator"] bundlePath], |
@"/Contents/Resources/tabs.png"]]; |
imgProps = [[NSMutableDictionary alloc] init]; |
[imgProps setObject:@"image/png" forKey:(__bridge NSString *)kQLPreviewPropertyMIMETypeKey]; |
[imgProps setObject:image forKey:(__bridge NSString *)kQLPreviewPropertyAttachmentDataKey]; |
[props setObject:[NSDictionary dictionaryWithObject:imgProps forKey:@"tabs.png"] |
forKey:(__bridge NSString *)kQLPreviewPropertyAttachmentsKey]; |
QLPreviewRequestSetDataRepresentation( |
preview, |
(__bridge CFDataRef)[html dataUsingEncoding:NSUTF8StringEncoding], |
kUTTypeHTML, |
(__bridge CFDictionaryRef)props |
); |
} |
else { |
NSLog(@"Couldn't get managed object!"); |
} |
} |
return noErr; |
} |
Generating Enriched HTML
A generally useful but slightly more complex approach to generating a preview dynamically is to create HTML. This approach is ideally suited for applications that aren’t primarily textual or graphical in nature, such as applications whose document user interface is a combination of text and graphics, or applications that display their document data in a user interface consisting of table views, text and form fields, labeled checkboxes, and so on.
An example of the latter sort of application is the Core Data example application, Event Manager. The Event Manager application allows users to enter information on social and work events, including the occasion, the description of the event, and the start and end dates. It uses Core Data to store and manage the entered information. The implementation of GeneratePreviewForURL shown in Listing 6-2gets the managed object representing the document file, creates a static HTML file using an NSMutableString object, and inserts in the appropriate places document data fetched from the managed object. It also creates the properties dictionary to be passed back to Quick Look in the call to QLPreviewRequestSetDataRepresentation; the properties in this dictionary define the HTML data and the attachments associated with that data.
Listing 6-2 Generating a preview composed of HTML data plus an image attachment
OSStatus GeneratePreviewForURL(void *thisInterface, QLPreviewRequestRef preview, CFURLRef url, CFStringRef contentTypeUTI, CFDictionaryRef options) |
{ |
NSMutableDictionary *props, *imgProps; |
NSManagedObject *occasion = NULL; |
NSMutableString *html; |
NSString *momPath; |
NSData *image; |
@autoreleasepool { |
// Initializes the Core Data stack to read from the file and returns a managed object |
occasion = InitializeCoreDataStackWithURL(url); |
// Before proceeding make sure the user didn't cancel the request |
if (QLPreviewRequestIsCancelled(preview)) { |
return noErr; |
} |
if (occasion != NULL) { |
props = [[NSMutableDictionary alloc] init]; |
[props setObject:@"UTF-8" forKey:(__bridge NSString *)kQLPreviewPropertyTextEncodingNameKey]; |
[props setObject:@"text/html" forKey:(__bridge NSString *)kQLPreviewPropertyMIMETypeKey]; |
html = [[NSMutableString alloc] init]; |
[html appendString:@"<html><body bgcolor=white>"]; |
[html appendString:@"<img src=\"cid:tabs.png\"><br>"]; |
[html appendString:@"<h1>Occasion:"]; |
[html appendString:[occasion valueForKey:@"name"]]; |
[html appendString:@"</h1><br><br><h2>Description:</h2><br>"]; |
[html appendString:[occasion valueForKey:@"detailDescription"]]; |
[html appendString:@"<br><h2>Start Date:</h2><br>"]; |
[html appendString:[[occasion valueForKey:@"startDate"] description]]; |
[html appendString:@"<br><h2>End Date:</h2><br>"]; |
[html appendString:[[occasion valueForKey:@"endDate"] description]]; |
[html appendString:@"</body></html>"]; |
image = [NSData dataWithContentsOfFile:[NSString stringWithFormat:@"%@%@", |
[[NSBundle bundleWithIdentifier:@"com.apple.eventsmanager.qlgenerator"] bundlePath], |
@"/Contents/Resources/tabs.png"]]; |
imgProps = [[NSMutableDictionary alloc] init]; |
[imgProps setObject:@"image/png" forKey:(__bridge NSString *)kQLPreviewPropertyMIMETypeKey]; |
[imgProps setObject:image forKey:(__bridge NSString *)kQLPreviewPropertyAttachmentDataKey]; |
[props setObject:[NSDictionary dictionaryWithObject:imgProps forKey:@"tabs.png"] |
forKey:(__bridge NSString *)kQLPreviewPropertyAttachmentsKey]; |
QLPreviewRequestSetDataRepresentation( |
preview, |
(CFDataRef)CFBridgingRetain([html dataUsingEncoding:NSUTF8StringEncoding]), |
kUTTypeHTML, |
(CFDictionaryRef)CFBridgingRetain(props) |
); |
} |
else { |
NSLog(@"Couldn't get managed object!"); |
} |
} |
return noErr; |
} |
There are a few things worthy of special notice in Listing 6-2:
The HTML references the image attachment using the URL scheme
cid:identifier. The identifier is always used as the key for a dictionary containing attachment data (imgProps) that is added to the properties dictionary.The properties dictionary (
props) contains the HTML encoding and HTML MIME type (kQLPreviewPropertyTextEncodingNameKeyandkQLPreviewPropertyMIMETypeKey) and any attachment subdictionaries.In this case there is one attachment subdictionary; it contains the MIME type of the image attachment and the image data (accessed with
kQLPreviewPropertyMIMETypeKeyandkQLPreviewPropertyAttachmentDataKey, respectively)
When the generator calls QLPreviewRequestSetDataRepresentation it passes in the HTML data (in the specified encoding), the properties dictionary, and the UTI constant identifying HTML content. With the HTML and the properties dictionary set up in this way, the Web Kit can load the HTML and, when it parses it, load the attachments into the web view.
© 2009 Apple Inc. All Rights Reserved. (Last updated: 2009-07-20)