Search code examples
javascripthtmljqueryjspdfpage-break

jsPDF html method with addPage to split pages in generated PDF


I am using jsPDF's html method to generate a PDF for some html that comprises of multiple paragraphs p with each p containing words between 50-500. The PDF is generated properly with the styles I have defined. The only issue I am facing is when the contents of the p tags overflow to a new page in PDF cutting off parts of text as can be seen here:

enter image description here

To counter this, I need to introduce addPage into my code to insert a "page break" where I wish. Here is a sample of addPage that works with jsPDF's text function:

var doc = new jsPDF('p', 'pt', 'letter');
doc.text(20, 20, 'first text');
doc.addPage();
doc.text(20, 20, 'second text');
doc.addPage();
doc.text(20, 20, 'third text');
doc.save();

I want to do the same for html and here is the code I used:

var doc = new jsPDF('p', 'pt', 'letter');
doc.html($("#p1")[0], {
    callback: function (doc) {
        doc.addPage('letter', 'p');

        doc.html($("#p2")[0], {
            callback: function (doc) {
                doc.save();
            }
        });
    }
});

The above generates a PDF with the contents with 2 pages as desired. But the contents that are suppose to be on the second page are on the first, on top of the contents of first page. In other words, #p2 contents are pasted on top of #p1 contents.

Here is the code snippet but please note, SO is throwing cross-origin error when you try to run the code so you will need to run the below outside SO:

function wrap() {
  var doc = new jsPDF('p', 'pt', 'letter');
  doc.html($("#p1")[0], {
    callback: function(doc) {
      doc.addPage('letter', 'p');
      doc.html($("#p2")[0], {
        callback: function(doc) {
          doc.save();
        }
      });
    }
  });
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://unpkg.com/jspdf@latest/dist/jspdf.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/html2canvas.js"></script>

<button id="btn" onclick="wrap()">Test</button>
<div id="lipsum">
  <p id="p1">
    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sed risus at orci elementum consequat. Suspendisse laoreet rhoncus dignissim. Aliquam a ultrices orci, in elementum nibh. Aliquam mollis erat at vehicula pellentesque. Nunc suscipit, leo
    at convallis lobortis, odio ipsum euismod sapien, auctor accumsan velit eros non risus. Cras sagittis nisi non orci finibus porta. Sed finibus ac eros eu tincidunt. Nam viverra egestas augue vestibulum elementum. Suspendisse convallis felis sodales,
    ornare neque ut, elementum dui. Mauris ac orci mattis, tristique nisi quis, ultricies urna. In tincidunt, dolor vitae euismod mattis, lorem arcu placerat velit, quis auctor est est et sem. Aenean convallis finibus posuere. Sed feugiat orci a lacinia
    efficitur. Ut semper, purus quis convallis vehicula, mi risus rhoncus arcu, et feugiat neque turpis non tellus.
  </p>
  <p id="p2">
    Mauris dapibus felis leo, vitae vestibulum purus feugiat sit amet. Fusce ac dapibus nunc. Etiam congue mi neque. Maecenas eget blandit turpis. Suspendisse volutpat, urna eu facilisis congue, elit felis faucibus velit, venenatis semper sem sem ut ante.
    Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Praesent blandit congue sapien. Curabitur non fermentum felis, sit amet auctor nunc. Cras id tellus nunc.
  </p>
  <p id="p3">
    In lacinia lectus nec quam dapibus eleifend. Phasellus iaculis eget massa non aliquam. Cras interdum gravida hendrerit. Suspendisse rhoncus pretium erat non malesuada. Fusce sit amet finibus est. Phasellus quis dapibus orci. Nunc iaculis felis odio, et
    dictum leo mollis sed. Morbi ultrices turpis at mi pellentesque, sit amet consectetur tortor volutpat. Mauris ac nunc ac nunc pellentesque lacinia. Duis at ligula tristique, maximus sapien sit amet, pulvinar neque.
  </p>

</div>

Please also note that the version used for html2canvas js is important here. See this for more info.


Solution

  • Using this, specifically the template (lines 125-150), I found a solution that "moves" the second (subsequent) page to the correct y (y: 792) coordinates:

    function wrap() {
      var doc = new jsPDF('p', 'pt', 'letter');
      doc.html($("#p1")[0], {
        callback: function(doc) {
          doc.addPage('letter', 'p');
          doc.html($("#p2")[0], {
            callback: function(doc) {
              doc.save();
            }, y:792
          });
        }, html2canvas: {scale: 1}
      });
    }
    

    Where 792 is the vertial length of a single letter page. In other words, the p2 renders on a position with a defined y-axis coordinate.

    One can update the code above (recursive function) to do this for multiple p elements or even split the pages on other criteria. I hope this helps someone.

    Note: html2canvas: {scale: 1} is used to resolve another problem that I had with page scaling of produced PDF on mobile devices.