Using UIWebView in memory to create a PDF file in PhoneGap

I am trying to figure out how to do this.

NOTE. I am not an experienced objective-c developer (hence why I use PhoneGap in the first place).

Disadvantage : my UIWebView (no, not PhoneGap, which displays webapp, the second UIWebView, created in memory and not visible) does not turn into PDF. I just get a blank PDF. I will post some of my opinions and code, and hopefully someone finds out what I'm doing wrong.


My starting point is that PhoneGap already has a print plugin:

https://github.com/phonegap/phonegap-plugins/tree/master/iPhone/PrintPlugin

This plugin creates a UIWebView on the fly, you pass it HTML code through JavaScript, and then it calls some print controller to print. A.

So, I borrowed some ideas. Then I noticed this wonderful blog post about creating PDF

http://www.ioslearner.com/convert-html-uiwebview-pdf-iphone-ipad/

So, I'm trying to combine the two in my own PhoneGap plugin to get some HTML (from my webapp) and create PDF on the fly.

HEADER:

#import <Foundation/Foundation.h> #import <QuartzCore/QuartzCore.h> #ifdef PHONEGAP_FRAMEWORK #import <PhoneGap/PGPlugin.h> #else #import "PGPlugin.h" #endif @interface ExportPlugin : PGPlugin <UIWebViewDelegate> { NSString* exportHTML; } @property (nonatomic, copy) NSString* exportHTML; //This gets called from my HTML5 app (Javascript): - (void) exportPdf:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options; @end 

MAIN:

 #import "ExportPlugin.h" @interface ExportPlugin (Private) -(void) doExport; -(void) drawPdf; @end @implementation ExportPlugin @synthesize exportHTML; - (void) exportPdf:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options{ NSUInteger argc = [arguments count]; if (argc < 1) { return; } self.exportHTML = [arguments objectAtIndex:0]; [self doExport]; } int imageName = 0; double webViewHeight = 0.0; - (void) doExport{ //Set the base URL to be the www directory. NSString *dbFilePath = [[NSBundle mainBundle] pathForResource:@"www" ofType:nil ]; NSURL *baseURL = [NSURL fileURLWithPath:dbFilePath]; //Load custom html into a webview UIWebView *webViewExport = [[UIWebView alloc] init]; webViewExport.delegate = self; //[webViewExport loadHTMLString:exportHTML baseURL:baseURL]; [webViewExport loadHTMLString:@"<html><body><h1>testing</h1></body></html>" baseURL:baseURL]; } - (BOOL)webView:(UIWebView *)theWebView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType { return YES; } - (void)webViewDidFinishLoad:(UIWebView *)webViewExport { webViewHeight = [[webViewExport stringByEvaluatingJavaScriptFromString:@"document.body.scrollHeight;"] integerValue]; CGRect screenRect = webViewExport.frame; //WHY DO I HAVE TO SET THE SIZE? OTHERWISE IT IS 0 screenRect.size.width = 768; screenRect.size.height = 1024; double currentWebViewHeight = webViewHeight; while (currentWebViewHeight > 0) { imageName ++; UIGraphicsBeginImageContext(screenRect.size); CGContextRef ctx = UIGraphicsGetCurrentContext(); //[[UIColor blackColor] set]; //CGContextFillRect(ctx, screenRect); [webViewExport.layer renderInContext:ctx]; UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *documentsDirectory = [paths objectAtIndex:0]; NSString *pngPath = [documentsDirectory stringByAppendingPathComponent:[NSString stringWithFormat:@"%d.png",imageName]]; if(currentWebViewHeight < 960) { CGRect lastImageRect = CGRectMake(0, 960 - currentWebViewHeight, webViewExport.frame.size.width, currentWebViewHeight); CGImageRef imageRef = CGImageCreateWithImageInRect([newImage CGImage], lastImageRect); newImage = [UIImage imageWithCGImage:imageRef]; CGImageRelease(imageRef); } [UIImagePNGRepresentation(newImage) writeToFile:pngPath atomically:YES]; [webViewExport stringByEvaluatingJavaScriptFromString:@"window.scrollBy(0,960);"]; currentWebViewHeight -= 960; } [self drawPdf]; } - (void) drawPdf { CGSize pageSize = CGSizeMake(612, webViewHeight); NSString *fileName = @"Demo.pdf"; NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *documentsDirectory = [paths objectAtIndex:0]; NSString *pdfFileName = [documentsDirectory stringByAppendingPathComponent:fileName]; UIGraphicsBeginPDFContextToFile(pdfFileName, CGRectZero, nil); // Mark the beginning of a new page. UIGraphicsBeginPDFPageWithInfo(CGRectMake(0, 0, pageSize.width, pageSize.height), nil); double currentHeight = 0.0; for (int index = 1; index <= imageName ; index++) { NSString *pngPath = [documentsDirectory stringByAppendingPathComponent:[NSString stringWithFormat:@"%d.png", index]]; UIImage *pngImage = [UIImage imageWithContentsOfFile:pngPath]; [pngImage drawInRect:CGRectMake(0, currentHeight, pageSize.width, pngImage.size.height)]; currentHeight += pngImage.size.height; } UIGraphicsEndPDFContext(); } @end 

The first indication, something is wrong, above, I have to set the size of UIWebView.frame:

 screenRect.size.width = 768; screenRect.size.height = 1024; 

But why? PhoneGap PrintPlugin should not do this. If I do not set it, the size is 0, and then I get a lot of contextual errors.

And then the next problem is that the UIWebView does nothing. Symptom of the first problem can be?

How can I debug this problem and solve the problem?


UPDATE

I am sure that it is not possible to display a UIWebView layer in the context of an image unless that UIWebView is displayed.

I'm not sure how PhoneGap PrintPlugin works. It seems that UIWebView shows very well that it is not displayed.

I'm currently experimenting with providing the actual PhoneGap UIWebView in PDF (as opposed to my own UIWebView). But this is not perfect.

  • This means that I need to hide all the toolbars and much more, and then pan the UIWebView so that I capture everything outside the viewport. This is not ideal because the user will see it visually!

  • Point 1 above does not seem to work, because the iPad is too slow to refresh the screen when playing the layout dynamically. On an iPad, if you do visual effects very fast (like panning the screen), the iPad is too slow and just doesn't show it. You see only the final state. Therefore, when I take screenshots, the screen does not visually light up (although the DOM says it is). (Hope this makes sense).

Ah, disappointment.

+4
source share
3 answers

Now I have a working solution, but it is not perfect.

What I'm doing is a UIWebView dialog box in PDF format.

This is quite difficult to do. I have a couple of objective-c functions

 - (void) takeScreenshot; - (void) renderPdf; 

which I call from Javascript.

Then I need to write a recursive JS algorithm that rotates the screen in all directions when calling takeScreenshot .

In between takeScreenshot calls takeScreenshot I use setTimeout , which gives a 20-millisecond gap in JS processing - enough time for the iPad to refresh the screen so that the next screenshot can be taken.

It was a royal pain in the ass. The bounty is still open, if anyone knows of a better way to handle this - I would be very interested to know!

+1
source

If you want to display the UIWebView in PDF, I think you could go for this:

1 / use the convertRect: fromView method implemented by your UIWebView object to get a CGRect

2 / cm. Link to the UIPrintPageRenderer class to do as print preview

3 / Use UIGraphicsGetCurrentContext to infer CGContextRef from it

4 / create a PDF from CGRect and CGContextRef (you can use the help provided in Apple's zoomingPDFViewer sample code to create a PDF using CGPdf

0
source

Here's how to make a PDF in UIWebView (the webpage being your UIWebView), your delegate (here's the "me") can implement the UIWebViewDelegate protocol:

 - (void)loadingPDFwithURL:(NSURL *)anURL { CGRect appFrame = [[UIScreen mainScreen] applicationFrame]; appFrame.origin.y = 0; [self.webPage initWithFrame:appFrame]; [self.webPage setScalesPageToFit:YES]; [self.webPage setDelegate:self]; NSURLRequest *requestObj = [NSURLRequest requestWithURL:anURL]; [self.webPage loadRequest:requestObj]; [self.view addSubview:self.webPage]; } 

So, you specify the PDF URL (if it is in memory, just write the file in the application, as the URL can be a file).

Another possibility is to immerse yourself in CGPdf, but it is becoming increasingly difficult.

-one
source

Source: https://habr.com/ru/post/1395099/


All Articles