Generate Multipage PDF using Single Canvas of HTML Document using jsPDF

jsPDF is a nice library to convert HTML content into PDF. We can put the different type of elements in PDF from HTML like an icon, images, text, CSS styles.
Here we will discuss an example of jsPDF to convert long HTML page into PDF document which will be generated client-side and download.

Let’s start…

Step 1) Include jsPDF and html2canvas liberary URl in head section of your HTML.

<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<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>

 

Step 2) We will add JS code for HTML to PDF conversion

In this code block we have used html2canvas function which will give canvas of HTML section we specified, then after getting canvas object we will add PDF page using a jsPDF method and add break-up of canvas s image(JPG) in PDF page.

There are some variables defined to automate Canvas width and HTML width height blunders during conversion. Names of variables are self-explanatory. In "totalPDFPages" we are getting total PDF pages we need to display whole data in HTML.

	function getPDF(){

		var HTML_Width = $(".canvas_div_pdf").width();
		var HTML_Height = $(".canvas_div_pdf").height();
		var top_left_margin = 15;
		var PDF_Width = HTML_Width+(top_left_margin*2);
		var PDF_Height = (PDF_Width*1.5)+(top_left_margin*2);
		var canvas_image_width = HTML_Width;
		var canvas_image_height = HTML_Height;
		
		var totalPDFPages = Math.ceil(HTML_Height/PDF_Height)-1;
		

		html2canvas($(".canvas_div_pdf")[0],{allowTaint:true}).then(function(canvas) {
			canvas.getContext('2d');
			
			console.log(canvas.height+"  "+canvas.width);
			
			
			var imgData = canvas.toDataURL("image/jpeg", 1.0);
			var pdf = new jsPDF('p', 'pt',  [PDF_Width, PDF_Height]);
		    pdf.addImage(imgData, 'JPG', top_left_margin, top_left_margin,canvas_image_width,canvas_image_height);
			
			
			for (var 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),canvas_image_width,canvas_image_height);
			}
			
		    pdf.save("HTML-Document.pdf");
        });
	};

 

Step 3) Following is HTML part having container selector(canvas_div_pdf) which we used in JS code in step 2.

<div class="canvas_div_pdf">

<!--Add HTML content you want to convert to PDF-->

</div>

Other Method: We can have multiple sections on the page and we can convert each section to a canvas to add to separate PDF page.

See working demo here.

Let me know if you have more suggestions.

Happy Coding 🙂

32 thoughts on “Generate Multipage PDF using Single Canvas of HTML Document using jsPDF”

  1. Thank you so much for this code, you saved me a great amount of time !
    I’d like to add a little correction for the vertical margins : when you add the imgData to PDF, you may want to count 2 of them for each page before, and one for the current page, by changing :
    pdf.addImage(imgData, ‘JPG’, top_left_margin, -(PDF_Height*i)+ (top_left_margin*4), canvas_image_width, canvas_image_height);

    To :
    pdf.addImage(imgData, ‘JPG’, top_left_margin, -(PDF_Height*i)+(top_left_margin*(2*i+1)), canvas_image_width,canvas_image_height);

    For cleaner result I also added white rectangles to cover the margins (add this twice : for the first page and the following ones) :
    pdf.setFillColor(255,255,255);
    pdf.rect(0, 0, PDF_Width, top_left_margin, ‘F’);
    pdf.rect(0, PDF_Height-top_left_margin, PDF_Width, top_left_margin, ‘F’);

    It’s also easy to adapt your code to output the result in A4 format (or something else), by changing PDF_Width and PDF_Height values, and doing a little math in addImage codes to adapt the canvas sizing 🙂

    Thanks again !

      1. Sure ! I kept values in pt in order to modify as few things as possible for the rest of the function.

        First, set your PDF dimensions like this :
        var PDF_Width = 595.276; // = 21cm
        var PDF_Height = 841.8898; // = 29.7cm

        Then the canvas dimensions should be set to these values (width is just PDF width minus margins, and height is proportionnaly adapted) :
        var canvas_image_width_A4 = (PDF_Width-(2*top_left_margin));
        var canvas_image_height_A4 = (HTML_Height*((PDF_Width-(2*top_left_margin))/HTML_Width));

        And finally totalPDFPages should be calculated with the new canvas height instead of HTML’s (this prevents excess blank pages from appearing) :
        var totalPDFPages = Math.ceil(canvas_image_height/(PDF_Height-(2*top_left_margin)))-1;

        Hope this may help

        1.    Thank you KopDoc and OP, the jsPDF API was difficult to parse and it would have taken a lot longer without your input. In thanks I’ve included all the relevant parts of the logic that I came up with, including a couple improvements of my own (to do with code reuse, naming and margins). It’s also heavily commented in areas that were hard for me to figure out initially.

              const stepEle: HTMLElement = stepEleRef.nativeElement;
              const { offsetWidth, offsetHeight, ...rest } = stepEle;
          
              const pdfWidth = 595.276;
              const pdfHeight = 841.8898;
              // Equivalent of MSFT Word 'thin-margins' (.5inches / 8.5inches) === .06 === ratio of margin to content
              const topLeftMargin = pdfWidth * 0.06;
          
              // Scale image width to fit in content area of PDF
              const canvasImgWidth = pdfWidth - 2 * topLeftMargin;
              // Scale image height to match new/old image width ratio
              const canvasImgHeight = offsetHeight * (canvasImgWidth / offsetWidth);
          
              // How many times the pdf content area fits in the total image height
              const totalPDFPages = Math.ceil(canvasImgHeight / (pdfHeight - 2 * topLeftMargin));
          
              html2canvas(stepEle).then((canvas) => {
                const contentDataURL = canvas.toDataURL('image/jpeg');
          
                // Generate PDF in portrait mode, using pts unit, each page added will have below width/height and can be compressed
                let pdf = new jsPDF('p', 'pt', [pdfWidth, pdfHeight], true); //Generates PDF in portrait mode
          
                for (let i = 0; i < totalPDFPages; i++) {
                  if (i > 0) pdf.addPage();
          
                  pdf.addImage(
                    contentDataURL,
                    'JPEG',
                    topLeftMargin, // + vals shift image projected on page to the right
                    // Shift projected image _up_ i pages, then back down equivalent to all previous pages' + current page margin(s)
                    -(pdfHeight * i) + topLeftMargin * (2 * i + 1), // + vals shift image projected on page down
                    canvasImgWidth, // + vals scale image projected on page up or down (horizontally) dep. on ratio to canvasImgWidth
                    canvasImgHeight, // + vals scale image projected on page up or down (vertically) dep. on ratio to canvasImgHeight
                    undefined,
                    'FAST' // FAST compression compresses _less_ than SLOW, but completes faster
                  );
          
                  pdf.setFillColor(255, 255, 255);
                  // This rectangle is set on top of the page and literally covers excess of projected image
                  // e.g. if these methods went before addImage, the image would 'overwrite' the rectangles
                  pdf.rect(0, 0, pdfWidth, topLeftMargin, 'F');
                  // This rectangle is set on the bottom of the page and covers excess to bottom of page
                  pdf.rect(0, pdfHeight - topLeftMargin, pdfWidth, topLeftMargin, 'F');
                }
          
                pdf.save(this.task.split(' ').join('') + 'File.pdf');
              });
          
      2. Note that I used new names for the canvas dimension variables. You should either keep the old ones (var canvas_image_width and var canvas_image_height) or replace them by the new names in the rest of the code

        1. Thanks a lot for the codes, really appreciate it! Been having trouble getting good output in A4 format.

    1. Hi KopDoc,
      I was working on LWC Component can you please help me on that. I cant able to load the JSPdf static resources and also I am getting blank image from html while clicking on button to generate HTML content as PDF
      Thanks,

  2. I am handling with multiple tables, when I use the above code to generate the pdf file some of the tables are displaying as empty tables, I can’t able to debug it, can you please help me out…

Leave a Comment

Your email address will not be published. Required fields are marked *