iOS create pdf from UIWebview content
There isn't a method that allows this directly via the SDK like there is on Mac however you may wish to take a look at BNHtmlPdfKit which allows you to save the contents of URLs, web views and also html strings as PDFs.
For example, as follows:
self.htmlPdfKit = [BNHtmlPdfKit saveUrlAsPdf:[NSURL URLWithString:@"http://itsbrent.net"] toFile:@"...itsbrent.pdf" pageSize:BNPageSizeA6 success:^(NSString *pdfFileName) {
NSLog(@"Done");
} failure:^(NSError *err) {
NSLog(@"Failure");
}];
It makes use of a custom UIPrintPageRenderer
which overrides paperRect
and printableRect
thus causing the UIPrintFormatter
to return a pageCount as well as render the document.
Creating PDF file from UIWebView
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");
}
}
Download PDF via UIWebView
-initWithContentsOfURL:
method of NSData
does a simple HTTP GET, but you needed to set some Authorization params that's why it fails.
To avoid downloading twice the data, you could use NSURLSession
to download it, save it, and use the -loadData:MIMEType:textEncodingName:baseURL:
of UIWebView
to load it.
NSMutableURLRequest *mutableRequest = //Your Custom request with URL and Headers
[[[NSURLSession sharedSession] dataTaskWithRequest:mutableRequest completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error)
{
if (data)
{
//SaveDataIntoDisk
dispatch_async(dispatch_get_main_queue(), ^(){
[_webView loadData:data MIMEType:@"application/pdf" textEncodingName:@"UTF-8" baseURL:nil];
});
}
}] resume];
Download PDF, saving to document directory and loading it in UIWebView
I didn't look through all of the code but the following two lines are a problem:
var filePath = URL(fileURLWithPath: documentsDirectory).appendingPathComponent("my.pdf").absoluteString
var targetURL = URL(fileURLWithPath: filePath)
The value of URL absoluteString
does not give you a file path so the value of filePath
is not a valid value for the URL fileURLWithPath:
initializer.
And what's the point of going from URL
to String
(as a path) and back to a URL
? Simply combine those two lines into:
var targetURL = URL(fileURLWithPath: documentsDirectory).appendingPathComponent("my.pdf")
As a side note, use some consistency. In other code you get the Documents folder URL using:
let docURL = (FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).last)
and in other code you use:
var paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
var documentsDirectory = paths[0]
var ... = URL(fileURLWithPath: documentsDirectory)...
Pick one approach and use it consistently. Since you need a URL
, use the first approach. This means the code I suggested should now be:
let docURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).last!
let targetURL = docURL.appendingPathComponent("my.pdf")
ios - create and load pdf in webview
UIWebView *webView = [[UIWebView alloc] initWithFrame:CGRectMake(0,0,screenSize.width,screenSize.height)];
I don't see anywhere where you actually add the UIWebView
as a subView
.
[self.view addSubview:webView];
Also. you should add the UIWebView
to a property
instead of allocating/creating it everytime someone presses a button.
@property (strong, nonatomic) UIWebView *webView;
Finally:
You should use a WKWebView instead of an UIWebView
as mentioned in Apple Documentations:
Starting in iOS 8.0 and OS X 10.10, use WKWebView to add web content
to your app. Do not use UIWebView or WebView.
EDIT:
For people reading this who actually runs in to the problem as the question actually implies check these other threads on the same question:
Load text+pdf file in uiwebview
iPhone : can we open pdf file using UIWebView?
how to load pdf file in UIWebview center
Display local pdf file in UIWebView in iphone
Loading Local PDF File Into WebView
Objective-C Issues with UIWebView to PDF
I've done this in the past using UIPrintPageRenderer. It's a more versetile way of creating a PDF from a UIWebView, and it's been working well for me so far. I've tested this solution with Xcode 6 and iOS 8.2. Also, tried printing the resulting PDF and everything printed out fine.
When I read the OP, I did some testing with various page sizes, to see if I can get a blank PDF. There are a few key items that I identified, that could contribute to a blank PDF file. I've identified them in the code.
When webViewDidFinishLoad() gets called, the view might not be 100% loaded. A check is necessary, to see if the view is still loading. This is important, as it might be the source of your problem. If it's not, then we are good to go. There is a very important note here. Some web pages are loaded dynamically (defined in the page itself). Take youtube.com for example. The page displays almost immediately, with a "loading" screen. This will trick our web view, and it's "isLoading" property will be set to "false", while the web page is still loading content dynamically. This is a pretty rare case though, and in the general case this solution will work well. If you need to generate a PDF file from such a dynamic loading web page, you might need to move the actual generation to a different spot. Even with a dynamic loading web page, you will end up with a PDF showing the loading screen, and not an empty PDF file.
Another key aspect is setting the printableRect and pageRect. Note that those are set separately. If the printableRect is smaller than the paperRect, you will end up with some padding around the content - see code for example. Here is a link to Apple's API doc with some short descriptions for both.
The example code below adds a Category to UIPrintPageRenderer to create the actual PDF data. The code in this sample has been put together using various resources online in the past, and I wasn't able to find which ones were used to credit them properly.
@interface UIPrintPageRenderer (PDF)
- (NSData*) createPDF;
@end
@implementation UIPrintPageRenderer (PDF)
- (NSData*) createPDF
{
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
And here is what I have in webViewDidFinishLoad()
- (void)webViewDidFinishLoad:(UIWebView *)webViewIn {
NSLog(@"web view did finish loading");
// webViewDidFinishLoad() could get called multiple times before
// the page is 100% loaded. That's why we check if the page is still loading
if (webViewIn.isLoading)
return;
UIPrintPageRenderer *render = [[UIPrintPageRenderer alloc] init];
[render addPrintFormatter:webViewIn.viewPrintFormatter startingAtPageAtIndex:0];
// Padding is desirable, but optional
float padding = 10.0f;
// Define the printableRect and paperRect
// If the printableRect defines the printable area of the page
CGRect paperRect = CGRectMake(0, 0, PDFSize.width, PDFSize.height);
CGRect printableRect = CGRectMake(padding, padding, PDFSize.width-(padding * 2), PDFSize.height-(padding * 2));
[render setValue:[NSValue valueWithCGRect:paperRect] forKey:@"paperRect"];
[render setValue:[NSValue valueWithCGRect:printableRect] forKey:@"printableRect"];
// Call the printToPDF helper method that will do the actual PDF creation using values set above
NSData *pdfData = [render createPDF];
// Save the PDF to a file, if creating one is successful
if (pdfData) {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *path = [paths objectAtIndex:0];
NSString *pdfPath = [path stringByAppendingPathComponent:[NSString stringWithFormat:@"Purchase Order.pdf"]];
[pdfData writeToFile:pdfPath atomically:YES];
}
else
{
NSLog(@"error creating PDF");
}
}
PDFSize is defined as a constant, set to a standard A4 page size. It can be edited to meet your needs.
#define PDFSize CGSizeMake(595.2,841.8)
Related Topics
How to Change Inactive Icon/Text Color on Tab Bar
How to Fix the Xcrun Unable to Find Simctl Error
How to Get the Udid in iOS 6 and iOS 7
Run Timer in 'Background' When App Is Closed
Keeping a Uibutton Selected After a Touch
Autoshrink on a Uilabel with Multiple Lines
How to Create a Uilabel with Strikethrough Text
Why Xcode 7 Shows *.Tbd Instead of *.Dylib
What Should Xcode 6 Gitignore File Include
How to Remove Provisioning Profiles from Xcode
How to Read Data Structure from .Plist File into Nsarray
What's the Difference Between "Architectures" and "Valid Architectures" in Xcode Build Settings
Uitableview Within Uiscrollview Using Autolayout