How to Create a PDF File Programmatically in an iOS Application

How can I create a PDF file programmatically in an iOS application?

See "Drawing with Quartz" to see how to create a PDF Graphics Context.

Some notes:

"iPhoneOSNote: If you want to create a PDF graphics context in an iPhone application, make sure you also read “Drawing to a Graphics Context in iPhone OS” (page 27)."

"You can write any content to a PDF that’s appropriate for your application—images, text, path drawing—and you can add links and encryption. For more information see “PDF Document Creation, Viewing, and Transforming” (page 177)."

Create and store PDF document programmatically using Swift for iOS

I used this code to create and save the file (using HTML)

func createPDF() {
let html = "<b>Hello <i>World!</i></b> <p>Generate PDF file from HTML in Swift</p>"
let fmt = UIMarkupTextPrintFormatter(markupText: html)

// 2. Assign print formatter to UIPrintPageRenderer

let render = UIPrintPageRenderer()
render.addPrintFormatter(fmt, startingAtPageAt: 0)

// 3. Assign paperRect and printableRect

let page = CGRect(x: 0, y: 0, width: 595.2, height: 841.8) // A4, 72 dpi
let printable = page.insetBy(dx: 0, dy: 0)

render.setValue(NSValue(cgRect: page), forKey: "paperRect")
render.setValue(NSValue(cgRect: printable), forKey: "printableRect")

// 4. Create PDF context and draw

let pdfData = NSMutableData()
UIGraphicsBeginPDFContextToData(pdfData, .zero, nil)

for i in 1...render.numberOfPages {
UIGraphicsBeginPDFPage();
let bounds = UIGraphicsGetPDFContextBounds()
render.drawPage(at: i - 1, in: bounds)
}

UIGraphicsEndPDFContext();

// 5. Save PDF file

let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]

pdfData.write(toFile: "\(documentsPath)/file.pdf", atomically: true)
}

Then I loaded it into UIWebView from the documents directory with this code:

func loadPDF(filename: String) {
let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
let url = URL(fileURLWithPath: documentsPath, isDirectory: true).appendingPathComponent(filename).appendingPathExtension("pdf")
let urlRequest = URLRequest(url: url)
webView.loadRequest(urlRequest)
}

iOS SDK - Programmatically generate a PDF file

A couple things...

First, there is a bug with CoreGraphics PDF generation in iOS that results in corrupted PDFs. I know this issue exists up to and including iOS 4.1 (I haven't tested iOS 4.2). The issue is related to fonts and only shows up if you include text in your PDF. The symptom is that, when generating the PDF, you'll see errors in the debug console that look like this:

<Error>: can't get CIDs for glyphs for 'TimesNewRomanPSMT'

The tricky aspect is that the resulting PDF will render fine in some PDF readers, but fail to render in other places. So, if you have control over the software that will be used to open your PDF, you may be able to ignore this issue (e.g., if you only intend to display the PDF on the iPhone or Mac desktops, then you should be fine using CoreGraphics). However, if you need to create a PDF that works anywhere, then you should take a closer look at this issue. Here's some additional info:

http://www.iphonedevsdk.com/forum/iphone-sdk-development/15505-pdf-font-problem-cant-get-cids-glyphs.html#post97854

As a workaround, I've used libHaru successfully on iPhone as a replacement for CoreGraphics PDF generation. It was a little tricky getting libHaru to build with my project initially, but once I got my project setup properly, it worked fine for my needs.

Second, depending on the format/layout of your PDF, you might consider using Interface Builder to create a view that serves as a "template" for your PDF output. You would then write code to load the view, fill in any data (e.g., set text for UILabels, etc.), then render the individual elements of the view into the PDF. In other words, use IB to specify coordinates, fonts, images, etc. and write code to render various elements (e.g., UILabel, UIImageView, etc.) in a generic way so you don't have to hard-code everything. I used this approach and it worked out great for my needs. Again, this may or may not make sense for your situation depending on the formatting/layout needs of your PDF.

EDIT: (response to 1st comment)

My implementation is part of a commercial product meaning that I can't share the full code, but I can give a general outline:

I created a .xib file with a view and sized the view to 850 x 1100 (my PDF was targeting 8.5 x 11 inches, so this makes it easy to translate to/from design-time coordinates).

In code, I load the view:

- (UIView *)loadTemplate
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@"ReportTemplate" owner:self options:nil];
for (id view in nib) {
if ([view isKindOfClass: [UIView class]]) {
return view;
}
}

return nil;
}

I then fill in various elements. I used tags to find the appropriate elements, but you could do this other ways. Example:

UILabel *label = (UILabel *)[templateView viewWithTag:TAG_FIRST_NAME];
if (label != nil) {
label.text = (firstName != nil) ? firstName : @"None";

Then I call a function to render the view to the PDF file. This function recursively walks the view hierarchy and renders each subview. For my project, I need to support only Label, ImageView, and View (for nested views):

- (void)addObject:(UIView *)view
{
if (view != nil && !view.hidden) {
if ([view isKindOfClass:[UILabel class]]) {
[self addLabel:(UILabel *)view];
} else if ([view isKindOfClass:[UIImageView class]]) {
[self addImageView:(UIImageView *)view];
} else if ([view isKindOfClass:[UIView class]]) {
[self addContainer:view];
}
}
}

As an example, here's my implementation of addImageView (HPDF_ functions are from libHaru):

- (void)addImageView:(UIImageView *)imageView
{
NSData *pngData = UIImagePNGRepresentation(imageView.image);
if (pngData != nil) {
HPDF_Image image = HPDF_LoadPngImageFromMem(_pdf, [pngData bytes], [pngData length]);
if (image != NULL) {
CGRect destRect = [self rectToPDF:imageView.frame];

float x = destRect.origin.x;
float y = destRect.origin.y - destRect.size.height;
float width = destRect.size.width;
float height = destRect.size.height;

HPDF_Page page = HPDF_GetCurrentPage(_pdf);
HPDF_Page_DrawImage(page, image, x, y, width, height);
}
}
}

Hopefully that gives you the idea.

Generate PDF file in ios

Create your custom view to add image, tableview or anything. Points the pdf converter to the mutable data object and to the UIView to be converted to pdf.

Try this:

NSMutableData *pdfData = [NSMutableData data];
UIGraphicsBeginPDFContextToData(pdfData,self.bounds, nil);
UIGraphicsBeginPDFPage();

CGContextRef pdfContext = UIGraphicsGetCurrentContext();

// draws rect to the view and thus this is captured by UIGraphicsBeginPDFContextToData
[self.layer renderInContext:pdfContext];

// remove PDF rendering context
UIGraphicsEndPDFContext();

// Retrieves the document directories from the iOS device
NSArray* documentDirectories = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask,YES);

NSString* documentDirectory = [documentDirectories objectAtIndex:0];
NSString* documentDirectoryFilename = [documentDirectory stringByAppendingPathComponent:@"ess.pdf"];

// instructs the mutable data object to write its context to a file on disk
[pdfData writeToFile:documentDirectoryFilename atomically:YES];

How can I create a PDF file programmatically in an iOS application?

See "Drawing with Quartz" to see how to create a PDF Graphics Context.

Some notes:

"iPhoneOSNote: If you want to create a PDF graphics context in an iPhone application, make sure you also read “Drawing to a Graphics Context in iPhone OS” (page 27)."

"You can write any content to a PDF that’s appropriate for your application—images, text, path drawing—and you can add links and encryption. For more information see “PDF Document Creation, Viewing, and Transforming” (page 177)."

Sample code to create pdf programmatically

Here I Have Best Example of how to create .pdf file in iPhone :)

PdfGenerationDemoViewController.h

#import <UIKit/UIKit.h>

#define kBorderInset 20.0
#define kBorderWidth 1.0
#define kMarginInset 10.0

//Line drawing
#define kLineWidth 1.0

@interface PdfGenerationDemoViewController : UIViewController
{
CGSize pageSize;
}

- (IBAction)generatePdfButtonPressed:(id)sender;

@end

PdfGenerationDemoViewController.m

#import "PdfGenerationDemoViewController.h"

@interface PdfGenerationDemoViewController (Private)
- (void) generatePdfWithFilePath: (NSString *)thefilePath;
- (void)drawPageNumber:(NSInteger)pageNum;
- (void) drawBorder;
- (void) drawText;
- (void) drawLine;
- (void) drawHeader;
- (void) drawImage;
@end

@implementation PdfGenerationDemoViewController

#pragma mark - Private Methods
- (void) drawBorder
{
CGContextRef currentContext = UIGraphicsGetCurrentContext();
UIColor *borderColor = [UIColor brownColor];

CGRect rectFrame = CGRectMake(kBorderInset, kBorderInset, pageSize.width-kBorderInset*2, pageSize.height-kBorderInset*2);

CGContextSetStrokeColorWithColor(currentContext, borderColor.CGColor);
CGContextSetLineWidth(currentContext, kBorderWidth);
CGContextStrokeRect(currentContext, rectFrame);
}

- (void)drawPageNumber:(NSInteger)pageNumber
{
NSString* pageNumberString = [NSString stringWithFormat:@"Page %d", pageNumber];
UIFont* theFont = [UIFont systemFontOfSize:12];

CGSize pageNumberStringSize = [pageNumberString sizeWithFont:theFont
constrainedToSize:pageSize
lineBreakMode:UILineBreakModeWordWrap];

CGRect stringRenderingRect = CGRectMake(kBorderInset,
pageSize.height - 40.0,
pageSize.width - 2*kBorderInset,
pageNumberStringSize.height);

[pageNumberString drawInRect:stringRenderingRect withFont:theFont lineBreakMode:UILineBreakModeWordWrap alignment:UITextAlignmentCenter];
}

- (void) drawHeader
{
CGContextRef currentContext = UIGraphicsGetCurrentContext();
CGContextSetRGBFillColor(currentContext, 0.3, 0.7, 0.2, 1.0);

NSString *textToDraw = @"Pdf Demo - iOSLearner.com";

UIFont *font = [UIFont systemFontOfSize:24.0];

CGSize stringSize = [textToDraw sizeWithFont:font constrainedToSize:CGSizeMake(pageSize.width - 2*kBorderInset-2*kMarginInset, pageSize.height - 2*kBorderInset - 2*kMarginInset) lineBreakMode:UILineBreakModeWordWrap];

CGRect renderingRect = CGRectMake(kBorderInset + kMarginInset, kBorderInset + kMarginInset, pageSize.width - 2*kBorderInset - 2*kMarginInset, stringSize.height);

[textToDraw drawInRect:renderingRect withFont:font lineBreakMode:UILineBreakModeWordWrap alignment:UITextAlignmentLeft];
}

- (void) drawText
{
CGContextRef currentContext = UIGraphicsGetCurrentContext();
CGContextSetRGBFillColor(currentContext, 0.0, 0.0, 0.0, 1.0);

NSString *textToDraw = @"Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Nam liber tempor cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer possim assum. Typi non habent claritatem insitam; est usus legentis in iis qui facit eorum claritatem. Investigationes demonstraverunt lectores legere me lius quod ii legunt saepius. Claritas est etiam processus dynamicus, qui sequitur mutationem consuetudium lectorum. Mirum est notare quam littera gothica, quam nunc putamus parum claram, anteposuerit litterarum formas humanitatis per seacula quarta decima et quinta decima. Eodem modo typi, qui nunc nobis videntur parum clari, fiant sollemnes in futurum.";

UIFont *font = [UIFont systemFontOfSize:14.0];

CGSize stringSize = [textToDraw sizeWithFont:font
constrainedToSize:CGSizeMake(pageSize.width - 2*kBorderInset-2*kMarginInset, pageSize.height - 2*kBorderInset - 2*kMarginInset)
lineBreakMode:UILineBreakModeWordWrap];

CGRect renderingRect = CGRectMake(kBorderInset + kMarginInset, kBorderInset + kMarginInset + 50.0, pageSize.width - 2*kBorderInset - 2*kMarginInset, stringSize.height);

[textToDraw drawInRect:renderingRect
withFont:font
lineBreakMode:UILineBreakModeWordWrap
alignment:UITextAlignmentLeft];

}

- (void) drawLine
{
CGContextRef currentContext = UIGraphicsGetCurrentContext();

CGContextSetLineWidth(currentContext, kLineWidth);

CGContextSetStrokeColorWithColor(currentContext, [UIColor blueColor].CGColor);

CGPoint startPoint = CGPointMake(kMarginInset + kBorderInset, kMarginInset + kBorderInset + 40.0);
CGPoint endPoint = CGPointMake(pageSize.width - 2*kMarginInset -2*kBorderInset, kMarginInset + kBorderInset + 40.0);

CGContextBeginPath(currentContext);
CGContextMoveToPoint(currentContext, startPoint.x, startPoint.y);
CGContextAddLineToPoint(currentContext, endPoint.x, endPoint.y);

CGContextClosePath(currentContext);
CGContextDrawPath(currentContext, kCGPathFillStroke);
}

- (void) drawImage
{
UIImage * demoImage = [UIImage imageNamed:@"demo.png"];
[demoImage drawInRect:CGRectMake( (pageSize.width - demoImage.size.width/2)/2, 350, demoImage.size.width/2, demoImage.size.height/2)];
}

- (void) generatePdfWithFilePath: (NSString *)thefilePath
{
UIGraphicsBeginPDFContextToFile(thefilePath, CGRectZero, nil);

NSInteger currentPage = 0;
BOOL done = NO;
do
{
//Start a new page.
UIGraphicsBeginPDFPageWithInfo(CGRectMake(0, 0, pageSize.width, pageSize.height), nil);

//Draw a page number at the bottom of each page.
currentPage++;
[self drawPageNumber:currentPage];

//Draw a border for each page.
[self drawBorder];

//Draw text fo our header.
[self drawHeader];

//Draw a line below the header.
[self drawLine];

//Draw some text for the page.
[self drawText];

//Draw an image
[self drawImage];
done = YES;
}
while (!done);

// Close the PDF context and write the contents out.
UIGraphicsEndPDFContext();
}

#pragma mark - View lifecycle

- (void)viewDidLoad
{
[super viewDidLoad];
}

- (void)viewDidUnload
{
[super viewDidUnload];
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}

- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}

- (IBAction)generatePdfButtonPressed:(id)sender
{
pageSize = CGSizeMake(612, 792);
NSString *fileName = @"Demo.pdf";
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *pdfFileName = [documentsDirectory stringByAppendingPathComponent:fileName];

[self generatePdfWithFilePath:pdfFileName];
}
@end


Related Topics



Leave a reply



Submit