Node.js: how to create PDF files

Node.js: how to create PDF files

In Node.js we can create PDF files with ease.

In Node.js we can create PDF files with ease.

First, we need to install the required modules for this specific task:


npm install phantomjs-prebuilt --save
npm install html-pdf --save
npm install express-pdf --save

express-pdf is an Express middleware built upon html-pdf which, in turn, relies on PhantomJS. The main process is pretty similar to the mPDF library main routine: we need a rendering engine in order to turn the HTML and CSS code into a full PDF file. The main difference with PHP is that in Node we can use PhantomJS:

...a headless WebKit scriptable with a JavaScript API. It has fast and native support for various web standards: DOM handling, CSS selector, JSON, Canvas, and SVG.

Now we're going to create a basic helper class to be used as a support tool during the generation of the PDF file. Since we're using PhantomJS, all URIs pointing to CSS files and images must be absolute. Also, we should take an extra care while serving our contents over SSL because we have to make sure that our SSL certificate is valid. Self-signed certificates don't work in this particular case.


'use strict';

class PDF {
    constructor() {
        this.css = '';
        this.html = '';
    }


    setCSS(path) {
        // Absolute URL
        this.css = '<link rel="stylesheet" href="' + path + '" />';
    }

    build(str) {
        this.html += '<html><head>' + this.css + '</head><body><div id="pageContent">';
        this.html += str;
        this.html += '</div></body></html>';
    }
}

module.exports = PDF;

Then:


'use strict';

const express = require('express');
const pdf = require('express-pdf');
const mongoose = require('mongoose');

mongoose.Promise = Promise;
mongoose.connect('mongodb://user:password@127.0.0.1/db');

const Documents = require('./models/Documents');
const pdfClass = require('./lib/pdf');
const port = process.env.PORT || 3000;
const app = express();

app.use(pdf);

app.get('/export/:id', function(req, res) {
    let id = req.params.id;
    Documents.findById(id).then(function(doc) {
    
      let renderer = new pdfClass();
      
      let html = doc.content;
      
      renderer.setCSS('http://site.com/styles/pdf.css');
      renderer.build(html);

      res.pdfFromHTML({ // express-pdf kicks in
          filename: doc.title + '.pdf',
          htmlContent: renderer.html
      });
    }).catch(function(err) {
        // Error
    });
});

app.listen(port);

Our CSS file is a stylesheet designed for printing documents (the media type is print), but as a plus we can benefit from floating and positioning, though we should run some tests in order to get the desired results (we're basically dealing with length units and properties that we don't use often).


@media print {
    body {
        font-family: sans-serif;
        font-size: 12px;
    }
    p, pre, ol, ul, dl, blockquote {
        line-height: 20px;
        page-break-inside: avoid;
    }

    img {
        display: block;
        max-width: 100%;
        height: auto;
    }

    #pageContent {
        padding: 0 20pt;
    }
}