Javascript converts HTML to pdf for download (html 2 canvas and jsPDF)

Keywords: github Javascript Attribute REST

Recently encountered a requirement, need to generate the current page pdf, and download. After a few days, I organized myself and recorded it. I think someone needs to:
Project source address: https://github.com/linwalker/render-html-to-pdf

html2canvas

brief introduction

  • We can use html2canvas directly on the browser side to take a'screenshot'of the whole or part of the page. But this is not a true screenshot, but a canvas-image is rendered by traversing the DOM structure of the page, collecting all element information and corresponding styles.
  • Because HTML 2 canvas can only process it to generate canvas-image, the rendered results are not 100% the same as the original. But it does not require the participation of the server, the entire picture is generated by the client browser, easy to use.

Use

  • The API used is also concise. The following code can render an element as canvas:
html2canvas(element, {
    onrendered: function(canvas) {
        // canvas is the final rendered <canvas> element
    }
});

The onrendered method allows you to call back the generated canvas, such as inserting it into a page:

html2canvas(element, {
    onrendered: function(canvas) {
       document.body.appendChild(canvas);
    }
});

To do a small example code as follows, online display links demo1

<html>
  <head>
    <title>html2canvas example</title>
    <style type="text/css">...</style>
  </head>
  <body>
    <header>
      <nav>
        <ul>
          <li>one</li>
          ...
        </ul>
      </nav>
    </header>
    <section>
      <aside>
        <h3>it is a title</h3>
        <a href="">Stone Giant</a>
        ...
     </aside>
      <article>
        ![](./Stone.png)
        <h2>Stone Giant</h2>
        <p>Coming ... </p>
        <p>With a mass of stones...</p>
      </article>
    </section>
    <footer>write by linwalker @2017</footer>
    <script type="text/javascript" src="./html2canvas.js"></script>
    <script type="text/javascript">
        html2canvas(document.body, {
          onrendered:function(canvas) {
            document.body.appendChild(canvas)
          }
        })
    </script>
  </body>
</html>

This example renders the elements in the page body into canvas and inserts them into the body.

jsPDF

The jsPDF library can be used to generate PDF on the browser side.

Text Generation PDF

The method of use is as follows:

// Default a4 size, vertical direction, PDF in mm units
var doc = new jsPDF();

// Add the text'Download PDF'
doc.text('Download PDF!', 10, 10);
doc.save('a4.pdf');

Online demo demo2

Photo Generation PDF

The method of use is as follows:

// Three parameters, the first direction, the second unit and the third dimension format
var doc = new jsPDF('landscape', 'pt', [205, 115])

// Converting pictures to dataUrl
var imageData = 'data:image/png; base64, iVBORw0KGgo...';

doc.addImage(imageData, 'PNG', 0, 0, 205, 115);
doc.save('a4.pdf');

Online demo demo3

Text and Picture Generation PDF

The method of use is as follows:

// Three parameters, the first direction, the second dimension and the third dimension format
var doc = new jsPDF('landscape','pt',[205, 155])

// Converting pictures to dataUrl
var imageData = 'data:image/png;base64,iVBORw0KGgo...';

//Set font size
doc.setFontSize(20);

//These two parameters control the distance between the text on the left and the text on the top.
doc.text('Stone', 10, 20);

// 0, 40. Control the distance between the text on the left and the text on the top.
doc.addImage(imageData, 'PNG', 0, 40, 205, 115);
doc.save('a4.pdf')

Online demo demo4

Generating PDF requires adding transformed elements to jsPDF instances and adding html, but some elements can not be generated in pdf, so we can use html2canvas + jsPDF to convert pages into pdf. Through HTML 2 canvas, the page elements are traversed and rendered to generate canvas. Then the canvas image format is added to the jsPDF instance to generate pdf.

html2canvas + jsPDF

Single page

Modify the demo1 example as follows:

<script type="text/javascript" src="./js/jsPdf.debug.js"></script>
<script type="text/javascript">
      var downPdf = document.getElementById("renderPdf");
      downPdf.onclick = function() {
          html2canvas(document.body, {
              onrendered:function(canvas) {

                  //Returns the image data URL, parameter: image format and clarity (0-1)
                  var pageData = canvas.toDataURL('image/jpeg', 1.0);

                  //Default vertical direction, size ponits, format a4[595.28,841.89]
                  var pdf = new jsPDF('', 'pt', 'a4');

                  //Two parameters after addImage control the size of the added image, where the page height is compressed according to the width-height ratio column of a4 paper.
                  pdf.addImage(pageData, 'JPEG', 0, 0, 595.28, 592.28/canvas.width * canvas.height );

                  pdf.save('stone.pdf');

              }
          })
      }
</script>

Online demo demo5
What happens to the generated pdf if the page content is converted according to the a4 ratio and the height of the page exceeds the height of the a4 paper? Do you paginate?

You can try and test your ideas.
demo6

jsPDF provides a very useful API, addPage(), we can add a page of PDF through pdf.addPage(), and then through pdf.addImage(...), the picture is given to this page PDF to display.

So how do we determine where to page?

To answer this question, we can set up a page Height to put content beyond this height into the next page pdf.

To explore the idea, generate canvas images from html page content, add the first page image to pdf through addImage, add more than one page content through addPage(), add pdf pages through addPage(), and then add the next page image to pdf through addImage.

Well, good! Bart, didn't you find the problem?

The prerequisite for this method to be realized is that uuuuuuuuuuu
According to page Height, we can first divide the canvas image generated by the whole page into corresponding small pictures, then a radish pit, a page by page addImage into.

What? Think about how swollen our canvas is. Instead of pulling it up, look directly at the following:

html2canvas(document.body, {
    onrendered:function(canvas) {
     //it is here we handle the canvas
    }
})

Here body is to generate an element object of canvas, an element to generate a canvas; then we need a page by page of canvas, that is to say...

Do you think it's possible?
I don't think it's realistic to get DOM elements at different locations on the page, and then process them through htnl2canvas(element,option), not to mention whether we can find a DOM element just in the position of each page Height, even if we find it, it's not tired to do so.

Tired words
:) Look at the following method.

Multi page

The idea I'm offering is that we only generate one canvas, for one, the transformation element is the parent element of the content you want to convert to pdf, and in this demo, the body; the rest is unchanged, and more than a page of content addPage, and then addImage, except that the same canvas is added here.

Of course, this will only lead to multiple pages of duplicate pdf, then how to achieve the correct paging display. In fact, it mainly uses two points of jsPDF:

- Contents that exceed the jsPDF instance format size are not displayed
 (var PDF = new jsPDF ('','pt','a4'); the size of A4 paper in demo)
- addImage has two parameters to control the position of the picture in pdf

Although the pictures displayed on each pdf page are the same, we create the illusion of paging by adjusting the position of the pictures. Taking the second page as an example, the offset in the vertical direction is set to - 841.89, that is, the height of an a4 paper, and because the picture beyond the height range of a4 paper is not displayed, the second page shows the content in the vertical direction of the picture [841.89, 1682.78], which obtains the effect of pagination, and so on.

Look at the code.

html2canvas(document.body, {
  onrendered:function(canvas) {
      var contentWidth = canvas.width;
      var contentHeight = canvas.height;
      //One page pdf shows the canvas height generated by html pages.
      var pageHeight = contentWidth / 592.28 * 841.89;
      //html page height without pdf generation
      var leftHeight = contentHeight;
      //Page offset
      var position = 0;
      //a4 paper size [595.28, 841.89], html page generated canvas in pdf picture width
      var imgWidth = 595.28;
      var imgHeight = 592.28/contentWidth * contentHeight;
      var pageData = canvas.toDataURL('image/jpeg', 1.0);
      var pdf = new jsPDF('', 'pt', 'a4');
      //There are two heights to distinguish, one is the actual height of the html page, and the page height of the generated pdf (841.89)
      //When the content does not exceed the range of pdf page display, there is no need to paginate
      if (leftHeight < pageHeight) {
      pdf.addImage(pageData, 'JPEG', 0, 0, imgWidth, imgHeight );
      } else {
          while(leftHeight > 0) {
              pdf.addImage(pageData, 'JPEG', 0, position, imgWidth, imgHeight)
              leftHeight -= pageHeight;
              position -= 841.89;
              //Avoid adding blank pages
              if(leftHeight > 0) {
                pdf.addPage();
              }
          }
      }
      pdf.save('content.pdf');
  }
})

Online demo demo7

Leave margins on both sides

Modify imgWidth and set the required margin in the x direction parameter when addImage is added. The specific code is as follows:

var imgWidth = 555.28;
var imgHeight = 555.28/contentWidth * contentHeight;
...
pdf.addImage(pageData, 'JPEG', 20, 0, imgWidth, imgHeight );
...
pdf.addImage(pageData, 'JPEG', 20, position, imgWidth, imgHeight);

Online demo demo8

Reproduction sources: bbs.thankbabe.com

Posted by DjMikeS on Mon, 24 Dec 2018 15:48:06 -0800