使用内存中的UIWebView在PhoneGap中生成PDF

时间:2012-02-06 21:35:26

标签: objective-c uiwebview cordova pdf-generation

我正试图弄清楚如何做到这一点。

注意:我不是一位经验丰富的Objective-c开发人员(因此我首先使用的是PhoneGap)

缺点:我的UIWebView(不,不是呈现webapp的PhoneGap,第二个在内存中创建且不可见的UIWebView)不会呈现到PDF中。我只是得到一个空白的PDF。我会发布一些思考和代码,希望有人会知道我做错了什么。


我的首发地点是PhoneGap已有一个打印插件:

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

此插件即时创建UIWebView,您通过JavaScript将一些HTML传递给它,然后调用一些打印控制器进行打印。

所以我从中借鉴了一些想法。然后我注意到这篇关于生成PDF的博客文章

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

因此,我正在尝试将这两者合并到我自己的PhoneGap插件中,以获取一些HTML(来自我的webapp)并动态生成PDF。

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

第一个指示是不对的,上面我要设置UIWebView.frame大小:

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

但为什么呢? PhoneGap PrintPlugin不必这样做。如果我没有设置它,大小为0,然后我得到很多上下文错误。

接下来的问题是UIWebView没有呈现任何内容。也许是第一个问题的症状?

如何调试此问题并解决问题所在?


更新

我很确定将UIWebView层渲染到图像上下文中是不可能的,除非UIWebView实际上是可见的。

我不确定PhoneGap PrintPlugin是如何工作的。似乎它的UIWebView很好,因为它不可见。

我目前正在尝试将实际的PhoneGap UIWebView渲染到PDF中(而不是我自己的UIWebView)。但这并不理想。

  • 这意味着我必须隐藏所有工具栏和诸如此类的内容,然后 pan UIWebView,以便捕获视口外的所有内容。这并不理想,因为用户会直观地看到这种情况发生!

  • 上面的第1点似乎无法正常工作,因为在动态摆弄布局时,iPad太慢而无法更新屏幕。在iPad上,如果你非常快速地进行视觉操作(比如平移屏幕),iPad太慢而且不会显示它。你最终只看到了最终状态。因此,当我拍摄截图时,屏幕在视觉上没有平移(即使DOM说它有)。 (希望这很有意义)。

唉,令人沮丧。

3 个答案:

答案 0 :(得分:1)

我现在有了一个有效的解决方案,但这并不理想。

我所做的是将手机屏幕UIWebView呈现为PDF。

要做到这一点非常棘手。我有几个Objective-c函数

- (void) takeScreenshot;

- (void) renderPdf;

我从Javascript调用。

然后我必须编写一个递归的JS算法,在调用takeScreenshot时向每个方向平移屏幕。

在对takeScreenshot的调用之间,我使用setTimeout,这使得JS处理中断了20毫秒 - 足够的时间让iPad更新屏幕,以便可以拍摄下一个屏幕截图。

这是屁股中的皇室痛苦。赏金仍然是开放的,以防有人知道更好的处理方法 - 我很想知道!

答案 1 :(得分:0)

如果你想将UIWebView呈现为PDF,我想你可以这样做:

1 /使用由UIWebView对象实现的convertRect:fromView方法来获取CGRect

2 /看UIPrintPageRenderer Class Reference制作打印预览

3 /使用UIGraphicsGetCurrentContext从中获取CGContextRef

4 /从CGRect和CGContextRef创建PDF(您可以使用Apple sample code zoomingPDFViewer中提供的帮助使用CGPdf构建PDF

答案 2 :(得分:-1)

以下是如何在UIWebView中呈现PDF(webPage是您的UIWebView),您的委托(此处&#34; self&#34;)可以实现UIWebViewDelegate协议:

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

所以你要提供PDF的URL(如果它在内存中,只需在应用程序中写入文件,因为URL可以是文件路径)。

另一种可能性是潜入CGPdf,但它变得越来越难。