declarative-pdf
v1.1.0
Published
A tool for generating PDF documents from declarative HTML templates
Downloads
144
Maintainers
Readme
Table of contents
Features
repeating sections
- customizable headers, footers, and backgrounds for each pagesection variants
- define variants for headers, footers, and backgrounds for different pagespage size and orientation
- define page size by name or by width, height and ppimulti-page content
- content that spans across multiple pagespage numbering
- display page numbers within headers, footers or backgrounds
Motivation
Creating PDF documents programmatically can be a complex and time-consuming task. Traditional methods often require intricate control over layout and content, which can be cumbersome and error-prone. This tool aims to simplify this process by using declarative HTML elements to control layout and content. By leveraging puppeteer, it slices your template and generates PDF elements from it, which are then used to assemble the final PDF document.
[!NOTE] This approach allows you to focus on the content of your PDF, rather than the layout, making PDF generation more intuitive and efficient.
Installation
Install it locally in your project folder:
# using npm
npm install --save declarative-pdf
# or using yarn
yarn add declarative-pdf
# or using pnpm
pnpm add declarative-pdf
[!NOTE] This package supports both CommonJS and ES modules. So you can either
require
it orimport
it.
Usage examples
Create from HTML template string
// ./docs/examples/example-a.js
import puppeteer from 'puppeteer';
import DeclarativePDF from '../../dist/index.js';
import {read, write} from './utils.js';
(async () => {
const html = await read('example-simple.html');
const browser = await puppeteer.launch();
const pdf = new DeclarativePDF(browser);
const pdfBuffer = await pdf.generate(html);
await write('example-a.pdf', pdfBuffer);
await browser.close();
})();
Most basic example is to create a PDF from a string. This approach is useful when you want to let declarative-pdf handle opening and closing tabs, and you don't need to interact with the page directly.
Create from Puppeteer tab
// ./docs/examples/example-b.js
import puppeteer from 'puppeteer';
import DeclarativePDF from '../../dist/index.js';
import {read, write} from './utils.js';
(async () => {
const html = await read('example-simple.html');
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.setContent(html);
const pdf = new DeclarativePDF(browser);
const pdfBuffer = await pdf.generate(page);
await write('example-b.pdf', pdfBuffer);
await browser.close();
})();
Another way is when you already have a puppeteer tab open, and you want to generate a PDF from it. This approach is useful when you want to interact with the page directly, or when you want to use puppeteer's API to manipulate the page before generating a PDF.
Debug created PDF
// ./docs/examples/example-c.js
import puppeteer from 'puppeteer';
import DeclarativePDF from '../../dist/index.js';
import {read, write} from './utils.js';
(async () => {
const html = await read('example-simple-bgcolor.html');
const browser = await puppeteer.launch();
const pdf = new DeclarativePDF(browser, {debug: {timeLog: true, attachSegments: true, pdfName: 'example-c.pdf'}});
const pdfBuffer = await pdf.generate(html);
await write('example-c.pdf', pdfBuffer);
await browser.close();
})();
Building a complex template can lead to some unexpected results. CSS styles might not be applied as expected, or the layout might not be as you intended. Some backgrounds might interfere with the content, or headers and footers might not be positioned correctly.
To help you debug these issues, you can set the debug
option when creating a new DeclarativePDF
instance. This will attach individual sections from which the final PDF is constructed, as well as the timing logs for each step.
[!NOTE] Only some PDF readers can view attachments. This includes Adobe Acrobat Reader, Foxit Reader, and Firefox browser.
Complete examples
// ./docs/examples/mockup-pages.js
import puppeteer from 'puppeteer';
import DeclarativePDF from '../../dist/index.js';
import {read, write} from './utils.js';
(async () => {
const browser = await puppeteer.launch();
const pdf = new DeclarativePDF(browser);
for (const name of ['a4-72-multipage', 'a4-72-standard', 'a4-297-standard']) {
const html = await read(`${name}.html`);
const pdfBuffer = await pdf.generate(html);
await write(`${name}.pdf`, pdfBuffer);
}
await browser.close();
})();
Create from Express server
It is quite possible to plug this into your express server. For example, if you send POST request with template
as a string, this would respond with generated PDF file.
[!NOTE] Spinning up puppeteer browser can be an expensive operation, so it might be beneficial to keep browser instance running. DeclarativePDF will open and close tabs, but not the browser itself.
const express = require('express');
const DeclarativePDF = require('declarative-pdf');
const puppeteer = require('puppeteer');
(async function () {
const app = express();
const browser = await puppeteer.launch();
app.use(
express.urlencoded({
extended: true,
limit: '2000kb', // default limit is 100kb and templates can grow
})
);
async function generate(req, res) {
const template = req.body.template;
const name = req.body.name;
const filename = `${name}.pdf`;
const pdfBuffer = await new DeclarativePDF(browser).generate(template);
res.setHeader('Content-disposition', `inline; name="${name}"; filename="${filename}"`);
res.setHeader('Content-Type', 'application/pdf');
res.writeHead(200);
res.end(Buffer.from(pdfBuffer).toString('base64'));
}
app.post('/generate', generate);
const server = http.createServer(app);
server.listen(80);
})();
Template
Template structure:
<html>
<head>
<style>
/* Add any CSS styles here */
</style>
</head>
<body>
<document-page>
<page-background> /* Add any page-background content here */ </page-background>
<page-header> /* Add any page-header content here */ </page-header>
<page-body> /* Add any page-body content here */ </page-body>
<page-footer> /* Add any page-footer content here */ </page-footer>
</document-page>
</body>
</html>
This is the most basic template structure. Check out the template documentation for more advanced features.
Documentation
Further reading: