BuySellAds.com

Read the chapters in my new book Barcodes with iOS 7 as I hand them in. Great new app opportunities await!
Our DNA is written in Objective-C
Jump

Rendering PDF is easier than you thought

We all know by now that Adobe is almost as evil as …, well let’s say they pioneered a couple of functionalities that where great for the longest time. One being the PDF format which is actually totally built into OSX everywhere. On OSX you’re able to print into a PDF without having to install extra software. Also iOS comes with PDF support and today we’ll look at how we can draw a PDF in a view.

How UIWebView sees it

The first idea you might have is to use UIWebView to display PDFs which is not difficult, just get the URL, make a NSURLRequest and pass this to a web view.

_webView = [[UIWebView alloc] initWithFrame:frame];
_webView.delegate = self;
_webView.autoresizingMask = UIViewAutoresizingFlexibleWidth |
	UIViewAutoresizingFlexibleHeight;
 
// we don't want interaction, full size
_webView.scalesPageToFit = YES;
_webView.userInteractionEnabled = NO;
_webView.backgroundColor = [UIColor whiteColor];
 
//fileURL is an NSURL to a PDF file
[_webView loadRequest:[NSURLRequest requestWithURL:fileURL]];

But there is one drawback: you cannot really control how it will arrive on screen. For one thing, UIWebView draws a fat gray border and shadow which might somewhat mess up your UI design.

Wouldn’t it be great if you could draw the PDF somehow into a view? Yes, we can!

Drawing PDF Pages

Apple provides in CoreGraphics a whole set of functions prefixed CGPDF to deal with PDFs. The easiest method for drawing the first page of a PDF file I pieced together from the documentation like this. This is a regular view where I replaced the drawRect as follows:

- (void)drawRect:(CGRect)rect
{
	CGContextRef ctx = UIGraphicsGetCurrentContext();
 
	// PDF might be transparent, assume white paper
	[[UIColor whiteColor] set];
	CGContextFillRect(ctx, rect);
 
	// Flip coordinates
	CGContextGetCTM(ctx);
	CGContextScaleCTM(ctx, 1, -1);
	CGContextTranslateCTM(ctx, 0, -rect.size.height);
 
	// url is a file URL
	CGPDFDocumentRef pdf = CGPDFDocumentCreateWithURL((CFURLRef)url);
	CGPDFPageRef page1 = CGPDFDocumentGetPage(pdf, 1);
 
	// get the rectangle of the cropped inside
	CGRect mediaRect = CGPDFPageGetBoxRect(page1, kCGPDFCropBox);
	CGContextScaleCTM(ctx, rect.size.width / mediaRect.size.width,
		rect.size.height / mediaRect.size.height);
	CGContextTranslateCTM(ctx, -mediaRect.origin.x, -mediaRect.origin.y);
 
	// draw it
	CGContextDrawPDFPage(ctx, page1);
	CGPDFDocumentRelease(pdf);
}

The part about the mediaRect is necessary because PDF pages are typically larger than what you really see on screen. There are usually some printing, color and crop marks outside of the content area. I managed to eliminate those by changing the transformation matrix of the CGContext.

The official method in the documentation is to use CGPDFPageGetDrawingTransform, but this has a catch: it won’t scale the image to be larger than 100% and instead center it on screen. So we build our own transform, ignoring the aspect ratio because we want to fill the view with that.

If the PDF is small enough to keep in memory you could also load it into an NSData object and access individual pages from it super fast. To read from data as opposed to providing an NSURL you change the code like this:

// data is an NSData object we filled with the PDF data from file before
CGDataProviderRef dataProvider = CGDataProviderCreateWithCFData((CFDataRef)data]);
CGPDFDocumentRef pdf = CGPDFDocumentCreateWithProvider(dataProvider);
CGDataProviderRelease(dataProvider);

We’re using “toll-free bridging” to simply use our NSData instance where the function is expecting a CFDataRef, and of course if we have a method called SomethingCreate then we also have to have a SomethingRelease.

Conclusion

Having this ability to render PDF pages int any resolution gives you a great deal of flexibility. A great tool to have in your toolchest! You might even go as far as using PDFs as your main graphics container because it can contain both vector and bitmap graphics. In fact it’s the closest you can get to vector graphics on iOS.

Now please don’t go and make yet another PDF reader with this. With iBooks 1.1 soon supporting native PDF viewing that would make no sense.

Here are a couple other ideas:

  • An app that lets you keep your musical note sheets on your iPad. You could mark sections and specify their order and then choose between classical mode (one sheet per screen) or 1-pass mode, where the sections are flattened such that you don’t have to go back to repeats, but always play from left to right. Couple that with some fancy notes OCR to have the iPad play a bar before you. Maybe use audio clues on when to know to turn the page.
  • An app that lets you EDIT PDFs on the iPad, with cut/copy/paste support so that you can paste things on the iPad into a PDF-based scrapbook.
  • Make a presentation app similar to Prezi. You would load a PDF as basis and then you would record zoom levels, viewed rectangle and rotations along a user-defined path. Give presentation to external display.
  • Use a similar technique to render rich-text documents into reports that you can e-mail from your app.
  • Make an electronic version of a magazine similar to Wired, no need for Adobe’s weird Illustrator-to-App converter that makes half a GB apps.

If you’re interesting in partnering or co-developing these ideas please e-mail me.


Categories: Recipes

%d bloggers like this: