How to Generate a PDF with External Images Using Express.js
This tutorial shows how you can include images from an external URL with the pdfmake library.
The reason why I want to share this tutorial is because I’ve struggled with the implementation myself, and assumed that other developers may be in a similar conundrum. Visitors may want to print or save your content in portable format. Having a feature to generate a PDF dynamically with all the content’s images may be a great option.
At Nyonya Cooking, for example, we offer visitors a way download a PDF of each recipe. It includes a cover picture and pictures of the step-by-step cooking instructions. Since all pictures are hosted with an external CDN, we have to buffer them during the PDF creation process. If you found this tutorial, I am happy for you as I believe this to be the ultimate guide to embed images from external URLs on the fly into your PDF using pure server-side JavaScript (without saving all images locally beforehand).
I’m assuming that you use Express.js for your back-end framework. If not, you may need to adapt some parts of the code. The full source code explained in this tutorial can be found in my Gist.
Install Libraries
To get started, install the pdfmake and axios NPM libraries in your server-side project (if not already done):
$ npm install pdfmake axios
We need axios to load and temporarily cache data from an URL via its GET method.
Write the Controller
In the controller file in which you want to generate a PDF, include both libraries. The path
library is shipped with your Node installation, so it does not need to be specifically installed.
const Printer = require(‘pdfmake’)
const axios = require('axios')
const path = require('path')...
Instantiate a PDF printer by defining a few fonts. They can be referenced throughout the PDF. Here I am using Google Font’s “Roboto”, but your font selection is entirely up to you. Just note that you have to download any font being used in the PDF and place them in your project directory (in my case under /src/fonts/
):
...module.exports.pdf = async (req, res, next) => {
var printer = new Printer({
Roboto: {
normal: path.resolve('src', 'fonts', 'Roboto.ttf'),
bold: path.resolve('src', 'fonts', 'Roboto-Bold.ttf'),
}
}) ...}
Now that a pdfmake printer is instantiated, we can proceed with the actual loading and buffering of the external image by calling a GET method with axios like this:
module.exports.pdf = async (req, res, next) => { ... try {
var result = await axios.get('http://via.placeholder.com/350x150', {
responseType: 'arraybuffer'
})
} catch(err) {
return next(err.message)
} var image = new Buffer(result.data, 'base64') ...}
In the above code snippet, we are loading the axios GET results into a new result variable and specifically request the response type to be arrayBuffer. After that, we obtain the result’s binary data stream content using Node’s Buffer method with Base64 encoding.
Generate PDF Output
Let’s generate and output the PDF now:
module.exports.pdf = async (req, res, next) => { ... var doc = printer.createPdfKitDocument({
info: {
title: 'PDF with External Image',
author: 'Matt Hagemann',
subject: 'PDF with External Image',
},
content: [{
image: image,
width: 595, // Full A4 size width.
absolutePosition: { x: 0, y: 0 }
}],
defaultStyle: {
fontSize: 11,
font: 'Roboto', // The font name was defined above.
lineHeight: 1.2,
}
}) doc.end() res.setHeader('Content-type', 'application/pdf')
res.setHeader('Content-disposition', 'inline; filename="Example.pdf"')
doc.pipe(res)
}
I hope this simple tutorial will help you build awesome PDF files without loading and saving any external images locally. You can find the full code snippet in my Gist.