How do I get a multi-page pdf from a website using jsPDF and HTML2Canvas?
Solution 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');`
Solution 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.
Solution 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();
});
});
}
})();
Solution 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> 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.
Related videos on Youtube
Tom Nolan
Updated on July 09, 2022Comments
-
Tom Nolan almost 2 years
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?
-
L84 over 8 yearsThere is an issue on jsPDF's Github that discusses the stretching problems and blurry problems. They do provide some workarounds.
-
Dynamic about 7 yearsIt stretches the HTML though?
-
Thien Long about 7 yearsWith the latest jsPDF, it works fine for me with pagesplit option = true.
-
avoliva about 5 yearsdid you even try this out?
-
avoliva almost 5 yearsIt definitely doesn't work because promises are asynchronous and this is in a for loop. I tried it out and doesn't work.
-
Jessé Filho almost 5 yearsit depends when and where you want generate your file... But it still works on my projects.
-
helle over 4 yearsThis does not work, I get only the first page. But I guess the problem lies around the html2canvas function...
-
Manmeet Khurana over 3 yearsHow to create the dynamic width and height of the page ?
-
Paul Sender over 3 years
var height = Math.max(body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight)
-
Paul Sender over 3 years
document.body.style.height = ${height}px
-
Paul Sender over 3 years@ManmeetKhurana this is how I get the full height for any page. 1) calculate the height 2) set the height. html2canvas will then set it's 'canvas' height to that value. Can be done with width as well, simply change Height to Width.
-
Sanidhya Singh over 3 yearsThe code was breaking for me when the number of pages in the PDF exceeded 2. I used -pageHeight * counter for position (in the while loop), that fixed it for me
-
WEshruth almost 3 yearsThank you @helle for this line:
let pdf = new jsPDF('p', 'mm', [PDF_Width, PDF_Height]);
Saved my day. -
Iswar over 2 yearsI was trying since not a long ago but unable to meet my requirement. This helps me to find the actual height of a page. Thanks a lot