1.1.0 • Published 6 months ago

declarative-pdf v1.1.0

Weekly downloads
-
License
MIT
Repository
github
Last release
6 months ago

Declarative PDF visualization

Table of contents

Features

  • repeating sections - customizable headers, footers, and backgrounds for each page
  • section variants - define variants for headers, footers, and backgrounds for different pages
  • page size and orientation - define page size by name or by width, height and ppi
  • multi-page content - content that spans across multiple pages
  • page 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 or import 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.

Example C attachments location

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:

License

MIT

1.1.0

6 months ago

1.0.6

7 months ago

1.0.5

7 months ago

1.0.4

1 year ago

1.0.3

1 year ago

1.0.2

1 year ago

1.0.1

1 year ago

1.0.0

1 year ago

0.0.3

1 year ago

0.0.2

2 years ago

0.0.1

2 years ago