【问题标题】:How do I get a multi-page pdf from a website using jsPDF and HTML2Canvas?如何使用 jsPDF 和 HTML2Canvas 从网站获取多页 pdf?
【发布时间】:2015-01-18 16:11:38
【问题描述】:

我有一个脚本,它使用 HTML2Canvas 在页面内截取div 的屏幕截图,然后使用 jsPDF 将其转换为 pdf。

问题是生成的pdf只有一页,而截图在某些情况下需要多页。例如,屏幕截图大于 8.5x11。宽度很好,但我需要它来创建多个页面以适应整个屏幕截图。

这是我的脚本:

var pdf = new jsPDF('portrait', 'pt', 'letter');
$('.export').click(function() {
      pdf.addHTML($('.profile-expand')[0], function () {
           pdf.save('bfc-schedule.pdf');
      });
 });

有什么想法可以修改它以允许多个页面吗?

【问题讨论】:

    标签: javascript jquery html2canvas jspdf


    【解决方案1】:

    找到了解决方案,将一个矩形作为每个 pdf 页面的边框,并通过改变 pageHeight 从一点点开始第二页和其他页面,希望这将解决少数...

    html2canvas(myCanvas).then(function (canvas) {
    
          var imgWidth = 210;
          var pageHeight = 290;
          var imgHeight = canvas.height * imgWidth / canvas.width;
          var heightLeft = imgHeight;
    
    
          var doc = new jsPDF('p', 'mm');
          var position = 0;
          var pageData = canvas.toDataURL('image/jpeg', 1.0);
          var imgData = encodeURIComponent(pageData);
          doc.addImage(imgData, 'PNG', 0, position, imgWidth, imgHeight);
          doc.setLineWidth(5);
          doc.setDrawColor(255, 255, 255);
          doc.rect(0, 0, 210, 295);
          heightLeft -= pageHeight;
    
          while (heightLeft >= 0) {
            position = heightLeft - imgHeight;
            doc.addPage();
            doc.addImage(imgData, 'PNG', 0, position, imgWidth, imgHeight);
            doc.setLineWidth(5);
            doc.setDrawColor(255, 255, 255);
            doc.rect(0, 0, 210, 295);
            heightLeft -= pageHeight;
          }
          doc.save('file.pdf');
    
        });
      };
    

    【讨论】:

      【解决方案2】:

      这是我的方法,还有 jspdf 和 html2canvas,这对我来说做得很好:

      我正在使用一个包装器节点来保存要转换为 pdf 的内容:

      <div id="main-content">
          <!-- the content goes here -->
      </div>
      <!-- and a button somewhere in the dom -->
       <a href="javascript:genPDF()">
           <i class="far fa-file-pdf"></i> &nbsp; Download PDF
       </a>
      

      这是 JS 集成(我只检查了这个版本)并调用:

      <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/1.3.3/jspdf.min.js"></script>
      <script src="https://html2canvas.hertzen.com/dist/html2canvas.js"></script>
      <script type="text/javascript">
      
          const documentSlug = "some Slug";
          const documentTitle ="Some Title";
      
          function genPDF(){
              let html2pdf = new pdfView();
              html2pdf.generate();
          }
      </script>
      

      在这里您可以看到 pdf 生成。我将生成部分封装在一个额外的 js 对象中(您还需要包含此文件):

      //assuming jquery is in use
      
      let pdfView = function(){
          //assuming documentSlug is a constant set earlier 
          this.documentSlug = documentSlug;
          //assuming documentTitle is a constant set earlier
          this.documentTitle = documentTitle;
      
          //in this html node the part which shall be converted to pdf
          this.nodeId = "main-content";
      };
      
      pdfView.prototype.generate = function(){
          let self = this;
          this.prepareDocumentToPrint();
      
          //variables
          let HTML_Width = $("#"+self.nodeId).width();
          let HTML_Height = $("#"+self.nodeId).height();
          let top_left_margin = 15;
          let PDF_Width = HTML_Width+(top_left_margin*2);
          let PDF_Height = (PDF_Width*1.5)+(top_left_margin*2);
      
          this.pdfWidth = PDF_Width;
          this.pdfHeight = PDF_Height;
      
          let canvas_image_width = HTML_Width;
          let canvas_image_height = HTML_Height;
      
          let totalPDFPages = Math.ceil(HTML_Height/PDF_Height)-1;
      
          html2canvas($("#"+self.nodeId)[0],{allowTaint:true, scale: 2}).then(function(canvas) {
              canvas.getContext('2d');
      
              //console.log(canvas.height+"  "+canvas.width);
      
              let imgData = canvas.toDataURL("image/jpeg", 1.0);
              let pdf = new jsPDF('p', 'mm',  [PDF_Width, PDF_Height]);
              pdf.addImage(imgData, 'JPG', top_left_margin, top_left_margin,canvas_image_width,canvas_image_height);
      
              pdf = self.addPdfHeader(pdf);
              pdf = self.addPdfFooter(pdf, 1);
      
              for (let i = 1; i <= totalPDFPages; i++) {
                  pdf.addPage(PDF_Width, PDF_Height);
      
                  pdf.addImage(imgData, 'JPG', top_left_margin, -(PDF_Height*i)+(top_left_margin*4) + top_left_margin,canvas_image_width,canvas_image_height);
      
                  pdf = self.addPdfHeader(pdf);
                  pdf = self.addPdfFooter(pdf, (i+1));
              }
      
              pdf.save(self.documentSlug+".pdf");
      
              self.resetDocumentAfterPrint();
      
          });
      };
      
      //this one sets a fixed viewport, to be able to 
      //get the same pdf also on a mobile device. I also
      //have a css file, which holds the rules for the 
      //document if the body has the .printview class added
      pdfView.prototype.prepareDocumentToPrint = function(){
          let viewport = document.querySelector("meta[name=viewport]");
          viewport.setAttribute('content', 'width=1280, initial-scale=0.1');
          $('body').addClass("printview");
          window.scrollTo(0,0);
      };
      
      pdfView.prototype.addPdfHeader = function(pdf){
      
          pdf.setFillColor(255,255,255);
          pdf.rect(0, 0, this.pdfWidth, 40, "F");
      
          pdf.setFontSize(40);
          pdf.text("Document: "+this.documentTitle+" - Example Lmtd. Inc. (Whatsoever)", 50, 22);
      
          return pdf;
      };
      
      pdfView.prototype.addPdfFooter = function(pdf, pageNumber){
      
          pdf.setFillColor(255,255,255);
          pdf.rect(0, pdf.internal.pageSize.height - 40, this.pdfWidth, this.pdfHeight, "F");
      
          pdf.setFontSize(40);
          pdf.text("Seite "+pageNumber, 50, pdf.internal.pageSize.height - 20);
          return pdf;
      };
      
      //and this one resets the doc back to normal
      pdfView.prototype.resetDocumentAfterPrint = function(){
          $('body').removeClass("printview");
          let viewport = document.querySelector("meta[name=viewport]");
          viewport.setAttribute('content', 'device-width, initial-scale=1, shrink-to-fit=no');
      };
      

      .printview 案例的我的 css 示例:

      body.printview #header,
      body.printview footer
      {
          display: none !important;
      }
      
      body.printview{
          margin-top: 0px !important;
      }
      
      body.printview #main-content{
          margin-top: 0px !important;
      }
      
      body.printview h1{
          margin-top: 40px !important;
      }
      

      享受

      为您提供的链接对@Code Spy 的贡献,这是此 aporach 的基础。

      【讨论】:

      • 感谢@helle 的这一行:let pdf = new jsPDF('p', 'mm', [PDF_Width, PDF_Height]); 拯救了我的一天。
      【解决方案3】:

      我能够使用async 功能完成它。

      (async function loop() {
          var pages = [...]
          for (var i = 0; i < pages.length; i++) {
            await new Promise(function(resolve) {
              html2canvas(pages[i], {scale: 1}).then(function(canvas) {
      
                var img=canvas.toDataURL("image/png");
                doc.addImage(img,'JPEG', 10, 10);
                if ((i+1) == pages.length) {
                  doc.save('menu.pdf');
                } else {
                  doc.addPage();
                }
                resolve();
              });
            });
          }
      })();
      

      【讨论】:

        【解决方案4】:

        如果网页上有 svg 图片,pdf.addHtml 不起作用。 我在这里复制解决方案,基于已经在画布中的图片。

        以下是我发现有效的数字(纸张宽度和高度)。它仍然在页面之间创建了一点重叠部分,但对我来说已经足够了。如果您能从 jsPDF 中找到官方编号,请使用它们。

        var imgData = canvas.toDataURL('image/png');
        var imgWidth = 210; 
        var pageHeight = 295;  
        var imgHeight = canvas.height * imgWidth / canvas.width;
        var heightLeft = imgHeight;
        var doc = new jsPDF('p', 'mm');
        var position = 0;
        
        doc.addImage(imgData, 'PNG', 0, position, imgWidth, imgHeight);
        heightLeft -= pageHeight;
        
        while (heightLeft >= 0) {
          position = heightLeft - imgHeight;
          doc.addPage();
          doc.addImage(imgData, 'PNG', 0, position, imgWidth, imgHeight);
          heightLeft -= pageHeight;
        }
        doc.save( 'file.pdf');`
        

        【讨论】:

        • 如何创建页面的动态宽度和高度?
        • var height = Math.max(body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight)
        • document.body.style.height = ${height}px
        • @ManmeetKhurana 这就是我获得任何页面的完整高度的方式。 1)计算高度 2)设置高度。 html2canvas 然后将它的“画布”高度设置为该值。也可以用宽度来完成,只需将高度更改为宽度即可。
        • 当 PDF 中的页数超过 2 时,我的代码被破坏了。我使用 -pageHeight * 计数器作为位置(在 while 循环中),为我修复了它
        【解决方案5】:

        你可以试试:

        $('#cmd2').click(function () {
        
            var len = 4; //$x(".//body/div/div").length
            var pdf = new jsPDF('p', 'mm','a4');
            var position = 0;
            Hide
            for (let i = 1;i  <= len; i++){
                html2canvas(document.querySelector('#pg'+i),
                    {dpi: 300, // Set to 300 DPI
                    scale: 1 // Adjusts your resolution
                    }).then(canvas => {
                    pdf.addImage(canvas.toDataURL("images/png", 1), 'PNG', 0,position, 210, 295);
        
                    if (i == len){
                        pdf.save('sample-file.pdf');
                    }else{
                        pdf.addPage();
                    }
                });
            }
        });
        

        【讨论】:

        • 你试过了吗?
        • 这绝对行不通,因为 Promise 是异步的,而且这是在 for 循环中。我试过了,还是不行。
        • 这取决于您想要生成文件的时间和地点...但它仍然适用于我的项目。
        • 这不起作用,我只得到第一页。但我想问题出在 html2canvas 函数周围......
        【解决方案6】:

        您可以像这样使用 addhtml 的页面拆分选项:

        var options = {
            background: '#fff',
            pagesplit: true
        };
        
        var doc = new jsPDF(orientation, 'mm', pagelayout);
        doc.addHTML(source, 0, 0, options, function () {
                doc.save(filename + ".pdf");
                HideLoader();`enter code here`
        });
        

        注意:这会破坏多个页面上的 html,但这些页面会被拉伸。到目前为止,addhtml 中的拉伸是一个问题,我仍然无法在互联网上找到如何解决这个问题。

        【讨论】:

        • jsPDF 的 Github 上有一个issue 讨论拉伸问题和模糊问题。他们确实提供了一些解决方法。
        • 它会拉伸 HTML 吗?
        • 使用最新的 jsPDF,pagesplit option = true 对我来说效果很好。
        猜你喜欢
        • 2016-04-05
        • 1970-01-01
        • 2021-06-05
        • 2020-07-12
        • 2018-01-12
        • 2019-04-06
        • 1970-01-01
        • 1970-01-01
        • 2020-01-11
        相关资源
        最近更新 更多