November 2, 2013

Vector Graphics in iOS via PDF

I make iOS card games in my spare time. I love playing cards on my iPhone, and I love making the AI for the computer player (yes, I am that type of person).

When I started my first game, I couldn't find any decent library to render playing cards on iOS. There were a bunch of repositories of playing card images (most of them hideous), but none worked for me. I needed multiple resolutions: iPhone, iPad, retina, non-retina, etc. I really wanted to be able to specify and change card sizes in my code.

I eventually discovered an awesome vector playing card set on Google Code, but quickly ran into a different roadblock: iOS does not support rendering vector graphics natively.

There are a few open source projects like SVGKit that offer full stack SVG parsing, but I was intimidated about importing such a large library into my weekend project just to render a playing card.

Knowing iOS and Mac OS support PDF natively, I decided to take a different approach: convert my vector graphics to a PDF document. PDF supports vector drawing, and I could use the Core Graphics PDF functions to convert them to UIImages, i.e.,

NSURL *url = [NSURL fileURLWithPath:[NSBundle.mainBundle pathForResource:@"Card" ofType:@"pdf"]];
CGSize size = CGSizeMake(100, 200); // Output image size
CGPDFDocumentRef document = CGPDFDocumentCreateWithURL((CFURLRef)url);
UIGraphicsBeginImageContextWithOptions(size, false, 0);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextTranslateCTM(context, 0.0, size.height);
CGContextScaleCTM(context, 1.0, -1.0);
CGPDFPageRef page = CGPDFDocumentGetPage(document, 1);
CGContextConcatCTM(context, CGPDFPageGetDrawingTransform(page, kCGPDFBleedBox, CGRectMake(0, 0, size.width, size.height), 0, true));
CGContextDrawPDFPage(context, page);
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();

In practice, I converted the repository of playing card graphics into a 52 page PDF document (one per card), and convert them to images in a single batch. It isn't particularly fast, but it was still faster than the competing SVG libraries. As long as you cache the UIImage you generate, it is more than fast enough, and much easier (and much smaller) than generating 4 or 8 images per graphic given the proliferation of iOS device screen resolutions.

I hadn't seen any other people using this technique (PDF as a vector graphics file format for iOS), so I figured I would blog about it in case it is useful to other people.

I also open sourced the card rendering library in case it is useful to anyone else working on card games on iOS.

Fork/download ios-cards on GitHub.