56

I am using jsPDF in my site to generate PDFs. But now I have multiple DIVs to print in a single PDF. which may take 2 to 3 pages.

For example:

<div id="part1">
  content
</div>

<div id="part2">
  content
</div>

<div id="part2">
   content
</div>

my JS code

  • This works but not as I expected, It add a part of the content(which cannot be included in more than one page).
  • It removes html tags like br, h1 etc.
    function formtoPDF() {
      jsPDF.API.mymethod = function() {
        // 'this' will be ref to internal API object. see jsPDF source
        // , so you can refer to built-in methods like so:
        //   this.line(....)
        //   this.text(....)
      };
      var doc = new jsPDF();
      doc.mymethod();
      var pdfPart1 = jQuery('#genPDFpart1');
      var pdfPart2 = jQuery(".ltinerary");
      var pdfPart3 = jQuery("#domElementHTML");
      var specialElementHandlers = {
        '#loadVar': function(element, renderer) {
          return true;
        }
      };
      doc.fromHTML(pdfPart1.html() + pdfPart3.html() + pdfPart3.html(), 15, 15, {
        'width': 170,
        'elementHandlers': specialElementHandlers
      });
      doc.output('save', 'Download.pdf');
    }

What's the solution for this?

starball
  • 20,030
  • 7
  • 43
  • 238
manipvd
  • 643
  • 1
  • 6
  • 7

11 Answers11

65

I have the same working issue. Searching in MrRio github I found this: https://github.com/MrRio/jsPDF/issues/101

Basically, you have to check the actual page size always before adding new content

doc = new jsPdf();
...
pageHeight= doc.internal.pageSize.height;

// Before adding new content
y = 500 // Height position of new content
if (y >= pageHeight)
{
  doc.addPage();
  y = 0 // Restart height position
}
doc.text(x, y, "value");
bertdida
  • 4,988
  • 2
  • 16
  • 22
MARC.RS
  • 1,108
  • 1
  • 11
  • 15
  • 6
    can you create a demo fiddle for this? – Shrinivas Pai Sep 02 '15 at 07:21
  • 1
    I guess you should iterate somehow until your "height needs" are fulfilled, not only add one page if the element doesn't fit in one (the question indeed says that the content may take 3 pages). But anyway... in my experience, the method "fromHTML" already handles new page creation, although in the page change some content gets lost -- which was the problem I was trying to search about. – DanielM Oct 01 '15 at 07:32
  • 2
    what is x here ? – tousif Noor Mar 21 '17 at 17:45
  • 5
    Well, have been passed 3 long years since. By deduction I guess that "x" is horizontal position – MARC.RS Aug 22 '17 at 10:33
57

here's an example using html2canvas & jspdf, although how you generate the canvas doesn't matter--we're just going to use the height of that as the breakpoint on a for loop, in which a new page is created and content added to it.

after the for loop, the pdf is saved.

function makePDF() {

   var quotes = document.getElementById('container-fluid');
   html2canvas(quotes).then((canvas) => {
        //! MAKE YOUR PDF
        var pdf = new jsPDF('p', 'pt', 'letter');

        for (var i = 0; i <= quotes.clientHeight/980; i++) {
            //! This is all just html2canvas stuff
            var srcImg  = canvas;
            var sX      = 0;
            var sY      = 980*i; // start 980 pixels down for every new page
            var sWidth  = 900;
            var sHeight = 980;
            var dX      = 0;
            var dY      = 0;
            var dWidth  = 900;
            var dHeight = 980;

            window.onePageCanvas = document.createElement("canvas");
            onePageCanvas.setAttribute('width', 900);
            onePageCanvas.setAttribute('height', 980);
            var ctx = onePageCanvas.getContext('2d');
            // details on this usage of this function: 
            // https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Using_images#Slicing
            ctx.drawImage(srcImg,sX,sY,sWidth,sHeight,dX,dY,dWidth,dHeight);

            // document.body.appendChild(canvas);
            var canvasDataURL = onePageCanvas.toDataURL("image/png", 1.0);

            var width         = onePageCanvas.width;
            var height        = onePageCanvas.clientHeight;

            //! If we're on anything other than the first page,
            // add another page
            if (i > 0) {
                pdf.addPage(612, 791); //8.5" x 11" in pts (in*72)
            }
            //! now we declare that we're working on that page
            pdf.setPage(i+1);
            //! now we add content to that page!
            pdf.addImage(canvasDataURL, 'PNG', 20, 40, (width*.62), (height*.62));

        }
        //! after the for loop is finished running, we save the pdf.
        pdf.save('Test.pdf');
  });
}
Kyle Baker
  • 3,424
  • 2
  • 23
  • 33
  • 5
    You are awesome man! I wish I could get this answer soon enough. – Tri Nguyen Dung Apr 24 '16 at 03:05
  • Yes, this helped. but due to more columns, the table exceeds the page width and some columns become invisible. I want table to autoresize to fit the page width. Also, blank space should not be black, it should have white background. Plz help. – ITSagar Dec 20 '16 at 17:47
  • 1
    @ITSagar: check out this answer for changing the size of your canvas: http://stackoverflow.com/questions/3420975/html5-canvas-zooming | Not sure why your blank space is black, though. That didn't occur for me in using this code, so that's a different issue you'll have to work out. – Kyle Baker Dec 21 '16 at 20:35
  • @KyleBaker your solution works very well to me i want to ask is there any possible solution that we can have the landscape type print in this form? – tech_geek Apr 06 '17 at 08:39
  • @adeel, haven't worked with this code in some time, however, my first assumption would be to try swapping the height and width... – Kyle Baker Apr 06 '17 at 10:12
  • Swap all "980" in the code for "900", and swap "612" for "791". If you're lucky, that might be enough. – Kyle Baker Apr 06 '17 at 12:22
  • 2
    for those who want page margins, I modified the height and width of the canvas via my #content's dimensions `var setWidth = 1170; var setHeight = 1514.118;` then played with the addImage parameters `pdf.addImage(canvasDataURL, 'PNG', 40, 40, (width*.62) - 190, (height*.62) - 90);` – Rad Apdal Nov 20 '17 at 06:19
  • @KyleBaker html2canvas: onrendered option is deprecated, html2canvas returns a Promise with the canvas as the value – Sohail Oct 30 '18 at 11:15
  • i am getting error in onePageCanvas and create blank pdf. – Bharti Ladumor Nov 24 '18 at 07:11
  • @sohail this answer is from years ago. You don't really need to tell me, though it's fine to tell the world. Still, the answer provides helpful conceptual information--converting callbacks to promises is not difficult. Feel free to edit my answer into the new promise format if you have the time. – Kyle Baker Dec 09 '18 at 00:05
  • 4
    You are a legend sir, thanyou – Jack Clayton Mar 16 '21 at 05:35
  • 1
    2022 says thanks! – eamanola Oct 13 '22 at 16:34
  • Amazingly, this was from my literally very first day-1 coding assignment at my very first "real" programming job. Was supposed to make something that allowed printing out quotes from a web page, needed a kind of screenshot functionality. Cool it see it still helping people all these years later. – Kyle Baker Oct 14 '22 at 19:03
34

I found the solution on this page: https://github.com/MrRio/jsPDF/issues/434 From the user: wangzhixuan

I copy the solution here: // suppose your picture is already in a canvas

      var imgData = canvas.toDataURL('image/png');

      /*
      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 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');
Jason Glez
  • 1,254
  • 1
  • 16
  • 16
  • Here's a complete example to create a html image in a pdf with jsPDF and html2canvas: http://www.techumber.com/2015/04/html-to-pdf-conversion-using-javascript.html with out the previous solution – Jason Glez Mar 05 '16 at 16:33
  • Thanks it works. https://github.com/MrRio/jsPDF/issues/434#issuecomment-145701763 – Hoang Subin Apr 12 '19 at 02:37
  • Thanks man, works like a charm. Only one thing, if you use click event, you have to go to the top of the page. window.scrollTo(0, 0); – Philip Jan 29 '20 at 08:15
  • doc.addImage(imgData, 'PNG', 0, position, imgWidth, imgHeight, undefined,'FAST'); PDF size 90mb to 3mb with great quality. – Utkarsh Apr 05 '21 at 11:45
9
 var doc = new jsPDF('p', 'mm');

 var imgData = canvas.toDataURL('image/png');
 var pageHeight= doc.internal.pageSize.getHeight();
 var pageWidth= doc.internal.pageSize.getWidth();

 var imgheight = $('divName').height() * 25.4 / 96; //px to mm
 var pagecount = Math.ceil(imgheight / pageHeight);

 /* add initial page */
 doc.addPage('l','mm','a4');
 doc.addImage(imgData, 'PNG', 2, 0, pageWidth-4, 0);

 /* add extra pages if the div size is larger than a a4 size */
 if (pagecount > 0) {
     var j = 1;
     while (j != pagecount) {
         doc.addPage('l','mm','a4');
         doc.addImage(imgData, 'PNG', 2, -(j * pageHeight), pageWidth-4, 0);
         j++;
     }
 }
xyzabc
  • 451
  • 1
  • 6
  • 13
4

You can use html2canvas plugin and jsPDF both. Process order: html to png & png to pdf

Example code:

jQuery('#part1').html2canvas({
    onrendered: function( canvas ) {
        var img1 = canvas.toDataURL('image/png');
    }
});
jQuery('#part2').html2canvas({
    onrendered: function( canvas ) {
        var img2 = canvas.toDataURL('image/png');
    }
});
jQuery('#part3').html2canvas({
    onrendered: function( canvas ) {
        var img3 = canvas.toDataURL('image/png');
    }
});
var doc = new jsPDF('p', 'mm');
doc.addImage( img1, 'PNG', 0, 0, 210, 297); // A4 sizes
doc.addImage( img2, 'PNG', 0, 90, 210, 297); // img1 and img2 on first page

doc.addPage();
doc.addImage( img3, 'PNG', 0, 0, 210, 297); // img3 on second page
doc.save("file.pdf");
Mehmet Hanoğlu
  • 2,942
  • 1
  • 13
  • 19
3
$( document ).ready(function() {    
$('#cmd').click(function() {
      var options = {
              pagesplit: true //include this in your code
      };
      var pdf = new jsPDF('p', 'pt', 'a4');
      pdf.addHTML($("#pdfContent"), 15, 15, options, function() {
        pdf.save('Menu.pdf');
      });
    });
});
Nikhil
  • 41
  • 2
  • 4
    While this code may answer the question, providing additional context regarding why and/or how this code answers the question improves its long-term value. – Thomas Flinkow Mar 22 '18 at 07:40
1

This is my first post which support only a single page http://www.techumber.com/html-to-pdf-conversion-using-javascript/

Now, the second one will support the multiple pages. http://www.techumber.com/how-to-convert-html-to-pdf-using-javascript-multipage/

Dimpu Aravind Buddha
  • 9,505
  • 4
  • 17
  • 23
0

Below is my code but the problem is that the document doesn't split to display the other part of the document in a new page.

Please improve this code.

<script type='text/javascript'>
$(document).on("click", "#btnExportToPDF", function () {
    var table1 =
    tableToJson($('#table1')[0]),
    cellWidth =42,
    rowCount = 0,
    cellContents,
    leftMargin = 2,
    topMargin = 12,
    topMarginTable =5,
    headerRowHeight = 13,
    rowHeight = 12,
    l = {
        orientation: 'p',
        unit: 'mm',
        format: 'a3',
        compress: true,
        fontSize: 11,
        lineHeight: 1,
        autoSize: false,
        printHeaders: true
    };

    var doc = new jsPDF(l,'pt', 'letter');

    doc.setProperties({
        title: 'Test PDF Document',
        subject: 'This is the subject',
        author: 'author',
        keywords: 'generated, javascript, web 2.0, ajax',
        creator: 'author'
    });
    doc.cellInitialize();

    $.each(table1, function (i, row)
    {
        rowCount++;

        $.each(row, function (j, cellContent) {

            if (rowCount == 1) {
                doc.margins = 1;
                doc.setFont("Times New Roman");
                doc.setFontType("bold");
                doc.setFontSize(11);

                doc.cell(leftMargin, topMargin, cellWidth, headerRowHeight, cellContent, i)
            }
            else if (rowCount == 2) {

                doc.margins = 1;
                doc.setFont("Times ");
                doc.setFontType("normal");
                // or for normal font type use ------ doc.setFontType("normal");

                doc.setFontSize(11);

                doc.cell(leftMargin, topMargin, cellWidth, rowHeight, cellContent, i);
            }
            else {

                doc.margins = 1;
                doc.setFont("Times  ");
                doc.setFontType("normal ");
                doc.setFontSize(11);
                doc.cell(leftMargin, topMargin, cellWidth, rowHeight, cellContent, i);
                // 1st=left margin    2nd parameter=top margin,     3rd=row cell width      4th=Row height
            }
        })
    })
    doc.save('sample Report.pdf');
});

function tableToJson(table) {

    var data = [];

    // first row needs to be headers
    var headers = [];

    for (var i=0; i<table.rows[0].cells.length; i++) {
        headers[i] = table.rows[0].cells[i].innerHTML.toLowerCase().replace(/ /gi,'');
    }

    // go through cells
    for (var i=1; i<table.rows.length; i++) {

        var tableRow = table.rows[i];

        var rowData = {};

        for (var j=0; j<tableRow.cells.length; j++) {
            rowData[ headers[j] ] = tableRow.cells[j].innerHTML;
        }

        data.push(rowData);
    }

    return data;
}
</script>
spenibus
  • 4,339
  • 11
  • 26
  • 35
Kazeem
  • 9
  • 1
0

Automatically not split data to multi pages. You may split manually.

If your ( rowCount * rowHeight ) > 420mm ( A3 Height in mm ) add new page function. ( Sorry I can't edit your code without run ) After add new page leftMargin, topMargin = 0; ( start over ) I added sample code with yours. I hope it's right.

else {
    doc.margins = 1;
    doc.setFont("Times  ");
    doc.setFontType("normal ");
    doc.setFontSize(11);
    if ( rowCount * rowHeight > 420 ) {
        doc.addPage();
        rowCount = 3; // skip 1 and 2 above
    } else {
        // now rowcount = 3 ( top of new page for 3 )
        // j is your x axis cell index ( j start from 0 on $.each function ) or you can add cellCount like rowCount and replace with
        // rowcount is your y axis cell index
        left = ( ( j ) * ( cellWidth + leftMargin );
        top = ( ( rowcount - 3 ) * ( rowHeight + topMargin );
        doc.cell( leftMargin, top, cellWidth, rowHeight, cellContent, i);
        // 1st=left margin    2nd parameter=top margin,     3rd=row cell width      4th=Row height
    }
}

You can convert html directly to pdf lossless. Youtube video for html => pdf example

Mehmet Hanoğlu
  • 2,942
  • 1
  • 13
  • 19
0
         html2canvas(element[0], {
                    onrendered: function (canvas) {
                        pages = Math.ceil(element[0].clientHeight / 1450);
                        for (i = 0; i <= pages; i += 1) {
                            if (i > 0) {
                                pdf.addPage();
                            }
                            srcImg = canvas;
                            sX = 0;
                            sY = 1450 * i;
                            sWidth = 1100;
                            sHeight = 1450;
                            dX = 0;
                            dY = 0;
                            dWidth = 1100;
                            dHeight = 1450;
                            window.onePageCanvas = document.createElement("canvas");
                            onePageCanvas.setAttribute('width', 1100);
                            onePageCanvas.setAttribute('height', 1450);
                            ctx = onePageCanvas.getContext('2d');
                            ctx.drawImage(srcImg, sX, sY, sWidth, sHeight, dX, dY, dWidth, dHeight);
                            canvasDataURL = onePageCanvas.toDataURL("image/png");
                            width = onePageCanvas.width;
                            height = onePageCanvas.clientHeight;
                            pdf.setPage(i + 1);
                            pdf.addImage(canvasDataURL, 'PNG', 35, 30, (width * 0.5), (height * 0.5));
                        }
                        pdf.save('testfilename.pdf');
                    }
                });
0
var a = 0;
var d;
var increment;

for(n in array){
    d = a++;        

    if(n % 6 === 0 && n != 0){
        doc.addPage();
        a = 1;
        d = 0;
    }

    increment = d == 0 ? 10 : 50;
    size = (d * increment) <= 0 ? 10 : d * increment;

    doc.text(array[n], 10, size);
}
Habib Mammadov
  • 311
  • 4
  • 4