Mac OS X Reference Library Apple Developer
Search

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 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)
{
    static CSSParser *theCSS = nil;
    NSError *theErr = nil;
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
 
    // cache the CSS parser
    if(theCSS == nil) {
        NSString *cssPath = [[NSBundle bundleWithIdentifier:@"com.apple.quicklooksweet"] pathForResource:@"editor" ofType:@"css"];
        if(cssPath == nil) {
            return noErr;
        }
        theCSS = [[CSSParser parserFromPath:cssPath] retain];
    }
    if(theCSS == nil) {
        return noErr;
    }
    NSLog(@"GeneratePreviewForURL start...");
    NSXMLDocument *theDoc = [[[NSXMLDocument alloc] initWithContentsOfURL:(NSURL *)url options:0 error:&theErr] autorelease];
    if (!theDoc && theErr) {
        NSLog(@"Error creating the XML, %@", theErr);
        [pool release];
        return noErr;
    }
    XMLAttributedStringCreation *theXMLStr = [XMLAttributedStringCreation XMLAttributedStringCreator];
    NSMutableAttributedString *theAttrStr = [theXMLStr attributedStringForNode:[theDoc rootElement] CSSParser:theCSS];
    if (!theAttrStr) {
        [pool release];
        return noErr;
    }
    NSData *theRTF = [theAttrStr RTFFromRange:NSMakeRange(0, [theAttrStr length]-1) documentAttributes:nil];
    QLPreviewRequestSetDataRepresentation(preview, (CFDataRef)theRTF, kUTTypeRTF, NULL);
 
    [pool release];
    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.

Important: For security reasons, you cannot use Web Kit plug-ins in HTML passed back to Quick Look (so you cannot, for example, use Java applets or Flash animations).

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)
{
    NSAutoreleasePool *pool;
    NSMutableDictionary *props,*imgProps;
    NSManagedObject *occasion=NULL;
    NSMutableString *html;
    NSString *momPath;
    NSData *image;
 
    pool = [[NSAutoreleasePool alloc] init];
    // 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] autorelease];
        [props setObject:@"UTF-8" forKey:(NSString *)kQLPreviewPropertyTextEncodingNameKey];
        [props setObject:@"text/html" forKey:(NSString *)kQLPreviewPropertyMIMETypeKey];
 
        html=[[[NSMutableString alloc] init] autorelease];
        [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] autorelease];
        [imgProps setObject:@"image/png" forKey:(NSString *)kQLPreviewPropertyMIMETypeKey];
        [imgProps setObject:image forKey:(NSString *)kQLPreviewPropertyAttachmentDataKey];
        [props setObject:[NSDictionary dictionaryWithObject:imgProps forKey:@"tabs.png"] forKey:(NSString *)kQLPreviewPropertyAttachmentsKey];
 
        QLPreviewRequestSetDataRepresentation(preview,(CFDataRef)[html dataUsingEncoding:NSUTF8StringEncoding],kUTTypeHTML,(CFDictionaryRef)props);
    }
    else {
        NSLog(@"Couldn't get managed object!");
    }
    [pool release];
    return noErr;
}

Note: In the interests of brevity, the InitializeCoreDataStackWithURL function in the above listing is a placeholder for the real code in the example project that initializes the Core Data stack from the passed-in file and returns a managed object.

There are a few things worthy of special notice in Listing 6-2:

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.




Last updated: 2009-07-20

Did this document help you? Yes It's good, but... Not helpful...