modern-pdf-lib
v0.26.0
Published
A modern, WASM-accelerated PDF creation engine for every JavaScript runtime
Maintainers
Readme
The PDF engine for modern JavaScript
Create, parse, fill, merge, sign, and manipulate PDF documentsin Node, Deno, Bun, Cloudflare Workers, and every browser.
Get Started · Features · API · Why This?
Quick Start
npm install modern-pdf-libimport { createPdf, PageSizes, rgb } from 'modern-pdf-lib';
const doc = createPdf();
const page = doc.addPage(PageSizes.A4);
page.drawText('Hello from modern-pdf-lib', {
x: 50,
y: 750,
size: 28,
color: rgb(0.13, 0.13, 0.13),
});
const bytes = await doc.save(); // Uint8Array
const stream = doc.saveAsStream(); // ReadableStream
const blob = await doc.saveAsBlob(); // Blob (browsers)CDN (no install)
Use directly in the browser via CDN — no bundler required:
<script type="module">
import { createPdf, PageSizes, rgb } from 'https://cdn.jsdelivr.net/npm/modern-pdf-lib/dist/browser.mjs';
const doc = createPdf();
const page = doc.addPage(PageSizes.A4);
page.drawText('Hello from CDN!', { x: 50, y: 750, size: 24, color: rgb(0, 0, 0) });
const bytes = await doc.save();
console.log('PDF size:', bytes.length, 'bytes');
</script>Also available via:
- unpkg:
https://unpkg.com/modern-pdf-lib/dist/browser.mjs - esm.sh:
https://esm.sh/modern-pdf-lib
Script Tag (no modules)
For environments without ES module support, use the IIFE bundle which exposes a window.ModernPdf global:
<script src="https://cdn.jsdelivr.net/npm/modern-pdf-lib/dist/modern-pdf-lib.iife.js"></script>
<script>
const { createPdf, PageSizes, rgb } = ModernPdf;
const doc = createPdf();
const page = doc.addPage(PageSizes.A4);
page.drawText('Hello from script tag!', { x: 50, y: 750, size: 24, color: rgb(0, 0, 0) });
doc.save().then(function (bytes) {
console.log('PDF size:', bytes.length, 'bytes');
});
</script>Features
Create & Draw
- Pages, text, images, shapes, SVG paths
- TrueType & OpenType font embedding
- Automatic font subsetting
- JPEG / PNG image embedding
- Image optimization (JPEG recompression, dedup, grayscale)
- RGB, CMYK, grayscale colors
- Linear & radial gradients, tiling patterns
- Text layout (multiline, combed, auto-size)
Parse & Modify
- Load existing PDFs (encrypted too)
- Extract text with positions
- Fill & flatten AcroForm fields
- Merge, split, copy pages
- Add / remove / reorder pages
- Incremental saves
Secure & Compliant
- AES-256 / RC4 encryption & decryption
- Digital signatures (PKCS#7, visible/invisible, timestamps)
- CRL/OCSP revocation checking & certificate chain validation
- Incremental save with signature preservation
- Multi-signature chains, MDP certification, LTV archival
- Counter-signatures & field locking
- PDF/A-1b through PDF/A-3u validation
- Tagged PDF / PDF/UA accessibility
- Structure tree & marked content
- Redaction with content removal
Advanced
- QR codes & barcodes (9 formats)
- Table layout engine with pagination
- JPEG2000 (JPXDecode) image support
- WebP image embedding (lossy, lossless, alpha)
- TIFF image embedding (multi-page, CMYK, direct mapping)
- Image format auto-detection (PNG/JPEG/WebP/TIFF)
- Form field JavaScript evaluation & sandboxing
- Outlines / bookmarks
- Optional content layers (OCGs)
- File attachments & watermarks
- Linearization (fast web view)
- Browser helpers (download, blob, data URL)
- Service Worker & Web Worker support
- CLI:
npx modern-pdf optimize
Why modern-pdf-lib?
Runtimes
| Runtime | Version | Status | |:---|:---|:---:| | Node.js | 25.7+ | Fully supported | | Deno | 1.40+ | Fully supported | | Bun | 1.0+ | Fully supported | | Cloudflare Workers | — | Fully supported | | Chrome / Edge | 109+ | Fully supported | | Firefox | 115+ | Fully supported | | Safari | 16.4+ | Fully supported |
API Surface
import { createPdf, loadPdf, PageSizes } from 'modern-pdf-lib';
// Create from scratch
const doc = createPdf();
doc.setTitle('Invoice #1042');
doc.setLanguage('en');
// Load existing
const existing = await loadPdf(pdfBytes, { password: 'secret' });
// Save
const bytes = await doc.save();
const stream = doc.saveAsStream();const page = doc.addPage(PageSizes.LETTER);
page.drawText('Hello', { x: 50, y: 700, size: 24 });
page.drawImage(imageRef, { x: 50, y: 400, width: 200, height: 200 });
page.drawRectangle({ x: 50, y: 300, width: 100, height: 50, color: rgb(0, 0.5, 1) });
page.drawCircle({ x: 200, y: 325, radius: 25 });
page.drawSvgPath('M 0 0 L 100 0 L 50 80 Z', { x: 300, y: 300 });// Standard fonts (no embedding needed)
const helvetica = doc.embedStandardFont('Helvetica');
// Custom TrueType / OpenType
const fontBytes = await readFile('Inter.ttf');
const inter = await doc.embedFont(fontBytes, { subset: true });
page.drawText('Custom font', { x: 50, y: 500, font: inter, size: 18 });const form = doc.getForm();
form.getTextField('name').setText('Jane Doe');
form.getCheckbox('agree').check();
form.getDropdown('country').select('Canada');
form.flatten(); // Burn values into page contentimport { mergePdfs, splitPdf, copyPages } from 'modern-pdf-lib';
const merged = await mergePdfs([pdf1Bytes, pdf2Bytes]);
const pages = await splitPdf(pdfBytes, [
{ start: 0, end: 4 }, // Pages 1-5
{ start: 5, end: 9 }, // Pages 6-10
]);const bytes = await doc.save({
userPassword: 'reader',
ownerPassword: 'admin',
permissions: { printing: true, copying: false },
});import { signPdf, verifySignatures } from 'modern-pdf-lib';
const signed = await signPdf(pdfBytes, 'Signature1', {
certificate: certDer,
privateKey: keyDer,
reason: 'Approved',
appearance: { // optional visible signature
rect: [50, 50, 200, 80],
fontSize: 10,
},
});
const results = await verifySignatures(signed);import { loadPdf, extractTextWithPositions } from 'modern-pdf-lib';
const doc = await loadPdf(pdfBytes);
const page = doc.getPage(0);
const items = extractTextWithPositions(page.getOperators(), page.getResources());
for (const item of items) {
console.log(`"${item.text}" at (${item.x}, ${item.y})`);
}import { loadPdf, initWasm, optimizeAllImages, deduplicateImages } from 'modern-pdf-lib';
await initWasm({ jpeg: true });
const doc = await loadPdf(pdfBytes);
// Deduplicate identical images
const dedupReport = deduplicateImages(doc);
// Optimize all images (JPEG recompression)
const report = await optimizeAllImages(doc, {
quality: 75,
progressive: true,
autoGrayscale: true,
});
console.log(`${report.optimizedImages}/${report.totalImages} images optimized`);
console.log(`Savings: ${report.savings.toFixed(1)}%`);
const optimized = await doc.save();CLI:
npx modern-pdf optimize report.pdf report-opt.pdf --quality 60 --grayscale --dedup -vimport { createPdf, PageSizes, professionalPreset, applyPreset } from 'modern-pdf-lib';
const doc = createPdf();
const page = doc.addPage(PageSizes.A4);
page.drawTable(applyPreset(professionalPreset(), {
x: 50,
y: 750,
width: 495,
headerRows: 1,
rows: [
{ cells: ['Product', 'Qty', 'Price', 'Total'] },
{ cells: ['Widget A', '10', '$5.00', '$50.00'] },
{ cells: ['Widget B', '25', '$3.50', '$87.50'] },
{ cells: [{ content: 'Grand Total', colSpan: 3, align: 'right' }, '$137.50'] },
],
columns: [{ flex: 2 }, { width: 60 }, { width: 80 }, { width: 80, align: 'right' }],
}));import { createPdf, PageSizes } from 'modern-pdf-lib';
const doc = createPdf();
const page = doc.addPage(PageSizes.A4);
// QR code
page.drawQrCode('https://example.com', { x: 50, y: 700, size: 120 });
// Barcodes (Code 128, EAN-13, UPC-A, Code 39, ITF, PDF417, Data Matrix)
import { encodeCode128, encodeEan13, renderStyledBarcode } from 'modern-pdf-lib';
const barcode = encodeCode128('ABC-12345');
const ops = renderStyledBarcode(barcode, { x: 50, y: 500, height: 60 });import { enforcePdfA, checkAccessibility } from 'modern-pdf-lib';
// Enforce PDF/A-2b compliance
const archival = enforcePdfA(pdfBytes, '2b');
// Check accessibility
const issues = checkAccessibility(doc);
for (const issue of issues) {
console.log(`[${issue.severity}] ${issue.code}: ${issue.message}`);
}Install
# npm
npm install modern-pdf-lib
# pnpm
pnpm add modern-pdf-lib
# yarn
yarn add modern-pdf-lib
# bun
bun add modern-pdf-lib
# deno
import { createPdf } from 'npm:modern-pdf-lib';WASM Acceleration
All WASM modules are optional. Without them, identical output is produced using pure-JS fallbacks.
import { initWasm } from 'modern-pdf-lib';
await initWasm({
deflate: true, // Faster compression
png: true, // Faster PNG decoding
fonts: true, // Faster font subsetting
jpeg: true, // JPEG encode/decode for image optimization
});| Module | Purpose | Speedup | |:---|:---|:---:| | libdeflate | Stream compression | ~2x | | png | PNG image decoding | ~5x | | ttf | Font parsing & subsetting | ~3x | | shaping | Complex script layout | ~10x | | jbig2 | JBIG2 bilevel image decoding | ~3x | | jpeg | JPEG encode/decode for image optimization | Required |
Project Structure
modern-pdf-lib/
src/
core/ PDF document model, objects, writer, pages
parser/ PDF loading, text extraction, content streams
form/ AcroForm fields (7 types) + appearances
annotation/ 18 annotation types + appearance generators
accessibility/ Structure tree, marked content, PDF/UA checker
compliance/ PDF/A validation & enforcement
signature/ PKCS#7 signatures, timestamps, verification, CRL/OCSP
crypto/ AES-256, RC4, MD5, SHA-256/384/512
compression/ Deflate (fflate + optional WASM)
assets/ Font metrics/embed/subset, image embed, SVG
barcode/ QR, Code 128, EAN, UPC, Code 39, ITF, PDF417, Data Matrix
layout/ Table engine (spanning, pagination, presets, overflow)
browser/ Download helpers, Service Worker, Web Worker
layers/ Optional content groups (OCG)
outline/ Bookmarks / document outline
metadata/ XMP metadata, viewer preferences
wasm/ Rust crate sources (6 modules)
cli/ CLI tool (modern-pdf optimize)
tests/ 4,282 tests across 199 suites
docs/ VitePress documentationContributing
git clone https://github.com/ABCrimson/modern-pdf-lib.git
cd modern-pdf-lib
npm install
npm test # 4,282 tests
npm run typecheck # TypeScript 6.0 strict
npm run build # ESM + CJS + declarationsLicense
MIT © 2026
