如何使用jsPDF和HTML2Canvas从网站获取多页pdf?

19

我有一个脚本,它使用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');
      });
 });

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

5个回答

47

如果网页上有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');`

如何创建动态宽度和高度的页面? - Manmeet Khurana
var height = Math.max(body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight) - Paul Sender
document.body.style.height = ${height}px - Paul Sender
@ManmeetKhurana 这是我获取任何页面完整高度的方法。1)计算高度 2)设置高度。然后,html2canvas将设置其“画布”高度为该值。同样可以通过宽度完成,只需将Height更改为Width即可。 - Paul Sender
当PDF中的页面数量超过2页时,我的代码出现了问题。我在while循环中使用了“-pageHeight * counter”来定位,这解决了我的问题。 - Sanidhya Singh
我之前一直在尝试,但是一直无法满足我的需求。这个帮助我找到了页面的实际高度。非常感谢。 - Iswar

5
您可以像这样使用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中拉伸是一个问题,到目前为止我仍然无法在互联网上找到如何解决这个问题。


1
在jsPDF的Github上有一个问题,讨论了拉伸问题和模糊问题。他们提供了一些解决方法。 - L84
它会拉伸 HTML 吗? - Dynamic
使用最新的jsPDF,启用pagesplit选项=true,对我来说效果很好。 - Thien Long

1
我使用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();
        });
      });
    }
})();

1
这是我的方法,同时使用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为您提供的链接,这是这种方法的基础。


1
谢谢@helle提供这行代码:let pdf = new jsPDF('p', 'mm', [PDF_Width, PDF_Height]);,救了我的一天。 - WEshruth

0
找到了解决方法,为每个PDF页面放置一个矩形作为边框,并通过在页面高度上进行差异来从第二页和其他页面开始向下移动一点,希望这能解决一些问题...
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');

    });
  };

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接