How to easy generate beautifull pdf using node, puppeteer and handlebars.

No more pain work on rendering texts, images and geometries using core pdf libraries.

Rodolfo Marcos
5 min readJan 7, 2019

In this post, we’ll learn how to generate beautifull pdf based on HTML Web Pages. The beauty of this is that you can even style the pdf using CSS.

Make organized, well-structured pdf files

When dealing with pdf generation on server-side there is some pain developers may struggle with. Most of libraries require a low-level manipulation on the pdf page being created. Instructions as “go to new line”, “change font size”, “create a point on (x,y)” are common. In addition to generating overwork, the use of these libraries can make more complex layouts infeasible.

PDF files on a system often plays important roles as showing to users relevant data that can be downloaded, exported or sent via e-mail. Creating a great, simple and clean layout is important for your users — as for you.

Design is not just what it looks like and feels like. Design is how it works — Steve Jobs

In this article we’ll build an application that renders a basic student’s report. It has some basic information about the student, the logo picture of the school and the student’s photo.

We will build an app that will:

  1. Import necessary libraries
  2. Construct the HTML/CSS template
  3. Render the template based on a JSON Object and generate pdf

Let’s Begin!

Create a npm project by running:

npm init your-project-name

Import necessary libraries

The libraries we’ll use in this project are:

  • fs: The default file system library that came with nodeJs.
  • path: The default path library that came with nodeJS. It is usefull when dealing with path to resources and navigate through folders in our system.
  • puppeteer: puppeteer is a library that provides a high-level API to access and control Chrome or Chromium over the DevTools Protocol. It will be used to render our HTML template page.
  • handlebars: handlebars is a library that permits compile a dynamic Mustache templating language into a HTML.

Go inside the project folder you created and install the libraries running the following commands:

npm install --save puppeteer
npm install --save handlebars

Let’s create a file named pdf-generator.js, open it and import the libraries you installed in the beggining of the file.

const fs = require("fs");
const path = require("path");
const puppeteer = require('puppeteer');
const handlebars = require("handlebars");

After that we have our libraries ready to use.

Construct the HTML/CSS template

Follows an example of a full template, you can make yours as you want. Note that we’re using the Mustache Template Language, all the {{var}} pieces in the code will be replaced at compile time by the values we pass to it. You can find more information for enrich your template in: https://handlebarsjs.com/.

Caution: Do not use Hexadecimals for colors in the template, the “#” character causes conflict when compiling the template. Use the RGB notation; instead of #FFFFFF use RGB(255,255,255) for example.

The template of our pdf file

The next step is to create the logic to render the template.

Render the template based on a JSON Object and generate pdf

As we said in the previous section, there are variables in the HTML template that are populated dynamically. So we must build in our application a json object with the same properties used in the template. In our case a valid object could be:

Note that with handlebars you can make loops, conditionals and other instructions to create a template, we are just using the basics in this tutorial but you can explore all the features as you need.

Once we have our template created, we have to use it to render our pdf, I’ll show you the code and then explain the mainly concepts.

The logic to render the pdf file

I prefer to use async/await logic instead of Callbacks or Promises but feel free to use which you prefer. When using async/await you must code your logic inside an async function (or other asynchrnous mechanism) as shown in the first line.

When the function createPDF is called, in line 3 we’re reading the HTML template file and putting it into a variable named templateHtml, in the next line handlebars makes a compilable out of it, and finally in line 5 we pass the object data as a parameter and compiles it — the return is a String with the full compiled HTML with the variables filled (Cool).

In line 10 we construct the path where the generated pdf file will be stored. Which is in a folder called pdf, you can organize it as you want.

In line 12 we create the configuration object for our pdf. It contains important informations such as document width, header and footer templates to be placed on every page and the margins. There are cool other configurations that can be checked in: https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#pagepdfoptions

In line 25 we start a browser with puppeteer, at 30 we start a new blank page and then at line 32 we passed our compiled HTML String to the page we just created, this causes the HTML String we have to be opened and rendered by the browser. The waitUntil command is used so that the page and all its external dependencies are waited to be loaded, without this command your pdf may be broken, since external files may not have been loaded yet.

Finishing in the line 36 we run the command to generate the pdf of the page that was opened by the Browser using the options we configured earlier, and then we close the Browser using the browser.close() command.

Now we have all the template and logic configured, let’s run the application and check the results!

Run the application with the following command:

node pdf-generator.js

Congratulations!! You have created a Node.js app that can generate beatifull pdf documents that will make your users happier thant old-designed, confusing layouts.

You can clone the repository of this article in: https://github.com/rodolfo-marcos07/pdf-generator-sample and contribute if you want to!

You can connect me via Gmail: rodolfo.marcos07@gmail.com or Linkedin: https://www.linkedin.com/in/rodolfo-marcos-41ab3198/

Thank you so much! Pardon my mistakes, I hope it will be usefull for you.

--

--