How To Create a PDF with Quartz 2D in iOS 5 – Part 1
This is a blog post by iOS Tutorial Team member Tope Abayomi, an iOS developer and Founder of App Design Vault, your source for iPhone App Design. Sometimes in your apps you might want to generate a PDF with data from the app for your users. For example, imagine you had an app that allowed […] By Tope Abayomi.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
How To Create a PDF with Quartz 2D in iOS 5 – Part 1
15 mins
Add a UIWebView to Show the PDF File
The only thing left to do is to show our PDF file on the screen. To do that, add the following method to PDFViewController.m, which adds a UIWebView to the View Controller and shows the PDF file path we just created.
-(void)showPDFFile
{
NSString* fileName = @"Invoice.PDF";
NSArray *arrayPaths =
NSSearchPathForDirectoriesInDomains(
NSDocumentDirectory,
NSUserDomainMask,
YES);
NSString *path = [arrayPaths objectAtIndex:0];
NSString* pdfFileName = [path stringByAppendingPathComponent:fileName];
UIWebView* webView = [[UIWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 480)];
NSURL *url = [NSURL fileURLWithPath:pdfFileName];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[webView setScalesPageToFit:YES];
[webView loadRequest:request];
[self.view addSubview:webView];
}
Then add the implementation of viewDidLoad to call these new methods:
- (void)viewDidLoad
{
[self drawText];
[self showPDFFile];
[super viewDidLoad];
}
Now we’re ready to see some results! Build and run the project, and you should see “Hello World” on the screen when you zoom in!
A Quick Refactoring Process
Our drawing code does not really belong in the View Controller, so let’s farm that off into a new NSObject called PDFRenderer. Create a new file with the iOS\Cocoa Touch\Objective-C class template, enter PDFRenderer for the Class and NSObject for the subclass, and finish creating the file.
Open up PDFRenderer.h and import Core Text at the top of the file:
#import CoreText/CoreText.h
Then move the drawText method from PDFViewController.m to PDFRenderer.m.
We will pass the filename into the new drawText method, so let’s create a new method in the PDFViewController.m file called getPDFFileName.
-(NSString*)getPDFFileName
{
NSString* fileName = @"Invoice.PDF";
NSArray *arrayPaths =
NSSearchPathForDirectoriesInDomains(
NSDocumentDirectory,
NSUserDomainMask,
YES);
NSString *path = [arrayPaths objectAtIndex:0];
NSString* pdfFileName = [path stringByAppendingPathComponent:fileName];
return pdfFileName;
}
Next, open PDFRenderer.m and remove this same block of code from the drawText method, and modify the method signature to take the filename as a parameter and make it a static method:
+(void)drawText:(NSString*)pdfFileName
Also predeclare this method in PDFRenderer.h.
Next, import PDFRenderer at the top of PDFViewController.m:
#import "PDFRenderer.h"
And modify viewDidLoad to call this new class and method:
- (void)viewDidLoad
{
NSString* fileName = [self getPDFFileName];
[PDFRenderer drawText:fileName];
[self showPDFFile];
[super viewDidLoad];
}
Build and run the project. Our quick refactoring shouldn’t have caused the application to behave any differently, but the code is better organized.
Drawing a Line Using Quartz 2D
The invoice we want to end up with is made up of text, lines and images. We’ve got text — now it’s time to practice drawing a line. To do that, we will use… wait for it… the drawLine method!
Add this new method to PDFRenderer.m:
+(void)drawLineFromPoint:(CGPoint)from toPoint:(CGPoint)to
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, 2.0);
CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();
CGFloat components[] = {0.2, 0.2, 0.2, 0.3};
CGColorRef color = CGColorCreate(colorspace, components);
CGContextSetStrokeColorWithColor(context, color);
CGContextMoveToPoint(context, from.x, from.y);
CGContextAddLineToPoint(context, to.x, to.y);
CGContextStrokePath(context);
CGColorSpaceRelease(colorspace);
CGColorRelease(color);
}
The above code sets the properties of the line we want to draw. The properties are the thickness of the line (2.0) and the color (transparent gray). It then draws the line between the CGPoints passed into the method.
We could now call this method from our View Controller. Notice, however, that the drawText method does not create a new PDF Graphics context or a new page by calling UIGraphicsBeginPDFContextToFile. So we need to make some modifications.
First, create a new method in the PDFRenderer file called drawPDF.
+(void)drawPDF:(NSString*)fileName
{
// Create the PDF context using the default page size of 612 x 792.
UIGraphicsBeginPDFContextToFile(fileName, CGRectZero, nil);
// Mark the beginning of a new page.
UIGraphicsBeginPDFPageWithInfo(CGRectMake(0, 0, 612, 792), nil);
CGPoint from = CGPointMake(0, 0);
CGPoint to = CGPointMake(200, 300);
[PDFRenderer drawLineFromPoint:from toPoint:to];
[self drawText];
// Close the PDF context and write the contents out.
UIGraphicsEndPDFContext();
}
This will create our graphics context, draw the text and a test line, and then end the context.
Note that the drawText method no longer takes the PDF filename as a parameter. Here is our new drawText method.
+(void)drawText
{
NSString* textToDraw = @"Hello World";
CFStringRef stringRef = (__bridge CFStringRef)textToDraw;
// Prepare the text using a Core Text Framesetter
CFAttributedStringRef currentText = CFAttributedStringCreate(NULL, stringRef, NULL);
CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString(currentText);
CGRect frameRect = CGRectMake(0, 0, 300, 50);
CGMutablePathRef framePath = CGPathCreateMutable();
CGPathAddRect(framePath, NULL, frameRect);
// Get the frame that will do the rendering.
CFRange currentRange = CFRangeMake(0, 0);
CTFrameRef frameRef = CTFramesetterCreateFrame(framesetter, currentRange, framePath, NULL);
CGPathRelease(framePath);
// Get the graphics context.
CGContextRef currentContext = UIGraphicsGetCurrentContext();
// Put the text matrix into a known state. This ensures
// that no old scaling factors are left in place.
CGContextSetTextMatrix(currentContext, CGAffineTransformIdentity);
// Core Text draws from the bottom-left corner up, so flip
// the current transform prior to drawing.
CGContextTranslateCTM(currentContext, 0, 100);
CGContextScaleCTM(currentContext, 1.0, -1.0);
// Draw the frame.
CTFrameDraw(frameRef, currentContext);
CFRelease(frameRef);
CFRelease(stringRef);
CFRelease(framesetter);
}
Add the predeclaration for drawPDF into PDFRenderer.h. Then modify the View Controller to call the correct method in viewDidLoad, which is drawPDF instead of drawText.
That’s it! Build and run the app, you should see both “Hello World” and a diagonal line, similar to the image below.
Yes, that’s funny-looking. And no, this is not “How to Draw Abstract Art with Quartz 2D.” Don’t worry, our PDF will achieve some polish in Part Two of the tutorial! :]
Where to Go From Here?
Here is a example project with all of the code from the above tutorial.
That brings us to the end of the first part of this tutorial. In Part Two, we will go into more advanced drawing techniques, like adding images and using a xib file to make the layout process easier.
If you have any questions or comments about what we’ve done so far, please join in the forum discussion below!
This is a blog post by iOS Tutorial Team member Tope Abayomi, an iOS developer with a passion for easy to use, useful, aesthetically pleasing apps. Here are some of his videos teaching you how to design an app with custom design.