b2cloud

24th February 2013

Drawing over a PDF in iOS (PDF template)

Guides | Tutorial By 4 years ago

In a project I’m currently working on I needed to generate a PDF based on a bunch of information the user had entered. The PDF needed to be very fancy and have images and graphics all over the place to make it look really nice. Instead of drawing everything from scratch I used to some of the PDF libraries on iOS that allowed me to create a new PDF based on an existing one, then draw the dynamic content over the top. This even preserves the nice vectors and crisp text inside the original so it remains perfect when printing. This saved me a lot of time and kept the PDF exactly how the client wanted it.

If you’ve never drawn a PDF before on iOS, check out my first PDF drawing guide to get you started.

The PDF I will draw over is the same one from my first PDF guide. Download that here.

Start a new project and add that PDF to the project. Open the AppDelegate, this is where we will add the code. First we need to open the existing PDF and get the number of pages in it, do so with the following code:

CGPDFDocumentRef pdf = CGPDFDocumentCreateWithURL((CFURLRef)[[NSBundle mainBundle] URLForResource:@"tutorial" withExtension:@"pdf"]);

const size_t numberOfPages = CGPDFDocumentGetNumberOfPages(pdf);

//	Drawing goes in here...

CGPDFDocumentRelease(pdf);
pdf = nil;

Now after getting the number of pages, create a new PDF. This code will be familiar from the previous blog

NSMutableData* data = [NSMutableData data];
UIGraphicsBeginPDFContextToData(data, CGRectZero, nil);

//	Drawing goes in here...

UIGraphicsEndPDFContext();

You need to loop over each page, redrawing it into the PDF context and then drawing with our own code. In my example I’m just going to draw a red box. Of course this could be replaced with anything.

for(size_t page = 1; page <= numberOfPages; page++)
{
	//	Get the current page and page frame
	CGPDFPageRef pdfPage = CGPDFDocumentGetPage(pdf, page);
	const CGRect pageFrame = CGPDFPageGetBoxRect(pdfPage, kCGPDFMediaBox);
	
	UIGraphicsBeginPDFPageWithInfo(pageFrame, nil);
	
	//	Draw the page (flipped)
	CGContextRef ctx = UIGraphicsGetCurrentContext();
	CGContextSaveGState(ctx);
	CGContextScaleCTM(ctx, 1, -1);
	CGContextTranslateCTM(ctx, 0, -pageFrame.size.height);
	CGContextDrawPDFPage(ctx, pdfPage);
	CGContextRestoreGState(ctx);
	
	//	Draw a red box
	[[UIColor redColor] set];
	UIRectFill(CGRectMake(20, 20, 100, 100));
}

Because I'm mixing CoreGraphics calls in with the UI PDF generation layer I need to flip the drawing context before I draw the page into it.

That should be it. Save your NSMutableData somewhere and you've got a PDF. The final code should be as follows. The PDF generated looks like this.

CGPDFDocumentRef pdf = CGPDFDocumentCreateWithURL((CFURLRef)[[NSBundle mainBundle] URLForResource:@"tutorial" withExtension:@"pdf"]);

const size_t numberOfPages = CGPDFDocumentGetNumberOfPages(pdf);

NSMutableData* data = [NSMutableData data];
UIGraphicsBeginPDFContextToData(data, CGRectZero, nil);

for(size_t page = 1; page <= numberOfPages; page++)
{
	//	Get the current page and page frame
	CGPDFPageRef pdfPage = CGPDFDocumentGetPage(pdf, page);
	const CGRect pageFrame = CGPDFPageGetBoxRect(pdfPage, kCGPDFMediaBox);
	
	UIGraphicsBeginPDFPageWithInfo(pageFrame, nil);
	
	//	Draw the page (flipped)
	CGContextRef ctx = UIGraphicsGetCurrentContext();
	CGContextSaveGState(ctx);
	CGContextScaleCTM(ctx, 1, -1);
	CGContextTranslateCTM(ctx, 0, -pageFrame.size.height);
	CGContextDrawPDFPage(ctx, pdfPage);
	CGContextRestoreGState(ctx);
	
	//	Draw a red box
	[[UIColor redColor] set];
	UIRectFill(CGRectMake(20, 20, 100, 100));
}

UIGraphicsEndPDFContext();

CGPDFDocumentRelease(pdf);
pdf = nil;

//	Do something with the 'data'...
  • Julio Bernardo

    Hello!
    I put this code on my AppDelegate.m – didFinishLaunchingWithOptions method. Apparently, there is no error, but, PDF doesn’t show on Ipad Emulator Screen. I’ve added ‘NSLog(@”N de paginas: %zu”,numberOfPages);’, just to make sure if PDF has been loaded. Well, NSLOG show me 9, correct number of pages from PDF. I don’t know more what to do, I’m almost giving up, I’ve tried for the several days to show PDF through Core Graphics, for the many examples that I found on google, but I didn’t get success. Please, help me. If you can to make xcode project available, or all the classes with the code (no exceptions), I’ll be grateful forever. 🙂

    Thanks,
    Júlio.

    • Tom

      Hi there,

      This code wont show a PDF on screen, it will generate NSData of the PDF.

      To view the PDF, write the “data” to a file then try viewing it on your computer.

      If you want to show the PDF on the iPad screen, try loading it in a UIWebView (Google this for code examples)

      • Julio Bernardo

        Hello Tom, thanks for response.

        I’ll started recently with objective-c and I confess that I’m a little confuse.

        I’ll will explain what I want to do.

        I’d like to load a PDF existing, that i’ve already have (instead of create it), on my Ipad Screen, but not only this, I’d like also to manipulate it. Example: make highlight text, make a rectangle with Core Graphics, zooming and many more. I know how to load my PDF on screen with UiWebView, but I cannot manipulate as I want.

        So, does it means that I can only show PDF on IPAD screen through UIWebView ? I’d like to have many powers (to manipulate PDF), so need I to use Core Graphics, but for show PDF on Ipad screen, need I UiWebView? How to use both together? Can you indicate some good tutorial?

        Many thanks!

        Júlio.

  • Tom

    If you need to do more advanced things with the PDF, CGContextDrawPDFPage can be used to draw it into a context which is shown on screen, then you can customize the controls as you wish. Then any further drawing can be done on top, like in my example above.

    • Julio Bernardo

      Hey Tom!

      Well, can you give me a very simple code example? I’m really confused, CGContextDrawPDFPage shows on IPad screen as well? Or need I mandatory to use UiWebView to that? THanks again.

      • Tom

        Try this, it mentions both the UIWebView and drawing methods: http://www.cocoanetics.com/2010/06/rendering-pdf-is-easier-than-you-thought/

        • john

          Hello Tom,
          Excellent article on pdf and core graphics.

          I am trying to draw a circle, rect on the pdf . i am able to achieve that using

          CGContextMoveToPoint(context, 5,5);
          CGContextSetStrokeColorWithColor(context, [[UIColor redColor] CGColor]);
          CGContextAddRect(context, CGRectMake(50, 50, 20, 50));
          CGContextStrokePath(context);
          CGContextSaveGState(context);

          where context is where I have rendered my pdf …. current GraphicsContext.

          I am actually using Apple zoomable pdf viewer using scrollView : https://developer.apple.com/library/ios/samplecode/zoomingpdfviewer/Listings/ReadMe_txt.html

          where a TitleLayer is used to preserve zoomed pdf to avoid loosing zoomed pdf quality.

          Now when I draw into the context it is shown correctly, but when zoom happens the TileLayer erases it (the extra circle/ rect drawn above pdf).

          –> The other way is to draw PDF back back from the NSMutableData (if it is possible) by your code. and setPDF page of TileLayer each time.

          Again if I have 50 such circles, it will be huge performance issue.

          Thankx in advance for showing some way around to achieve this!!

      • Tom

        Try this, it mentions both the UIWebView and drawing methods: http://www.cocoanetics.com/2010/06/rendering-pdf-is-easier-than-you-thought/

  • john

    Hello Tom,
    Excellent article on pdf and core graphics.

    I am trying to draw a circle, rect on the pdf . i am able to achieve that using

    CGContextMoveToPoint(context, 5,5);
    CGContextSetStrokeColorWithColor(context, [[UIColor redColor] CGColor]);
    CGContextAddRect(context, CGRectMake(50, 50, 20, 50));
    CGContextStrokePath(context);
    CGContextSaveGState(context);

    where context is where I have rendered my pdf …. current GraphicsContext.

    I am actually using Apple zoomable pdf viewer using scrollView : https://developer.apple.com/library/ios/samplecode/zoomingpdfviewer/Listings/ReadMe_txt.html

    where a TitleLayer is used to preserve zoomed pdf to avoid loosing zoomed pdf quality.

    Now when I draw into the context it is shown correctly, but when zoom happens the TileLayer erases it (the extra circle/ rect drawn above pdf).

    –> The other way is to draw PDF back back from the NSMutableData (if it is possible) by your code. and setPDF page of TileLayer each time.

    Again if I have 50 such circles, it will be huge performance issue.

    Thankx in advance for showing some way around to achieve this!!

  • Binyamin Muhammad

    Hello Tom,

    I am trying your code in my project. its added the red box. Requirement of my project is we have add an image in PDF file where the position of the image in not hard coded. it will give by the user via gesture. My problem is that your code working fine for hard coded values but when we added position in “UIRectFill(CGRectMake(20, 20, 100, 100));” instead of “(20 , 20)” hard coded values.
    it is not adding the image at exact position as we require. Can u help me?

    • Tom

      You may need to translate the position to the PDF’s coordinate system, which will most likely be different from the coordinate system on the screen.

  • bedros hanounik

    Hi Tom,

    Is it possible to draw freehand lines on top of pdf context and then save in a new pdf file?

    Thanks

    Bedros

    • Tom

      You can do this, it’s the same drawing code as if you were dynamically drawing an image.

      • bedros hanounik

        thanks, I’ll give it a try

  • Samy

    Hi Tom,

    Thanks for the tutorial 🙂 I am wondering whether we can edit the graphics that we drawn and saved already in PDF ?

    Regards,

    Samy

    • Tom

      Hi there

      You could possibly do it with some low level PDF code, however it would be difficult and I wouldn’t recommend it
      Instead I’d recommend leaving blank spots in the PDF where you want to put custom content, I have done this in the past and it works well

      • Samy

        Hi Tom,

        Thanks for your quick reply. If possible could you please give me some sample code ?

        Regards,
        Samy

        • Tom

          It will be similar to my drawing code with the red square, as for examples you’d need to look into different ways to draw images and such to the screen. For in depth help you would be best to post on stackoverflow.com 🙂

          • Samy

            I am able to draw images inside PDF. Now I need to update the red square to yellow square, which means i need to get red square graphics.

  • Arbab

    Hi Tom,

    Thanks for the tutorial (y). I need to know that can we get the x,y coordinates of every word which is exist in existing PDF file?

    • Tom

      It’s pretty tricky, I’d recommend finding an open source library to do this

    • aks

      Hey Arbab,Tom,
      i am in need of the same answer. let me know if you have found the solution/open source code for that question. Thanks in advance.

Recommended Posts

Scalable iOS graphics (PDFImage.framework)

Post by 4 years ago

Over the Christmas holidays I was building an app in my own time and came across a bit of a common problem I wanted to solve - multiple PNG exports all at different sizes for...

Got an idea?

We help entrepreneurs, organizations and established brands from around
the country bring ideas to life. We would love to hear from you!