Convert an image to PDF on the iPhone and iPad
April 22nd, 2011
A reader asked a me about image to PDF conversion a few days ago and I promised him an article, so here it is.
Image to PDF conversion is based on Quartz API. Using Quartz we can create PDF files and add content to them, the conversion actually means drawing the image on a PDF page.
Let's begin.
First we compute the size of the PDF page. Based on the image size and the resolution we want to draw the image, the page size is computed like this (for flexibility different horizontal and vertical resolutions are supported):
1 2 | double pageWidth = image.size.width * image.scale * 72 / horzRes; double pageHeight = image.size.height * image.scale * 72 / vertRes; |
Now we're ready to create the PDF file. It can be created directly on disk or in memory. For flexibility let's create it in memory.
1 2 3 4 5 6 | NSMutableData *pdfFile = [[NSMutableData alloc] init]; CGDataConsumerRef pdfConsumer = CGDataConsumerCreateWithCFData((CFMutableDataRef)pdfFile); // The page size matches the image, no white borders. CGRect mediaBox = CGRectMake(0, 0, pageWidth, pageHeight); CGContextRef pdfContext = CGPDFContextCreate(pdfConsumer, &mediaBox, NULL); |
The PDF context is created, we can now perform the conversion based on image's orientation (mostly required by the images taken with the camera):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | CGContextBeginPage(pdfContext, &mediaBox); switch (image.imageOrientation) { case UIImageOrientationDown: CGContextTranslateCTM(pdfContext, pageWidth, pageHeight); CGContextScaleCTM(pdfContext, -1, -1); break; case UIImageOrientationLeft: mediaBox.size.width = pageHeight; mediaBox.size.height = pageWidth; CGContextTranslateCTM(pdfContext, pageWidth, 0); CGContextRotateCTM(pdfContext, M_PI / 2); break; case UIImageOrientationRight: mediaBox.size.width = pageHeight; mediaBox.size.height = pageWidth; CGContextTranslateCTM(pdfContext, 0, pageHeight); CGContextRotateCTM(pdfContext, -M_PI / 2); break; case UIImageOrientationUp: default: break; } CGContextDrawImage(pdfContext, mediaBox, [image CGImage]); CGContextEndPage(pdfContext); |
The work is done, finalize the PDF file and release the used objects.
1 2 | CGContextRelease(pdfContext); CGDataConsumerRelease(pdfConsumer); |
The PDF file is now available in the pdfFile variable.
I placed all the code above in the convertImageToPDF:withHorizontalResolution:verticalResolution:
static method in the PDFImageConverter
utility class, it can be used like this:
1 2 3 4 | NSData *pdfData = [PDFImageConverter convertImageToPDF: image withHorizontalResolution: 300 verticalResolution: 300]; NSString *path = [NSHomeDirectory() stringByAppendingPathComponent: @"Documents/image.pdf"]; [pdfData writeToFile:path atomically:NO]; |
Sometimes it is necessary to convert an image to PDF and use a predefined page size, such as Letter or A4. Also a maximum bounding rectangle can be specified for the image, so it is placed at a specific position on the page and it does not get out of the page. In this situation the image size must be adjusted (increased resolution) to make sure the image fits the given rectangle.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | double imageWidth = image.size.width * image.scale * 72 / resolution; double imageHeight = image.size.height * image.scale * 72 / resolution; double sx = imageWidth / boundsRect.size.width; double sy = imageHeight / boundsRect.size.height; // At least one image edge is larger than maxBoundsRect, reduce its size. if ((sx > 1) || (sy > 1)) { double maxScale = sx > sy ? sx : sy; imageWidth = imageWidth / maxScale; imageHeight = imageHeight / maxScale; } // Put the image in the top left corner of the bounding rectangle CGRect imageBox = CGRectMake( boundsRect.origin.x, boundsRect.origin.y + boundsRect.size.height - imageHeight, imageWidth, imageHeight); |
Having the final box, the image can be converted to PDF using the code in the first half of the article.
The code for the new conversion method is placed in the convertImageToPDF:withResolution:maxBoundsRect:pageSize:
static method in the PDFImageConverter
utility class, it is used like this:
1 2 3 4 5 6 7 8 | CGSize pageSize = CGSizeMake(612, 792); CGRect imageBoundsRect = CGRectMake(50, 50, 512, 692); NSData *pdfData = [PDFImageConverter convertImageToPDF: image withResolution: 300 maxBoundsRect: imageBoundsRect pageSize: pageSize]; NSString *path = [NSHomeDirectory() stringByAppendingPathComponent: @"Documents/image.pdf"]; [pdfData writeToFile:path atomically:NO]; |
Source code: PDFImageConverter.zip
[relatedPosts]
April 29th, 2011 - 07:47
It would be possible to add a digital signature (certificate, hash, etc) to a PDF on the iPhone?
Congratulations on the blog
April 29th, 2011 - 12:47
Current PDF API in iOS does not support adding new objects in a PDF file, such as digital signatures, annotations, etc. Let’s hope this will be fixed in iOS 5 🙂
March 18th, 2014 - 09:02
how to convert a pdf file into jpg format and again a jpg format in pdf file in ipad
plz show of full code in xcode 5.0
July 11th, 2011 - 20:23
If I convert a PDF page to image, add some ink to it, and convert the image back to PDF, will it grow in size substantially? Is there an easy way to replace this new page in the original PDF document?
July 11th, 2011 - 20:38
The growth in size depends on the source PDF page. It is text and image based, the growth in size can be significant. If you convert the page to image at a lower resolution to get a smaller image, when you create the new page from the image it will look pixelated. If you convert the page to image at a higher resolution you will have an image with a higher quality but also greater size. I recommend to add the ink directly to the existing PDF page. You can create a CGPDFDocumentRef from your original PDF file, update the document’s pages and then save the new document.
July 11th, 2011 - 20:56
Thanks!
August 1st, 2011 - 21:10
We’re trying to save the ink in a separate context and send it up to the server with a document id where it will be combined with the original document. How can we determine what page of the PDF document the user is viewing?
August 3rd, 2011 - 09:53
In your application you should have a variable that stores the current displayed page, because you control what page is displayed on the screen.
September 16th, 2011 - 10:48
Thank you for your article. This works perfectly.But I am trying to convert tiff images to pdf. Actually it converts the first page of tiff images to pdf. Do you know how to convert multi-paged tiff images to pdf ?
September 27th, 2011 - 22:01
There is the NSTiffSplitter on GitHub (https://github.com/Sharrp/NSTiffSplitter) that lets you split multi frame TIFF image into single frames. You can draw then each frame on a PDF page.
May 11th, 2013 - 09:29
CEM, Have you found any soluion for the same?
March 8th, 2012 - 17:32
Thanks for this great post! I’m trying to take an image from the device’s camera and convert it to PDF. I’m using this method to grab the image and convert it with your class:
– (void)imagePickerController:(UIImagePickerController *)picker
didFinishPickingMediaWithInfo:(NSDictionary *)info
{
UIImage *image = [info objectForKey:UIImagePickerControllerOriginalImage];
NSData *pdfData = [PDFImageConverter convertImageToPDF: image
withHorizontalResolution: 300 verticalResolution: 300];
NSString *path = [NSHomeDirectory() stringByAppendingPathComponent: @”Documents/new.pdf”];
[pdfData writeToFile:path atomically:NO];
}
The issue is the image is saved rotated and it looks distorted. It works fine with any image that is not taken from the camera. Any ideas?
March 9th, 2012 - 19:31
If you can send me a sample project that I can run and reproduce the problem, I will investigate it and I’ll get back to you.
November 28th, 2013 - 11:29
any solution for this issue?
thanks!
November 28th, 2013 - 11:38
I don’t know as I have not received any sample project.
November 28th, 2013 - 12:52
I have exactly the same problem. If I take an image from the camera, it is rotated and the aspect ratio is incorrect.
I’ll check whether I can provide you an example if you are still so kind to look at it.
November 28th, 2013 - 13:22
Please check the following example: https://dl.dropboxusercontent.com/u/4095619/PhotoPicker_ResizeIssue.zip
If you take a portrait-image, you’ll see that problem.
It would be great if you could look at it and tell me, why this happens.
Thanks in Advance!
November 28th, 2013 - 14:17
I talked to my boss that I don’t like to get you help without any donation to you. He agreed that this would be a good idea. 🙂
Please give me some information how we can give you some donation for your help.
Thanks.. 🙂
August 29th, 2012 - 06:20
Can i use this code to convert SVG images to PDF .
August 29th, 2012 - 09:16
No, this code cannot be used to convert SVG to PDF.
August 29th, 2012 - 06:22
Can you provide a code that converts SVG File to PDF.
August 29th, 2012 - 09:16
I do not have such code.
January 19th, 2013 - 10:20
It’s a nice article. One thing what I observed was the UIImage will be converted to an image in PDF rather than actual PDF . Is there any way to convert it into actual PDF text.
February 9th, 2013 - 19:43
Very helpful post as I have posted this on our forum so other people can find out also on image to PDF creation
February 10th, 2013 - 21:36
Thank you.
February 14th, 2013 - 12:48
Hi,
How can i make multiple image into a single pdf. I tried ur code. but it works oly for 1 image. The image which i choose last alone appears in the pdf but i want all the images which i choose from the photo library to be displayed in the pdf. please help me
February 14th, 2013 - 21:16
You have to call begin page, draw image, end page for each image you want to draw in the pdf file.
February 15th, 2013 - 07:55
Hi,
I could understatnd what you say. But i dont know how to implement as i am new to iOS . so, please help me with some sample code for multiple images in single pdf.
November 17th, 2013 - 10:38
I am also having difficulty saving multiple images as a single pdf. I loop through the images and apply the “begin”, “draw”, and “end” methods to each image, but the resulting pdf is only one of the images.
////////////////////////////////////////////////////////////////////////////////////////////////////////////
+(NSData*)convertArrayOfImagesToPDF: (NSArray*)theImages withResolution: (double)resolution {
if (resolution <= 0) {
return nil;
}
CGSize pageSize = CGSizeMake(612, 792);
NSMutableData *pdfFile = [[NSMutableData alloc] init];
CGDataConsumerRef pdfConsumer = CGDataConsumerCreateWithCFData((CFMutableDataRef)pdfFile);
// CGContextRef pdfContext = nil;
for (int i=0; i 1) || (sy > 1)) {
double maxScale = sx > sy ? sx : sy;
imageWidth = imageWidth / maxScale;
imageHeight = imageHeight / maxScale;
}
// Put the image in the top left corner of the bounding rectangle
CGRect imageBox = CGRectMake(0, 0 + pageSize.height – imageHeight, imageWidth, imageHeight);
CGRect mediaBox = CGRectMake(0, 0, pageSize.width, pageSize.height);
CGContextRef pdfContext = CGPDFContextCreate(pdfConsumer, &mediaBox, NULL);
CGContextBeginPage(pdfContext, &mediaBox);
CGContextDrawImage(pdfContext, imageBox, [image CGImage]);
CGContextEndPage(pdfContext);
CGContextRelease(pdfContext);
}
CGDataConsumerRelease(pdfConsumer);
return pdfFile;
}
November 17th, 2013 - 21:02
Nevermind – I figured it out. Just needed a good night’s sleep 🙂
Here’s is the method that will give multi-page pdf from an array of images, in case anyone else is interested:
//Code added by Chris Ruddell
////////////////////////////////////
+(NSData*)convertArrayOfImagesToPDF: (NSArray*)theImages withResolution: (double)resolution {
if (resolution < = 0) { return nil; } CGSize pageSize = CGSizeMake(612, 792); NSMutableData *pdfFile = [[NSMutableData alloc] init]; CGDataConsumerRef pdfConsumer = CGDataConsumerCreateWithCFData((CFMutableDataRef)pdfFile); CGRect mediaBox = CGRectMake(0, 0, pageSize.width, pageSize.height); CGContextRef pdfContext = CGPDFContextCreate(pdfConsumer, &mediaBox, NULL); for (int i=0; i < theImages.count; i++) { double maxScale = sx > sy ? sx : sy; imageWidth = imageWidth / maxScale; imageHeight = imageHeight / maxScale; // Put the image in the top left corner of the bounding rectangle CGRect imageBox = CGRectMake(0, 0 + pageSize.height - imageHeight, imageWidth, imageHeight); UIImage* image = [theImages objectAtIndex: i]; CGContextBeginPage(pdfContext, &mediaBox); CGContextDrawImage(pdfContext, imageBox, [image CGImage]); CGContextEndPage(pdfContext); } CGContextRelease(pdfContext); CGDataConsumerRelease(pdfConsumer); return pdfFile; }
May 15th, 2014 - 19:33
I’m using this code in a Cocoa app to see what I can do with some large images I have. This works very well when the image is 72 DPI but there’s a lot of detail lost when the DPI is higher. I have a large map that was scanned with 400 DPI that I’m trying to convert to PDF. Any ideas on how to handle that?
Thank you for any thoughts you have,
Nick
May 16th, 2014 - 13:02
If you can send me a sample image and the output PDF file, I’ll take a look at them and give you an answer.
May 18th, 2014 - 17:25
Hi iPDFdev,
Here’s a link to one of the images that’s having a problem:
https://www.dropbox.com/s/s2r56rw3npmmrnq/Half_Dome_50.jpg
Preview print to PDF works with it, this is the functionality I’m trying to get working. I know the PDF will be much bigger than the image and that’s fine, I just want this functionality in my toolbox.
Thank you for taking a look!
Nick
October 27th, 2014 - 09:50
I have the same issue as Dana & Stefan :
“The issue is the image is saved rotated and it looks distorted. It works fine with any image that is not taken from the camera. Any ideas?”
Any solutions anyone ?
thanks
Frank
October 27th, 2014 - 10:14
I updated the article and the attached archive to include to source code that handles the image orientation. The images taken with the camera have a fixed layout and they are logically rotated through the Orientation tag in the EXIF data. The code now handles this situation correctly.
October 27th, 2014 - 10:17
Great !! Thanks, I’ll use the updated archive.
November 20th, 2015 - 11:28
Great article! I want to add electronic signature (handwriting signature) into a PDF. Do you think if I can do it directly with the quartz instead of using commercial PDF lib? I am sorry for the silly question because I am new to iOS development.
Best,
Thanh
May 20th, 2016 - 13:36
How can i add water mark in right bottom corner of pdf?
October 10th, 2016 - 02:13
I think you may have these lines wrong
double pageWidth = image.size.width * image.scale * 72 / horzRes;
double pageHeight = image.size.height * image.scale * 72 / vertRes;
on this code horzRez and vertRez are divisors. That means that if you increase the resolution your final variables will have smaller values. As far as I know, higher resolutions will always give bigger contexts.
so, I suppose these lines have to be
double pageWidth = image.size.width * image.scale * horzRes / 72;
double pageHeight = image.size.height * image.scale * vertRes / 72;
isn’t it?
October 10th, 2016 - 09:32
The article refers to image to PDF conversion (not PDF to image). In this scenario, the higher the resolution you want the smaller the image will appear on the page.
October 10th, 2016 - 23:28
ok, if what you say is true then I do not understand the whole thing. I will give the example I am trying to understand. I have a picture taken by the camera that is 720 x 720 pixels. I want to print this picture and I want this picture, when printed at 600 dpi, to be exactly 2 x 2 inches. What is the size of the context I need to create?
I thought this: PDF uses the reference of 72 points = 1 inch. If you will embed a 720 x 720 pixels in a 72 x 72 context, then, each inch will be 720 dpi when printed at 72 dpi. If I print it at 600 dpi the final printing will have 1.2 inches, right?
so, what context should I have to generate a PDF that will print at 100 % scale to be 2 x 2 inches?
October 12th, 2016 - 15:20
There are 2 resolutions here: the resolution of the embedded image in the PDF and the resolution of the printing device and they are totally independent. Please see this article that explains the situation: http://ipdfdev.com/2016/07/06/what-resolution-pdf-files/
If your image is 720*720 pixels and you want its size on the page to be 2*2 inches, then you create a PDF file that is 2*2 inches and draw the image in it. The resolution of the embedded image will be 720/2 = 360dpi. When you print the PDF file it will always be 2*2 inches, no matter the dpi of the printer. The higher the printer dpi, the more details you will have in the printout, but the printout size will always be 2*2 inches.