Convert HTML/CSS Content to a Sleek Multiple Page PDF File Using jsPDF JavaScript library

Published Dec 22, 2017Last updated Apr 22, 2018
Convert HTML/CSS Content to a Sleek Multiple Page PDF File Using jsPDF JavaScript library

Introduction

Generating PDF file format in client-side JavaScript is now trivial with a great library we have probably heard of.

The library allows you to select the DOM (Document Object Model) elements that you wish to publish as PDF content.

Exporting generic HTML/CSS to PDF has been an open question without a definitive answer for quite a long time.

We have struggled with a variety of approaches and, in general, all of them have drawbacks. I have concrete research on the use of the jsPDF library and am going to walk you through the process of setting things up and creating a fairly simple, yet complex, design that you will need on your journey to making great PDF reporting templates.

Problem statement

Working with generic HTML/CSS content to PDF has been an open question without a definitive answer for quite a long time. We have struggled with a variety of approaches and, in general, all of them have drawbacks.

Popular questions you will see on the web are shown below:
• Is it possible to save an HTML page as a PDF using JavaScript or jQuery?
• How to export HTML pages as a PDF using pure JavaScript.
• How to create multiple pages.
• Generate PDF from HTML in div using JavaScript.
• Multiple pages with HTML to PDF converters.

The questions go on and on.

The Goal of this Tutorial

I will walk you through getting started and integrating jsPDF into your browser-based JavaScript web application.

At the end of this lesson, you will be able to do the following:
• Use the fromHtml API End Point to get our HTML content container wrapper.
• Create a PDF with multiple pages.
• Create a function to convert images to base64 image Uniform Resource Locator (URL) and use in our header.
• Add header and footer text (like page count) to every single generated jsPDF page.

Prerequisites

You can find the library here: MrRio/jsPDF.
Click to download octocat.jpg.

Download the jsPDF library and use the image as a guide for our project folder structure:
folder-structure.png

First, we will set up our folder structure as seen in the Figure above.
Open the jsPDF folder from the prerequisite and copy the dist folder to your project folder.
Create a new file and name it index.html.
Download or Copy the Header Image to the project folder.

Working with the index.html file

We will create the skeleton of our HTML with the standard HTML tags:

Index.html

<!doctype>
<html>
<head>
    <title>Html to Multi Page PDF using jsPDF</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <style>
    @CHARSET "UTF-8";
    .page-break {
      page-break-after: always;
      page-break-inside: avoid;
      clear:both;
    }
    .page-break-before {
      page-break-before: always;
      page-break-inside: avoid;
      clear:both;
    }
    #html-2-pdfwrapper{
      position: absolute; 
      left: 20px; 
      top: 50px; 
      bottom: 0; 
      overflow: auto; 
      width: 600px;
    }
  </style>
 </head>
<body>
  <button onclick="generate()">Generate PDF</button>
  <div id="html-2-pdfwrapper">
    <!-- html content goes here -->
  </div>
  <script src='dist/jspdf.min.js'></script>
</body>
</html>

From the code, it should be self explanatory, but I will go over the different sections of the HTML code structure.

The head section holds the page title and the stylesheet for the page.
The HTML body section holds the following:
• Button: the generate PDF button on click will trigger the generate function which we have yet to create.
<button onclick="generate()">Generate PDF</button>

• The next section will hold all of our HTML content we want to convert to PDF.
<div id="html-2-pdfwrapper"> <!-- html content goes here --> </div>

Writing the JavaScript function to handle the HTML conversion.

Global Variables

<script>
//Global Variable Declaration
var base64Img = null;
margins = {
  top: 70,
  bottom: 40,
  left: 30,
  width: 550
};

/* append other function below: */

</script>

Generate PDF Function

generate = function()
{
  var pdf = new jsPDF('p', 'pt', 'a4');
  pdf.setFontSize(18);
  pdf.fromHTML(document.getElementById('html-2-pdfwrapper'), 
    margins.left, // x coord
    margins.top,
    {
      // y coord
      width: margins.width// max width of content on PDF
    },function(dispose) {
      headerFooterFormatting(pdf)
    }, 
    margins);
    
  var iframe = document.createElement('iframe');
  iframe.setAttribute('style','position:absolute;right:0; top:0; bottom:0; height:100%; width:650px; padding:20px;');
  document.body.appendChild(iframe);
  
  iframe.src = pdf.output('datauristring');
};

Code Explanation

Inside generate function, we instantiate the jsPDF Object as PDF.
The jsPDF has an API Endpoint called fromHTML with the following parameters:

jsPDFAPI.fromHTML = function (HTML, x, y, settings, callback, margins)

The HTML is the HTML content you want to convert.
X is the horizontal distance from the x-coordinate of your PDF layout page.
Y is the vertical distance from the y-coordinate of your PDF layout page.

Callback function is used to perform extra functionality on the pages generated. We are going to take advantage of the callback function to add page numbering and header to our pages later on.

We are also creating an iframe element to display our generated PDF.

function headerFooterFormatting(doc)
{
    var totalPages  = doc.internal.getNumberOfPages();

    for(var i = totalPages; i >= 1; i--)
    { //make this page, the current page we are currently working on.
        doc.setPage(i);      
                      
        header(doc);
        
        footer(doc, i, totalPages);
        
    }
};

Code Explanation

The function is used to add extra data to each page of the PDF and so it will be called on the fromPDF callback parameters. It takes a jsPDF object.

Header Function

function header(doc)
{
    doc.setFontSize(30);
    doc.setTextColor(40);
    doc.setFontStyle('normal');
  
    if (base64Img) {
       doc.addImage(base64Img, 'JPEG', margins.left, 10, 40,40);        
    }
      
    doc.text("Report Header Template", margins.left + 50, 40 );
  
    doc.line(3, 70, margins.width + 43,70); // horizontal line
};

Load the header image and convert to base64

imgToBase64('octocat.jpg', function(base64) {
    base64Img = base64; 
});

Image Converter Function

// You could either use a function similar to this or preconvert an image with, for example, http://dopiaza.org/tools/datauri
// http://stackoverflow.com/questions/6150289/how-to-convert-image-into-base64-string-using-javascript
function imgToBase64(url, callback, imgVariable) {
 
    if (!window.FileReade) {
        callback(null);
        return;
    }
    var xhr = new XMLHttpRequest();
    xhr.responseType = 'blob';
    xhr.onload = function() {
        var reader = new FileReader();
        reader.onloadend = function() {
      imgVariable = reader.result.replace('text/xml', 'image/jpeg');
            callback(imgVariable);
        };
        reader.readAsDataURL(xhr.response);
    };
    xhr.open('GET', url);
    xhr.send();
};

Download complete project now: Codementor HTML to PDF.zip

Please, feel free to share your comments and help improve the code wherever possible.

Discover and read more posts from Ameh Joseph
get started