Convert html file to PDF Document in iOS using Cocoa-Touch

10,472

Solution 1

To convert html to pdf, you need to:

  • Create a Webview and load the HTML.
  • Take Webview Capture.
  • Convert it into PDF file.

The second step (webview capture) have to be done once your webview content is loaded For this capture you can find sample as in How Do I Take a Screen Shot of a UIView? Then you use this context to convert into PDF file.

For the webview you don't need to show it, just create it without adding the view on screen.

Edit: Other detail, for webview height you can calculate the webview content height and change your webview frame

- (void)webViewDidFinishLoad:(UIWebView *)theWebView {
    NSUInteger contentHeight = [[theWebView stringByEvaluatingJavaScriptFromString:[NSString stringWithFormat:@"document.body.scrollHeight;"]] intValue];
    NSUInteger contentWidth = [[theWebView stringByEvaluatingJavaScriptFromString:[NSString stringWithFormat:@"document.body.scrollWidth;"]] intValue];
    [theWebView setFrame:CGRectMake(0, 0, contentWidth, contentHeight)];
}

Solution 2

I created a class based on every good advice I found around. I've been digging a lot and I hope my class will offer some good start for anyone trying to create multi-page PDF directly out of some HTML source.

You'll find the whole code here with some basic sample code : https://github.com/iclems/iOS-htmltopdf

I had just the same issue as you and my requirements were: - full PDF (real text, no bitmap) - smart multi-pages (compared to cutting a full height webview every X pixels...)

Thus, the solution I use is pretty nice as it resorts to the same tools iOS uses to split pages for print.

Let me explain, I setup a UIPrintPageRenderer based on the web view print formatter (first tip) :

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];

In the meantime, I have created a category on UIPrintPageRenderer to support:

-(NSData*) printToPDF
{
    [self doNotRasterizeSubviews:self.view];

    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;
}
Share:
10,472
Mike
Author by

Mike

Updated on June 04, 2022

Comments

  • Mike
    Mike almost 2 years

    I want to convert local html file on iPhone to PDF document. I have used the following code I found online to generate PDF file from Picture, I tried to replace the image with the html file but it failed. I would very much appreciate your help. Thanks in advance,

    // Our method to create a PDF file natively on the iPhone
    // This method takes two parameters, a CGRect for size and
    // a const char, which will be the name of our pdf file
    void CreatePDFFile (CGRect pageRect, const char *filename) {
    
        // This code block sets up our PDF Context so that we can draw to it
        CGContextRef pdfContext;
        CFStringRef path;
        CFURLRef url;
        CFMutableDictionaryRef myDictionary = NULL;
        // Create a CFString from the filename we provide to this method when we call it
        path = CFStringCreateWithCString (NULL, filename,
                                          kCFStringEncodingUTF8);
        // Create a CFURL using the CFString we just defined
        url = CFURLCreateWithFileSystemPath (NULL, path,
                                             kCFURLPOSIXPathStyle, 0);
        CFRelease (path);
        // This dictionary contains extra options mostly for 'signing' the PDF
        myDictionary = CFDictionaryCreateMutable(NULL, 0,
                                                 &kCFTypeDictionaryKeyCallBacks,
                                                 &kCFTypeDictionaryValueCallBacks);
        CFDictionarySetValue(myDictionary, kCGPDFContextTitle, CFSTR("My PDF File"));
        CFDictionarySetValue(myDictionary, kCGPDFContextCreator, CFSTR("My Name"));
        // Create our PDF Context with the CFURL, the CGRect we provide, and the above defined dictionary
        pdfContext = CGPDFContextCreateWithURL (url, &pageRect, myDictionary);
        // Cleanup our mess
        CFRelease(myDictionary);
        CFRelease(url);
        // Done creating our PDF Context, now it's time to draw to it
    
        // Starts our first page
        CGContextBeginPage (pdfContext, &pageRect);
    
        // Draws a black rectangle around the page inset by 50 on all sides
        CGContextStrokeRect(pdfContext, CGRectMake(50, 50, pageRect.size.width - 100, pageRect.size.height - 100));
    
        // This code block will create an image that we then draw to the page
        const char *picture = "Picture";
        CGImageRef image;
        CGDataProviderRef provider;
        CFStringRef picturePath;
        CFURLRef pictureURL;
    
        picturePath = CFStringCreateWithCString (NULL, picture,
                                          kCFStringEncodingUTF8);
        pictureURL = CFBundleCopyResourceURL(CFBundleGetMainBundle(), picturePath, CFSTR("png"), NULL);
        CFRelease(picturePath);
        provider = CGDataProviderCreateWithURL (pictureURL);
        CFRelease (pictureURL);
        image = CGImageCreateWithPNGDataProvider (provider, NULL, true, kCGRenderingIntentDefault);
        CGDataProviderRelease (provider);
        CGContextDrawImage (pdfContext, CGRectMake(200, 200, 207, 385),image);
        CGImageRelease (image);
    
    
        // End image code
    
        // Adding some text on top of the image we just added
        CGContextSelectFont (pdfContext, "Helvetica", 16, kCGEncodingMacRoman);
        CGContextSetTextDrawingMode (pdfContext, kCGTextFill);
        CGContextSetRGBFillColor (pdfContext, 0, 0, 0, 1);
        const char *text = "Hello World!";
        CGContextShowTextAtPoint (pdfContext, 260, 390, text, strlen(text));
        // End text
    
        // We are done drawing to this page, let's end it
        // We could add as many pages as we wanted using CGContextBeginPage/CGContextEndPage
        CGContextEndPage (pdfContext);
    
        // We are done with our context now, so we release it
        CGContextRelease (pdfContext);
    }
    
    - (IBAction)createPDF:(id)sender {
        NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
        NSString *saveDirectory = [paths objectAtIndex:0];
        NSString *saveFileName = @"myPDF.pdf";
        NSString *newFilePath = [saveDirectory stringByAppendingPathComponent:saveFileName];
        const char *filename = [newFilePath UTF8String];
        CreatePDFFile(CGRectMake(0, 0, 612, 792),filename);
    }