pdf-lib
`pdf-lib` is a robust and actively maintained JavaScript library designed for creating and modifying PDF documents in any modern JavaScript environment, including Node.js, browsers, Deno, and React Native. Currently at version 1.17.1, it receives frequent minor and patch releases, with major versions introducing significant architectural changes. A key differentiator of `pdf-lib` from many other open-source PDF libraries is its comprehensive support for *modifying* existing PDF documents, not just creating new ones. Its features include drawing text, images, and vector graphics, embedding fonts (with UTF-8 and UTF-16 support), managing pages (add, insert, remove, copy), creating and filling forms, and setting/reading document metadata and viewer preferences. The library is written in TypeScript, providing excellent type support for its users.
Common errors
-
TypeError: Cannot read properties of undefined (reading 'addPage')
cause Attempting to use methods on the `pdfDoc` object immediately after `PDFDocument.create()` or `PDFDocument.load()` without awaiting the Promise.fixEnsure `PDFDocument.create()` and `PDFDocument.load()` are awaited, as they return Promises since v1.0.0: `const pdfDoc = await PDFDocument.create();` -
The font '...' contains a bad /BBox. (Adobe Reader error)
cause Issues when processing complex or malformed PDFs, or specific font embedding problems leading to an invalid PDF structure after saving.fixValidate the original PDF with external tools (e.g., qpdf) if possible. Try simplifying the operations performed on the PDF. If re-saving an existing PDF, try `pdfDoc.save({ useObjectStreams: false })` as a workaround for some legacy viewer issues. Ensure embedded fonts are correctly registered and compatible. -
TypeError: pdfDoc.embedJPG is not a function
cause Using the old casing for image embedding methods after upgrading to `pdf-lib` v1.0.0 or later.fixRename `pdfDoc.embedJPG()` to `pdfDoc.embedJpg()` and `pdfDoc.embedPNG()` to `pdfDoc.embedPng()`. -
Standard fonts in pdf-lib cannot encode certain characters outside WinAnsi.
cause Attempting to draw text with characters not supported by the standard PDF fonts (e.g., `TimesRoman`, `Helvetica`), which primarily support the WinAnsi encoding.fixFor text containing characters outside WinAnsi (e.g., Unicode characters, emojis), embed a custom font that supports the required character set (e.g., `fontBytes = await fetch('/path/to/my-font.ttf').then(res => res.arrayBuffer()); const customFont = await pdfDoc.embedFont(fontBytes);`). Remember to install and register `@pdf-lib/fontkit` for custom fonts. -
TypeError: pdfDoc.catalog.getMaybe is not a function
cause The `getMaybe` method was removed in `pdf-lib` v1.0.0.fixReplace `getMaybe` calls with `get`. If looking up a string-based dictionary key, wrap the string in `PDFName.of()`: `pdfDoc.catalog.get(PDFName.of('AcroForm'))`.
Warnings
- breaking Version 1.0.0 introduced significant breaking changes. Key API methods such as `PDFDocument.create()`, `PDFDocument.load()`, and `pdfDoc.save()` became asynchronous and now return Promises. Additionally, `PDFDocumentFactory` was renamed to `PDFDocument`, and the page drawing API was simplified from content streams to direct methods (e.g., `page.drawText`). The `getMaybe` method was removed, and string-based dictionary lookups with `get` now require wrapping strings in `PDFName.of()`.
- gotcha Embedding custom fonts requires the separate `@pdf-lib/fontkit` package to be installed and registered with `pdfDoc.registerFontkit(fontkit)`.
- gotcha The `pdf-lib` bundle size, especially when including `@pdf-lib/fontkit` for custom font support, can be large. This is a consideration for browser-based applications where bundle size impacts load times.
- gotcha `pdf-lib` does not officially support modifying encrypted PDF documents. Attempting to load and save encrypted PDFs may result in unexpected behavior, errors, or corrupted output.
- breaking Image embedding methods `pdfDoc.embedJPG()` and `pdfDoc.embedPNG()` were renamed to `pdfDoc.embedJpg()` and `pdfDoc.embedPng()` respectively (lowercase 'g' and 'n') for consistency.
Install
-
npm install pdf-lib -
yarn add pdf-lib -
pnpm add pdf-lib
Imports
- PDFDocument
const PDFDocument = require('pdf-lib').PDFDocumentimport { PDFDocument } from 'pdf-lib' - rgb
import rgb from 'pdf-lib/lib/api/colors/rgb'
import { rgb } from 'pdf-lib' - StandardFonts
import { HELVETICA } from 'pdf-lib/lib/api/fonts/StandardFonts'import { StandardFonts } from 'pdf-lib' - PDFName
import { PDFName } from 'pdf-lib/es/core/objects/PDFName'import { PDFName } from 'pdf-lib'
Quickstart
import { PDFDocument, StandardFonts, rgb } from 'pdf-lib';
async function createPdf() {
// Create a new PDFDocument
const pdfDoc = await PDFDocument.create();
// Embed the Times Roman font
const timesRomanFont = await pdfDoc.embedFont(StandardFonts.TimesRoman);
// Add a blank page to the document
const page = pdfDoc.addPage();
// Get the width and height of the page
const { width, height } = page.getSize();
// Draw a string of text toward the top of the page
const fontSize = 30;
page.drawText('Creating PDFs with pdf-lib is awesome!', {
x: 50,
y: height - 4 * fontSize,
size: fontSize,
font: timesRomanFont,
color: rgb(0, 0.53, 0.71),
});
// Serialize the PDFDocument to bytes (a Uint8Array)
const pdfBytes = await pdfDoc.save();
// In a Node.js environment, you could write this to a file:
// import { writeFileSync } from 'node:fs';
// writeFileSync('example.pdf', pdfBytes);
// In a browser, you might open it in a new tab:
// const blob = new Blob([pdfBytes], { type: 'application/pdf' });
// const url = URL.createObjectURL(blob);
// window.open(url, '_blank');
console.log('PDF created successfully (or bytes generated).');
return pdfBytes;
}
createPdf().catch(console.error);