【问题标题】:Creating PDF file from UIWebView从 UIWebView 创建 PDF 文件
【发布时间】:2013-03-26 15:12:13
【问题描述】:
 -(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);
}

我正在使用上面的代码从 webview 生成一个 pdf 文件。它工作正常,但是内容没有正确裁剪:页面底部的内容被弄乱了。 我认为我可以使用 Core Graphics 方法做得更好,但我找不到如何做到这一点。有什么想法吗?

【问题讨论】:

    标签: ios pdf uiwebview core-graphics crop


    【解决方案1】:

    使用UIWebView 中的UIPrintPageRenderer 按照以下步骤操作:

    添加CategoryUIPrintPageRenderer 以获取PDF 数据

    @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
    

    为 A4 尺寸添加这些定义

    #define kPaperSizeA4 CGSizeMake(595.2,841.8)
    

    现在在UIWebView's webViewDidFinishLoad delegate 使用UIPrintPageRenderer propertyUIWebView

    - (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");
      }
    }
    

    【讨论】:

    • 您好,这是一个老问题,但我会尝试您的解决方案。这似乎比我当时做的更好。
    • 我希望我有一百万票可以给你。太棒了。
    • 在这里我可以从 uiwebview 渲染 pdf。但我无法使用 uiwebview 中的子视图呈现 pdf。我使用上面的代码遇到了这个问题。
    • 方向为横向,如何转为纵向?
    【解决方案2】:

    感谢您对 Firemarble 的回答,它对我帮助很大。 我设法用这种方法得到了一些好看的结果:

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

    【讨论】:

    • 干得好!你拯救了我的一天:)
    【解决方案3】:

    Swift 2.0 解决方案,如何将 pdf 从 webview 保存到 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()
    }
    

    您还需要将 UIWebViewDelegate 添加到您的 ViewController 和 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;
        }
    }
    

    【讨论】:

    • 对不起,我已经使用你的函数从 webview 创建了一个 pdf 并且它运行良好,我在 webView.scrollView 添加了一个 UILabel 以在 PDF 上显示一些文本。你知道如何创建带有额外文本的 PDF 吗?
    【解决方案4】:

    @Dan Loewenherz 回答的 Swift 3 解决方案:

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

    【讨论】:

      【解决方案5】:

      UIWebView 现已弃用。使用 WKWebView 扩展从 WKWebView 创建 PDF

      信用:http://www.swiftdevcenter.com/create-pdf-from-uiview-wkwebview-and-uitableview/

      斯威夫特 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
          }
      }
      

      它将PDF文件保存在目录中并返回pdf文件路径。

      【讨论】:

        【解决方案6】:

        看起来问题可能来自硬编码的高度 648。如果将 648 的所有实例更改为调用窗口 screenHeight 会发生什么?

        //抓取窗口的宽度和高度。

        CGRect screenRect = [[UIScreen mainScreen] bounds];
        CGFloat screenWidth = screenRect.size.width;
        CGFloat screenHeight = screenRect.size.height;
        

        【讨论】:

        • 更糟糕的是,我在 iPad 上,所以我认为硬编码的 648 值是错误的。不幸的是,当我使用屏幕高度时,我的 pdf 文档甚至没有从 webview 的开头开始
        • 当时您使用 648 的原因是什么?
        • NSMutableData *pdfData = [NSMutableData 数据]; UIGraphicsBeginPDFContextToData(pdfData, webView.bounds, nil); UIGraphicsBeginPDFPage(); CGContextRef pdfContext = UIGraphicsGetCurrentContext(); UIGraphicsBeginPDFContextToData [webView.layer renderInContext:pdfContext]; UIGraphicsEndPDFContext();
        • 好吧,没有任何理由......不要误会我同意648不适合iPad尺寸,我只是没有注意它。
        • 看看这一行你有 UIGraphicsBeginPDFContextToData( pdfData, CGRectZero, nil );尝试删除 CGRectZero 并用 webView.bounds 替换它
        猜你喜欢
        • 1970-01-01
        • 2015-08-08
        • 2017-11-03
        • 2011-10-08
        • 1970-01-01
        • 2015-06-16
        • 2011-02-03
        • 2015-01-29
        • 2017-05-22
        相关资源
        最近更新 更多