Monday 15 April 2013

Convert Html to pdf in iOS


 In this post we will learn on how to generate a pdf from an html file, or convert an html rendered in a UIWebView into a pdf.
Steps to convert html file to pdf
Step 1: Create a new project, and choose View Based Application from the project template. We will name our project “HtmlToPdfDemo”. 
Step 2:  Then add UIWebView into our HtmlToPdfDemoViewController.xib file. We will now create an outlet for our UIWebView, and write an action as follows:
#import <UIKit/UIKit.h>

@interface HtmlToPdfDemoViewController: UIViewController
{
    UIWebView *webView;
}
@property (nonatomic,retain) IBOutlet UIWebView *webView;

- (IBAction) htmlToPdfButtonPressed:(id)sender;

@end

Step 3:  Our initial project setup is complete, we will get started with pdf generation stuff. I have included a Demo.html file in the bundle, and we will convert this html into a pdf . For this we first load our webView in our viewDidLoad as follows.
- (void)viewDidLoad
{
   [super viewDidLoad];
   NSURL *url = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"Demo" ofType:@"html"]];
   NSURLRequest *req = [NSURLRequest requestWithURL:url];
   [webView loadRequest:req];
}
Step 4: Our pdf converting code does not really belong in the HtmlToPdfDemoViewController, so let’s farm that off into a new UIViewController called HTMLtoPDF. Create a new file with the iOS\Cocoa Touch\Objective-C class template, enter HTMLtoPDF for the Class and UIViewController for the subclass,  and finish creating the file. class implements UIWebViewDelegate
    Let’s create a new method in the HTMLtoPDF.m file called createPDFWithHTML: and make it a static method. Also predeclare this method in HTMLtoPDF.h file.
+ (id)createPDFWithHTML:(NSString*)HTML pathForPDF:(NSString*)PDFpath delegate:(id )delegate
               pageSize:(CGSize)pageSize margins:(UIEdgeInsets)pageMargins;
Step 5: Now write one protocol HTMLtoPDFDelegate which extend the protocal to be of type “NSObject” In which we can declare the two methods HTMLtoPDFDidSucceed: and HTMLtoPDFDidFail: These protocol methods will returns the generated pdf file to HtmlToPdfDemoViewController. Following are the code for HTMLtoPDF.h file.
// Declare pdf paper size
#define kPaperSizeA4 CGSizeMake(595,842)
#define kPaperSizeLetter CGSizeMake(612,792)

@protocol NDHTMLtoPDFDelegate 
@optional
- (void)HTMLtoPDFDidSucceed:(NDHTMLtoPDF*)htmlToPDF;
- (void)HTMLtoPDFDidFail:(NDHTMLtoPDF*)htmlToPDF;
@end

@interface NDHTMLtoPDF : UIViewController 
@property (nonatomic, weak) id  delegate;
@property (nonatomic, strong, readonly) NSString *PDFpath;

+ (id)createPDFWithHTML:(NSString*)HTML pathForPDF:(NSString*)PDFpath delegate:(id )delegate pageSize:(CGSize)pageSize margins:(UIEdgeInsets)pageMargins;

@end
To convert html to pdf look at the HTMLtoPDF.m 
#import "NDHTMLtoPDF.h"

@interface UIPrintPageRenderer (PDF)
- (NSData*) printToPDF;
@end

@implementation NDHTMLtoPDF
- (void)viewDidLoad
{
    [super viewDidLoad];
    self.webview = [[UIWebView alloc] initWithFrame:self.view.frame];
    webview.delegate = self;
    [self.view addSubview:webview];
    [webview loadHTMLString:self.HTML baseURL:nil];
}

+ (id)createPDFWithHTML:(NSString*)HTML pathForPDF:(NSString*)PDFpath delegate:(id )delegate
               pageSize:(CGSize)pageSize margins:(UIEdgeInsets)pageMargins
{
    NDHTMLtoPDF *creator = [[NDHTMLtoPDF alloc] initWithHTML:HTML delegate:delegate pathForPDF:PDFpath pageSize:pageSize margins:pageMargins];
    return creator;
}

- (id)initWithHTML:(NSString*)HTML delegate:(id )delegate
        pathForPDF:(NSString*)PDFpath pageSize:(CGSize)pageSize margins:(UIEdgeInsets)pageMargins
{
    self = [super init];
    if (self)
    {
        self.HTML = HTML;
        self.delegate = delegate;
        self.PDFpath = PDFpath;
        self.pageMargins = pageMargins;
        self.pageSize = pageSize;
        
        [[UIApplication sharedApplication].delegate.window addSubview:self.view];
        self.view.frame = CGRectMake(0, 0, 1, 1);
        self.view.alpha = 0.0;
    }
    return self;
}

- (void)webViewDidFinishLoad:(UIWebView *)webView
{
    if (webView.isLoading) return;
    
    UIPrintPageRenderer *render = [[UIPrintPageRenderer alloc] init];
    [render addPrintFormatter:webView.viewPrintFormatter startingAtPageAtIndex:0];
    CGRect printableRect = CGRectMake(self.pageMargins.left,
                                  self.pageMargins.top,
                                  self.pageSize.width - self.pageMargins.left - self.pageMargins.right,
                                  self.pageSize.height - self.pageMargins.top - self.pageMargins.bottom);
   CGRect paperRect = CGRectMake(0, 0, self.pageSize.width, self.pageSize.height);
    
    [render setValue:[NSValue valueWithCGRect:paperRect] forKey:@"paperRect"];
    [render setValue:[NSValue valueWithCGRect:printableRect] forKey:@"printableRect"];

    NSData *pdfData = [render printToPDF];
    [pdfData writeToFile: self.PDFpath  atomically: YES];
    [self terminateWebTask];

    if (self.delegate && [self.delegate respondsToSelector:@selector(HTMLtoPDFDidSucceed:)])
        [self.delegate HTMLtoPDFDidSucceed:self];
    
}

- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error
{
    if (webView.isLoading) return;
    [self terminateWebTask];
    if (self.delegate && [self.delegate respondsToSelector:@selector(HTMLtoPDFDidFail:)])
      [self.delegate HTMLtoPDFDidFail:self];
}

- (void)terminateWebTask
{
    [self.webview stopLoading];
    self.webview.delegate = nil;
    [self.webview removeFromSuperview];
    [self.view removeFromSuperview];
    self.webview = nil;
}
@end

@implementation UIPrintPageRenderer (PDF)

- (NSData*) printToPDF
{
    NSMutableData *pdfData = [NSMutableData data];
    
    UIGraphicsBeginPDFContextToData( pdfData, CGRectZero, nil );
        
    [self prepareForDrawingPages: NSMakeRange(0, self.numberOfPages)];
    
    CGRect bounds = UIGraphicsGetPDFContextBounds();
        
    for ( int i = 0 ; i < self.numberOfPages ; i++ )
    {
        UIGraphicsBeginPDFPage();
        
        [self drawPageAtIndex: i inRect: bounds];
    }
    UIGraphicsEndPDFContext();
    return pdfData;
}

@end
Step 6: Move to the HtmlToPdfDemoViewController.m  htmlToPdfButtonPressed: method. From that method we have to call createPDFWithHTML: method and pass html, path for storing the pdf file, page size and margins.
- (IBAction)htmlToPdfButtonPressed:(id)sender
{
    NSString *html = [webView      stringByEvaluatingJavaScriptFromString:@"document.documentElement.outerHTML"];

    self.PDFCreator = [NDHTMLtoPDF createPDFWithHTML:html
                                         pathForPDF:[@"~/Documents/demo.pdf"           stringByExpandingTildeInPath]
                                           delegate:self
                                           pageSize:kPaperSizeLetter
                                            margins:UIEdgeInsetsMake(10, 5, 10, 5)];
}
Step 7:  In HtmlToPdfDemoViewController implements the HTMLtoPDFDelegate methods where you can get pdf file.
- (void)HTMLtoPDFDidSucceed:(NDHTMLtoPDF*)htmlToPDF
{
    NSLog(@"HTMLtoPDF did succeed (%@ / %@)", htmlToPDF, htmlToPDF.PDFpath);
}

- (void)HTMLtoPDFDidFail:(NDHTMLtoPDF*)htmlToPDF
{
    NSLog(@"HTMLtoPDF did fail (%@)", htmlToPDF);
}
Finally generated pdf (demo.pdf)  saves into the documents directory. If you run the app on simulator you can find (demo.pdf) in Documents directory in our Library folder, inside iPhone Simulator.