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)
 
{
    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:

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.


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