Creating PDF file from UIWebView
Solution 1
Use UIPrintPageRenderer
from UIWebView
Follow below steps :
Add Category
of UIPrintPageRenderer
for getting PDF Data
@interface UIPrintPageRenderer (PDF)
- (NSData*) printToPDF;
@end
@implementation UIPrintPageRenderer (PDF)
- (NSData*) printToPDF
{
NSMutableData *pdfData = [NSMutableData data];
UIGraphicsBeginPDFContextToData( pdfData, self.paperRect, 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
Add these define for A4 size
#define kPaperSizeA4 CGSizeMake(595.2,841.8)
Now in UIWebView's
webViewDidFinishLoad
delegate
use UIPrintPageRenderer
property
of UIWebView
.
- (void)webViewDidFinishLoad:(UIWebView *)awebView
{
if (awebView.isLoading)
return;
UIPrintPageRenderer *render = [[UIPrintPageRenderer alloc] init];
[render addPrintFormatter:awebView.viewPrintFormatter startingAtPageAtIndex:0];
//increase these values according to your requirement
float topPadding = 10.0f;
float bottomPadding = 10.0f;
float leftPadding = 10.0f;
float rightPadding = 10.0f;
CGRect printableRect = CGRectMake(leftPadding,
topPadding,
kPaperSizeA4.width-leftPadding-rightPadding,
kPaperSizeA4.height-topPadding-bottomPadding);
CGRect paperRect = CGRectMake(0, 0, kPaperSizeA4.width, kPaperSizeA4.height);
[render setValue:[NSValue valueWithCGRect:paperRect] forKey:@"paperRect"];
[render setValue:[NSValue valueWithCGRect:printableRect] forKey:@"printableRect"];
NSData *pdfData = [render printToPDF];
if (pdfData) {
[pdfData writeToFile:[NSString stringWithFormat:@"%@/tmp.pdf",NSTemporaryDirectory()] atomically: YES];
}
else
{
NSLog(@"PDF couldnot be created");
}
}
Solution 2
Thank you for your answers Firemarble, it helped me a lot. I managed to get a somewhat good looking results with this method :
-(void)createPDFfromUIView:(UIView*)aView saveToDocumentsWithFileName:(NSString*)aFilename
{
// Creates a mutable data object for updating with binary data, like a byte array
UIWebView *webView = (UIWebView*)aView;
NSString *heightStr = [webView stringByEvaluatingJavaScriptFromString:@"document.body.scrollHeight;"];
int height = [heightStr intValue];
// CGRect screenRect = [[UIScreen mainScreen] bounds];
// CGFloat screenHeight = (self.contentWebView.hidden)?screenRect.size.width:screenRect.size.height;
CGFloat screenHeight = webView.bounds.size.height;
int pages = ceil(height / screenHeight);
NSMutableData *pdfData = [NSMutableData data];
UIGraphicsBeginPDFContextToData(pdfData, webView.bounds, nil);
CGRect frame = [webView frame];
for (int i = 0; i < pages; i++) {
// Check to screenHeight if page draws more than the height of the UIWebView
if ((i+1) * screenHeight > height) {
CGRect f = [webView frame];
f.size.height -= (((i+1) * screenHeight) - height);
[webView setFrame: f];
}
UIGraphicsBeginPDFPage();
CGContextRef currentContext = UIGraphicsGetCurrentContext();
[[[webView subviews] lastObject] setContentOffset:CGPointMake(0, screenHeight * i) animated:NO];
[webView.layer renderInContext:currentContext];
}
UIGraphicsEndPDFContext();
// Retrieves the document directories from the iOS device
NSArray* documentDirectories = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask,YES);
NSString* documentDirectory = [documentDirectories objectAtIndex:0];
NSString* documentDirectoryFilename = [documentDirectory stringByAppendingPathComponent:aFilename];
// instructs the mutable data object to write its context to a file on disk
[pdfData writeToFile:documentDirectoryFilename atomically:YES];
[webView setFrame:frame];
}
Solution 3
Swift 2.0 solution, how to save pdf from webview to NSData:
func webViewDidFinishLoad(webView: UIWebView) {
let pdfData = createPdfFile(webView.viewPrintFormatter())
pdfData.writeToFile("/path/to/file", atomically: true)
}
func createPdfFile(printFormatter: UIViewPrintFormatter) -> NSData {
let renderer = UIPrintPageRenderer()
renderer.addPrintFormatter(printFormatter, startingAtPageAtIndex: 0);
let paperSize = CGSizeMake(self.view.frame.size.width, self.view.frame.size.height)
let printableRect = CGRectMake(0, 0, paperSize.width, paperSize.height)
let paperRect = CGRectMake(0, 0, paperSize.width, paperSize.height);
renderer.setValue(NSValue(CGRect: paperRect), forKey: "paperRect")
renderer.setValue(NSValue(CGRect: printableRect), forKey: "printableRect")
return renderer.printToPDF()
}
You also need to add UIWebViewDelegate to your ViewController and extension for UIPrintPageRenderer.
extension UIPrintPageRenderer {
func printToPDF() -> NSData {
let pdfData = NSMutableData()
UIGraphicsBeginPDFContextToData(pdfData, self.paperRect, nil)
self.prepareForDrawingPages(NSMakeRange(0, self.numberOfPages()))
let bounds = UIGraphicsGetPDFContextBounds()
for i in 0..<self.numberOfPages() {
UIGraphicsBeginPDFPage();
self.drawPageAtIndex(i, inRect: bounds)
}
UIGraphicsEndPDFContext();
return pdfData;
}
}
Solution 4
Swift 3 solution of @Dan Loewenherz answer:
func webViewDidFinishLoad(_ webView: UIWebView) {
let pdfData = createPdfFile(printFormatter: webView.viewPrintFormatter())
pdfData.write(toFile: "/path/to/file", atomically: true)
}
func createPdfFile(printFormatter: UIViewPrintFormatter) -> NSData {
let renderer = UIPrintPageRenderer()
renderer.addPrintFormatter(printFormatter, startingAtPageAt: 0)
let point = CGPoint(x:0,y:0)
let paperSize = CGSize(width: self.view.frame.size.width, height: self.view.frame.size.height)
let printableRect = CGRect(origin: point, size: CGSize(width:paperSize.width, height: paperSize.height))
let paperRect = CGRect(origin: point, size: CGSize(width: paperSize.width, height: paperSize.height))
renderer.setValue(NSValue(cgRect: paperRect), forKey: "paperRect")
renderer.setValue(NSValue(cgRect: printableRect), forKey: "printableRect")
return renderer.printToPDF()
}
extension UIPrintPageRenderer {
func printToPDF() -> NSData {
let pdfData = NSMutableData()
UIGraphicsBeginPDFContextToData(pdfData, self.paperRect, nil)
self.prepare(forDrawingPages: NSMakeRange(0, self.numberOfPages))
let bounds = UIGraphicsGetPDFContextBounds()
for i in 0..<self.numberOfPages {
UIGraphicsBeginPDFPage();
self.drawPage(at: i, in: bounds)
}
UIGraphicsEndPDFContext();
return pdfData;
}
}
Solution 5
UIWebView is deprecated now. Use WKWebView extension to create PDF from WKWebView
Credit: http://www.swiftdevcenter.com/create-pdf-from-uiview-wkwebview-and-uitableview/
Swift 4.2
extension WKWebView {
// Call this function when WKWebView finish loading
func exportAsPdfFromWebView() -> String {
let pdfData = createPdfFile(printFormatter: self.viewPrintFormatter())
return self.saveWebViewPdf(data: pdfData)
}
func createPdfFile(printFormatter: UIViewPrintFormatter) -> NSMutableData {
let originalBounds = self.bounds
self.bounds = CGRect(x: originalBounds.origin.x,
y: bounds.origin.y,
width: self.bounds.size.width,
height: self.scrollView.contentSize.height)
let pdfPageFrame = CGRect(x: 0, y: 0, width: self.bounds.size.width,
height: self.scrollView.contentSize.height)
let printPageRenderer = UIPrintPageRenderer()
printPageRenderer.addPrintFormatter(printFormatter, startingAtPageAt: 0)
printPageRenderer.setValue(NSValue(cgRect: UIScreen.main.bounds), forKey: "paperRect")
printPageRenderer.setValue(NSValue(cgRect: pdfPageFrame), forKey: "printableRect")
self.bounds = originalBounds
return printPageRenderer.generatePdfData()
}
// Save pdf file in document directory
func saveWebViewPdf(data: NSMutableData) -> String {
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
let docDirectoryPath = paths[0]
let pdfPath = docDirectoryPath.appendingPathComponent("webViewPdf.pdf")
if data.write(to: pdfPath, atomically: true) {
return pdfPath.path
} else {
return ""
}
}
}
extension UIPrintPageRenderer {
func generatePdfData() -> NSMutableData {
let pdfData = NSMutableData()
UIGraphicsBeginPDFContextToData(pdfData, self.paperRect, nil)
self.prepare(forDrawingPages: NSMakeRange(0, self.numberOfPages))
let printRect = UIGraphicsGetPDFContextBounds()
for pdfPage in 0..<self.numberOfPages {
UIGraphicsBeginPDFPage()
self.drawPage(at: pdfPage, in: printRect)
}
UIGraphicsEndPDFContext();
return pdfData
}
}
It will save PDF file in the directory and returns pdf file path.
AnderCover
Developer since 2011 iOS Developer since 2011 Switched to swift but still <3 Objective-C
Updated on June 20, 2022Comments
-
AnderCover almost 2 years
-(void)createPDFfromUIView:(UIView*)aView saveToDocumentsWithFileName:(NSString*)aFilename { // Creates a mutable data object for updating with binary data, like a byte array UIWebView *webView = (UIWebView*)aView; NSString *heightStr = [webView stringByEvaluatingJavaScriptFromString:@"document.body.scrollHeight;"]; int height = [heightStr intValue]; // Get the number of pages needed to print. 9 * 72 = 648 int pages = ceil(height / 648.0); NSMutableData *pdfData = [NSMutableData data]; UIGraphicsBeginPDFContextToData( pdfData, CGRectZero, nil ); CGRect frame = [webView frame]; for (int i = 0; i < pages; i++) { // Check to see if page draws more than the height of the UIWebView if ((i+1) * 648 > height) { CGRect f = [webView frame]; f.size.height -= (((i+1) * 648.0) - height); [webView setFrame: f]; } UIGraphicsBeginPDFPage(); CGContextRef currentContext = UIGraphicsGetCurrentContext(); CGContextTranslateCTM(currentContext, 72, 72); // Translate for 1" margins [[[webView subviews] lastObject] setContentOffset:CGPointMake(0, 648 * i) animated:NO]; [webView.layer renderInContext:currentContext]; } UIGraphicsEndPDFContext(); // Retrieves the document directories from the iOS device NSArray* documentDirectories = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask,YES); NSString* documentDirectory = [documentDirectories objectAtIndex:0]; NSString* documentDirectoryFilename = [documentDirectory stringByAppendingPathComponent:aFilename]; // instructs the mutable data object to write its context to a file on disk [pdfData writeToFile:documentDirectoryFilename atomically:YES]; [webView setFrame:frame]; NSLog(@"documentDirectoryFileName: %@",documentDirectoryFilename); }
I'm using the code above to generate a pdf file from a webview. It's working, however, the content is not cropped correctly : the content on the bottom of the pages get messed up. I think I could do something better using Core Graphics methods but I can't find how to do that. Any ideas ?
-
AnderCover about 11 yearsIt's worse, I'm on iPad though so I think that the hardcoded 648 value is wrong. Un fortunately when I use the screen height my pdf document doesn't even start at the beginning of my webview
-
user-44651 about 11 yearsWhat was your reasoning for using 648 then?
-
user-44651 about 11 yearsNSMutableData *pdfData = [NSMutableData data]; UIGraphicsBeginPDFContextToData(pdfData, webView.bounds, nil); UIGraphicsBeginPDFPage(); CGContextRef pdfContext = UIGraphicsGetCurrentContext(); UIGraphicsBeginPDFContextToData [webView.layer renderInContext:pdfContext]; UIGraphicsEndPDFContext();
-
AnderCover about 11 yearsWell there was no reasoning whatsoever... Don't get met wrong I agree that 648 doesn't fit iPad size, I just didn't pay attention to it.
-
AnderCover about 11 yearsDon't understand your code sample ? what should I do with it ?
-
user-44651 about 11 yearsLook at this line you have UIGraphicsBeginPDFContextToData( pdfData, CGRectZero, nil ); Try removing CGRectZero and replacing it with webView.bounds
-
AnderCover about 11 yearsThank you for your answers Firemarble, it helped me a lot. I managed to get a somewhat good looking results with this method :
-
Tib over 9 yearsGreat job! You saved my day :)
-
AnderCover over 9 yearsHello, it's an old question, but i'll try your solution. It seems better than what I did back then.
-
Michael Kernahan over 9 yearsI wish I had a million upvotes to give you. Fantastic.
-
santhosh about 9 yearsHere i can render the pdf from uiwebview. But i can't render the pdf with the subviews in uiwebview. i am facing this problem using the above code.
-
MobileMon about 8 yearsThe orientation is landscape, how to convert to portrait?
-
Shin622 almost 7 yearsexcuse me, I have used your function to create a pdf from webview and it working perfectly, I added a UILabel to webView.scrollView for display some text on PDF. Do you know how can I create PDF with the extra text ?