【问题标题】:Why does NSPrintOperation create an empty PDF when I try to save a WebView to a PDF?当我尝试将 WebView 保存为 PDF 时,为什么 NSPrintOperation 会创建一个空的 PDF?
【发布时间】:2014-10-07 07:03:48
【问题描述】:

我正在尝试在 OS X 上将 HTML 转换为 PDF 文件。经过大量挖掘,我发现最好的方法是使用 NSPrintOperation。这是我用来将 HTML 字符串转换为 PDF 的代码:

WebView *webview = [[WebView alloc] init];
[webview.mainFrame loadHTMLString:htmlString baseURL:nil];

NSDictionary *printOpts = @{
                            NSPrintJobDisposition: NSPrintSaveJob,
                            NSPrintSavePath: outputFilePath
                            };
NSPrintInfo *printInfo = [[NSPrintInfo alloc] initWithDictionary:printOpts];
printInfo.horizontalPagination = NSAutoPagination;
printInfo.verticalPagination = NSAutoPagination;
NSPrintOperation *printOp = [NSPrintOperation 
                             printOperationWithView:webview.mainFrame.frameView.documentView
                                          printInfo:printInfo];
printOp.showsPrintPanel = NO;
printOp.showsProgressPanel = NO;

[printOp runOperation];

此代码确实生成一个 PDF 文件,但 PDF 文件为空。是因为WebView 没有框架吗?我尝试过使用dataWithPDFInsideRect:,但这也不起作用。

我应该用 A4 纸大小的框架初始化 webview 吗?还是其他地方有问题?

【问题讨论】:

    标签: html objective-c cocoa pdf webview


    【解决方案1】:

    我知道这是一个旧线程,但我想发布我今天提出的解决方案。由于 WebView 是异步加载的,因此关键是设置一个帧加载委托回调,然后在运行循环中运行,直到帧加载完成。

    我下面的代码创建了一个新的 WebView,然后将一个 HTML 文件加载到其中:

    // Clear the loaded flag
    _webViewIsLoaded = NO;
    
    // Fetch the default paper size and init print settings
    NSPrintInfo * sharedInfo = [NSPrintInfo sharedPrintInfo];
    NSMutableDictionary * sharedDict = [sharedInfo dictionary];
    NSMutableDictionary * printInfoDict = [NSMutableDictionary dictionaryWithDictionary:sharedDict];
    NSRect printRect = NSZeroRect;
    printRect.size = [sharedInfo paperSize];
    
    // Create a new web view
    WebView * printView = [[WebView alloc] initWithFrame:printRect];
    [printView setFrameLoadDelegate:self];
    
    // Configure the WebView
    WebPreferences * preferences = [[WebPreferences alloc] initWithIdentifier:@"com.id.here"];
    [preferences setShouldPrintBackgrounds:YES];
    [preferences setAllowsAnimatedImageLooping:NO];
    [preferences setJavaScriptCanOpenWindowsAutomatically:NO];
    [preferences setAutosaves:NO];
    [preferences setJavaEnabled:NO];
    [preferences setJavaScriptEnabled:NO];
    [preferences setCacheModel:WebCacheModelDocumentViewer];        // most memory-efficient setting
    [preferences setPlugInsEnabled:NO];                             // disable plugins
    [printView setPreferences:preferences];
    
    // Load the HTML file
    NSURL * url = [NSURL fileURLWithPath:htmlPath];
    NSURLRequest * request = [NSURLRequest requestWithURL:url];
    [[printView mainFrame] loadRequest:request];
    
    [printInfoDict setObject:NSPrintSaveJob
                      forKey:NSPrintJobDisposition];
    [printInfoDict setObject:saveUrl forKey:NSPrintJobSavingURL];
    
    NSPrintInfo * printInfo = [[NSPrintInfo alloc] initWithDictionary:printInfoDict];
    [printInfo setHorizontalPagination:NSFitPagination];
    [printInfo setVerticalPagination:NSAutoPagination];
    [printInfo setVerticallyCentered:NO];
    [printInfo setHorizontallyCentered:YES];
    
    // Wait for the frame to finish loading
    while(!_webViewIsLoaded)
    {
        @autoreleasepool
        {
            [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.005]];
        }
    }
    
    NSPrintOperation * printOp = [NSPrintOperation printOperationWithView:[[[printView mainFrame] frameView] documentView]
                                                                printInfo:printInfo];
    [printOp setShowsProgressPanel:YES];
    [printOp setShowsPrintPanel:NO];
    [printOp runOperation];
    

    接下来,我的框架加载委托方法会在框架完全加载后设置一个标志,并调整框架的高度以匹配内容的高度:

    -(void)webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)webFrame
    {
        _webViewIsLoaded = YES;
    
        NSRect webFrameRect = [[[webFrame frameView] documentView] frame];
        NSRect webViewRect = [webView frame];
    
        // Resize the webview so it fits all of its contents
        NSRect newWebViewRect = NSMakeRect(webViewRect.origin.x,
                                       webViewRect.origin.y - (NSHeight(webFrameRect) - NSHeight(webViewRect)),
                                       NSWidth(webViewRect),
                                       NSHeight(webFrameRect));
        [webView setFrame:newWebViewRect];
    }
    

    这应该适用于将 HTML 文档保存为 PDF。

    【讨论】:

    • 您是否也可以将NSPrintOperation 初始化和执行放在didFinishLoadForFrame 委托中,而不是设置NSRunLoop
    • 顺便说一句,这非常有帮助。谢谢! WKWebView 不适用于 macOS 上的打印(据我所知),因此使用旧的 WebView 是您所展示的唯一选项。
    【解决方案2】:

    试试这个:

     NSPrintOperation *printOp = [NSPrintOperation
                                 printOperationWithView:[[[webview mainFrame] frameView] documentView] printInfo:printInfo];
    

    代替:

    NSPrintOperation *printOp = [NSPrintOperation printOperationWithView:webview.mainFrame.frameView.documentView
                                 printInfo:printInfo];
    

    这应该可行:)

    编辑: 这是我使用的完整代码

    NSString *htmlString = @"<html><head></head><body><h1>Hello, World</h1></body></html>";
    
    WebView *webview = [[WebView alloc] init];
    [webview.mainFrame loadHTMLString:htmlString baseURL:nil];
    
    NSString* outputFilePath = @"/Users/YOUR-USERNAME/Documents/MyAwesomePDF.pdf";
    NSURL *url = [[NSURL alloc] initWithString:outputFilePath];
    
    NSDictionary *printOpts = @{
                                NSPrintJobDisposition: NSPrintSaveJob,
                                NSPrintSaveJob: url
                                };
    NSPrintInfo *printInfo = [[NSPrintInfo alloc] initWithDictionary:printOpts];
    printInfo.horizontalPagination = NSAutoPagination;
    printInfo.verticalPagination = NSAutoPagination;
    
    
    
    NSPrintOperation *printOp = [NSPrintOperation
                                 printOperationWithView:[[[webview mainFrame] frameView] documentView] printInfo:printInfo];
    printOp.showsPrintPanel = NO;
    printOp.showsProgressPanel = NO;
    
    [printOp runOperation];
    

    【讨论】:

    • 点语法不只是这种传统 getter 语法的简写吗?此外,如果点语法不起作用,我希望看到一个错误,但我的代码构建成功。
    • 检查我的编辑,我忘了提到 NSPrintSavePath 自 10.6 以来已被弃用,所以这可能是另一个原因。如果在获取 documentView 时使用点表示法,我也会遇到错误
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-07
    • 1970-01-01
    相关资源
    最近更新 更多