16

Is there any way to get the content of a UIWebView and convert it to a PDF or PNG file? I'd like to get similar output to that available on the Mac by selecting the PDF button when printing from Safari, for example. I'm assuming this isn't possible/built in yet, but hopefully I'll be surprised and find a way to get the content from a webview to a file.

Thanks!

mjdth
  • 6,536
  • 6
  • 37
  • 44

4 Answers4

21

You can use the following category on UIView to create a PDF file:

#import <QuartzCore/QuartzCore.h>

@implementation UIView(PDFWritingAdditions)

- (void)renderInPDFFile:(NSString*)path
{
    CGRect mediaBox = self.bounds;
    CGContextRef ctx = CGPDFContextCreateWithURL((CFURLRef)[NSURL fileURLWithPath:path], &mediaBox, NULL);

    CGPDFContextBeginPage(ctx, NULL);
    CGContextScaleCTM(ctx, 1, -1);
    CGContextTranslateCTM(ctx, 0, -mediaBox.size.height);
    [self.layer renderInContext:ctx];
    CGPDFContextEndPage(ctx);
    CFRelease(ctx);
}

@end

Bad news: UIWebView does not create nice shapes and text in the PDF, but renders itself as an image into the PDF.

Nikolai Ruhe
  • 81,520
  • 17
  • 180
  • 200
  • Thanks for the post. I'm getting a problem though where the CGPDFContextCreateWithURL is not correctly creating the context. I've tried several different urls and it doesn't seem to change anything. Any ideas? – mjdth Mar 23 '10 at 21:04
  • There was a bug in how the CFURLRef was created. I fixed it and it should work now for proper file URLs. – Nikolai Ruhe Aug 15 '10 at 09:50
  • Rendering UILabels with this method seem to rasterize the text. If you want your text to remain vectorized, draw them directly into the layer as described here http://stackoverflow.com/questions/2209734/add-text-to-calayer –  Oct 02 '11 at 19:45
  • Can anyone tell me how to access this method defined in category in my viewController ? coz category is completely new to me – Raj Oct 26 '12 at 10:50
  • @NikolaiRuhe If we want to render the content inside a UIWebView which is scrollable because of content not fitting inside the frame, this answer described above renders just what is visible within the UIWebView frame. I have tried giving a large hypothetical frame for CGRect mediaBox = large hypothetical frame; but still the result is same. Only what's visible in the webView frame gets rendered in the pdf. Any way we can render the whole webView content (even whatever is not visible but loaded inside webview) in the pdf? – SayeedHussain Dec 10 '12 at 11:18
  • @crypticcoder To do that you have to set the bounds of the view to your desired dimensions before calling above method. – Nikolai Ruhe Dec 10 '12 at 11:25
5

Creating a image from a web view is simple:

UIImage* image = nil;

UIGraphicsBeginImageContext(offscreenWebView_.frame.size);
{
    [offscreenWebView_.layer renderInContext: UIGraphicsGetCurrentContext()];
    image = UIGraphicsGetImageFromCurrentImageContext();
}
UIGraphicsEndImageContext();

Once you have the image you can save it as a PNG.

Creating PDFs is also possible in a very similar way, but only on a yet unreleased iPhone OS version.

Stefan Arentz
  • 34,311
  • 8
  • 67
  • 88
  • I think that the CGPDF... API existed beginning from iPhone OS 2.0. – Nikolai Ruhe Mar 16 '10 at 14:00
  • Yeah but I thought rendering a layer into a PDF context was something new. – Stefan Arentz Mar 16 '10 at 14:31
  • This answer almost works too. The problem is the size of the page changes between pages. Is there any way to determine the size of the content in the webview so that I can create a correctly sized context? – mjdth Mar 23 '10 at 23:21
  • @St3fan If we want to render the content inside a UIWebView which is scrollable because of content not fitting inside the frame, this answer described above renders just what is visible within the UIWebView frame. I have tried giving a large hypothetical frame for CGRect mediaBox = large hypothetical frame; but still the result is same. Only what's visible in the webView frame gets rendered in the pdf. Any way we can render the whole webView content (even whatever is not visible but loaded inside webview) in the pdf? – SayeedHussain Dec 10 '12 at 11:19
  • @mjdth If we want to render the content inside a UIWebView which is scrollable because of content not fitting inside the frame, this answer described above renders just what is visible within the UIWebView frame. I have tried giving a large hypothetical frame for CGRect mediaBox = large hypothetical frame; but still the result is same. Only what's visible in the webView frame gets rendered in the pdf. Any way we can render the whole webView content (even whatever is not visible but loaded inside webview) in the pdf? – SayeedHussain Dec 10 '12 at 11:19
0

The code below will convert the (full) content of a UIWebView to an UIImage.

After rendering the UIImage I write it to disk as PNG to see the result.
Of course you could do with the UIImage whatever you like.

UIImage *image = nil;
CGRect oldFrame = webView.frame;

// Resize the UIWebView, contentSize could be > visible size
[webView sizeToFit];
CGSize fullSize = webView.scrollView.contentSize;

// Render the layer content onto the image  
UIGraphicsBeginImageContext(fullSize);
CGContextRef resizedContext = UIGraphicsGetCurrentContext();
[webView.layer renderInContext:resizedContext];
image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

// Revert the UIWebView back to its old size
webView.frame = oldFrame;

// Write the UIImage to disk as PNG so that we can see the result
NSString *path= [NSHomeDirectory() stringByAppendingPathComponent:@"Documents/Test.png"];
[UIImagePNGRepresentation(image) writeToFile:path atomically:YES];

Note: make sure the UIWebView is fully loaded (UIWebViewDelegate or loading property).

Yvo
  • 18,681
  • 11
  • 71
  • 90
0

@mjdth, try fileURLWithPath:isDirectory: instead. URLWithString wasn't working for me either.

@implementation UIView(PDFWritingAdditions)

- (void)renderInPDFFile:(NSString*)path
{
    CGRect mediaBox = self.bounds;
    CGContextRef ctx = CGPDFContextCreateWithURL((CFURLRef)[NSURL fileURLWithPath:path isDirectory:NO], &mediaBox, NULL);

    CGPDFContextBeginPage(ctx, NULL);
    CGContextScaleCTM(ctx, 1, -1);
    CGContextTranslateCTM(ctx, 0, -mediaBox.size.height);
    [self.layer renderInContext:ctx];
    CGPDFContextEndPage(ctx);
    CFRelease(ctx);
}

@end
Ford
  • 1,485
  • 3
  • 14
  • 20