What for? I needed this to send text-only faxes through Twilio's Fax API, which required a PDF link.
Why pdfmake? Why not puppeteer on a server/lambda that renders any webpage into PDF? pdfmake's layout language has much more features for paged documents, and I don't need the fancy stuff html+css+js affords. pdfmake has tables and columns and good font support. Having worked with html to print layout I know how much of a pain in the ass it is.
On to the documentation
First of all pdfmake is, I believe a wrapper around pdfkit. pdfkit does not have any convenient features to generate documents other than one-column stuff. pdfmake can do tons, but an API doc is non-existence.
The best place to discover features and usage is the README and the Playground. Neither is comprehensive.
The docs also assume browser usage. Server side is VERY different. Here's what I gathered from various Github Issues and past experience with pdfkit, which also does not have a comprehensive documentation. This script creates a pdf as binary, which you can send directly to client.
You need to download Roboto fonts from google fonts and place it in the project folder of your script, then reference them in the fonts object in the code below.
Roboto is the default font used, so if you produce a pdf document without specifying any font, that's what it will use. So having Roboto avoids all bugs caused by not having a fallback font.
import {Base64Encode} from 'base64-stream'
const PdfPrinter = require('pdfMake/src/printer')
const fonts = {
Roboto: {
normal: './fonts/Roboto-Medium.ttf',
bold: './fonts/Roboto-Black.ttf',
italics: './fonts/Roboto-MediumItalic.ttf',
bolditalics: './fonts/Roboto-BlackItalic.ttf'
}
};
const printer = new PdfPrinter(fonts)
const docDef = {content: 'Hello World'}
const doc = printer.createPdfKitDocument(docDef)
const stream = doc.pipe(new Base64Encode())
doc.end()
let finalString = ''
stream.on('data', function(chunk) {
finalString += chunk;
});
stream.on('end', function() {
console.log('binary pdf: ', finalString)
});
For an express server, simply send respond with finalString in the body and set Content-Type to application/pdf. Still need help? search for "responding with pdf binary in express". There are many solutions.
I use it on a AWS Lambda via serverless nodejs starter kit. It cannot work with callbacks, just promises (there may be a solution but you don't need callbacks). You can wrap stream.on in a new Promsise
return new Promise((resolve, reject) => {
stream.on('end', function() {
resolve(finalString)
});
})
Also AWS Lambda by default does not support binary response, so you need a plugin called serverless-apigw-binary. But the plugin doesn't have instructions for PDF binary, which is:
custom:
apigwBinary:
types:
- '*/*'
Having application/pdf as types does not work here for reasons I don't know.
This configuration can cause bugs with normal lambda functions (e.g. CORS won't work for your other functions, I don't know about the pdf function itself, you may test). So you should not have other functions that deal with text responses in the same project. The underlying reason is that all functions in the same project share an API Gateway, which is now configured to treat all content types as binary.
That's all the missing docs for the project I needed to do.
Contact me if you want a working example.
Top comments (2)
I would add that if anybody doesn't use Serverless, then it possible to add fonts to a layer and then access the fonts by e.g.:
const fonts = {
Roboto: {
normal: '/opt/Roboto-Regular.ttf',
bold: '/opt/Roboto-Medium.ttf',
italics: '/opt/Roboto-Italic.ttf',
bolditalics: '/opt/Roboto-MediumItalic.ttf'
}
};
Docs:
docs.aws.amazon.com/lambda/latest/...
Hi Dan,
Wonderful article .. In fact I have a use-case of using pdfmake with AWS Lamda to generate PDF files. Can you share the working example as a reference.
my email ajay.ep@gmail.com
Happy New Year !