How do I get a multi-page pdf from a website using jsPDF and HTML2Canvas?

早过忘川 提交于 2021-02-18 09:58:11

问题


I have a script that uses HTML2Canvas to take a screenshot of a div within the page, and then converts it to a pdf using jsPDF.

The problem is the pdf that is generated is only one page, and the screenshot requires more than one page in some instances. For example the screenshot is larger than 8.5x11. The width is fine, but I need it to create more than one page to fit the entire screenshot.

Here is my script:

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

Any ideas how I could modify that to allow for multiple pages?


回答1:


pdf.addHtml does not work if there are svg images on the web page. I copy the solution here, based on the picture being in a canvas already.

Here are the numbers (paper width and height) that I found to work. It still creates a little overlap part between the pages, but good enough for me. if you can find an official number from jsPDF, use them.

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');`



回答2:


you can use page split option of addhtml like this:

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

Note: This will break the html on multiple pages but these pages will get stretched. Stretching is a issue in addhtml till now and i am still not able to find on internet how to solve this issue.




回答3:


I was able to get it done using async functionality.

(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:


This is my aproach, also with jspdf and html2canvas which did a very good job for me:

I am using a wrapper node which holds the content to be converted to 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>

This is the JS integration ( I only checked this versions ) and call:

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

And here you can see the pdf generation. I encapsulated the generation part in an extra js object (you need to include this file also):

//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');
};

my css example for the .printview case:

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

enjoy

Contributions to @Code Spy for the Link you provided, which was the basis for this aporach.




回答5:


You can try it:

$('#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();
            }
        });
    }
});


来源:https://stackoverflow.com/questions/27045704/how-do-i-get-a-multi-page-pdf-from-a-website-using-jspdf-and-html2canvas

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!