NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
npm · #4790 most downloaded on npm
A JavaScript PDF generation library
Last release 1 months ago
30 Aug 2026
Ships unpredictably
gaps range from 2 weeks to 2.0 years
Some releases are documented
notes for 23 of the last 60 stable releases
Nothing withdrawn
no release was ever pulled
15 years old
62 releases · first in 2011
Fix bundlers and file tracers packing the ESM copies of the standard font metrics instead of the CommonJS ones the Node build actually loads, which le
Cannot find module errors for every standard font at runtime, by resolving the internal #standard-fonts/* mapping to a single file under all conditionsdoc.file() throwing when the same in-memory attachment is embedded twice under one name, because the creation and modified dates the deduplication check compares are absent for sources that are not read from diskdoc.text() throwing unsupported number: NaN when lineBreak: false is combined with underline, strike, link or goTo, because the rendered width was computed from the line wrapper's measurements, which are never produced when wrapping is disabledOne column per quarter.
Add a Node ESM build so import 'pdfkit' in Node resolves to the Node build (real file system, native zlib, Node streams, self-registering standard fon
import 'pdfkit' in Node resolves to the Node build (real file system, native zlib, Node streams, self-registering standard fonts) instead of the browser bundle[BREAKING CHANGE] Remove the virtual file system ( pdfkit/virtual-fs ). Browser builds no longer depend on fs : use registerFile to register Uint8Arra…
TLDR: "A PDF generation library for Node.js" -> "A JavaScript PDF generation library"
Standard font support has been rewritten to use pre-parsed font metrics instead of parsing raw AFM definitions at runtime. The new approach is more efficient (less runtime overhead and memory usage) and reduces the size of the browser bundle significantly. Standalone build which bundles all standard fonts is down to 1.3MB from 2.3MB.
In Node, font metrics are loaded lazily as before, while in browser builds, except the standalone one, each font must be imported from pdfkit/standard-fonts/* and registered with registerStdFonts(). Previously, to use a standard font in browser was necessary to use a bundler.
See usage example in output helpers section below.
Buffer is no longer used internally. Uint8Array is now the minimum denominator for binary data in both Node and browsers. Since Buffer is a Uint8Array subclass, this change is fully backward compatible.
Native fs, zlib and ReadableStream are conditionally imported only in Node. Browser builds use minimal implementations. This approach gives us the best of both worlds: Node builds still use the native modules, while browser ones are portable.
Many thanks to @diegomura for his help in removing the Node specific dependencies.
registerFile APIThe new registerFile(path, data) API allows registering in-memory files globally. The data argument must be a Uint8Array. Passing undefined as data unregisters the path.
In browsers, it can be used as a simplified virtual file system. In Node, this is useful for registering fonts, images and other resources that are not available on disk. The native file system is still used when the path is not registered.
import { PDFDocument, registerFile } from 'pdfkit';
const response = await fetch('/fonts/Roboto-Regular.ttf');
const fontData = new Uint8Array(await response.arrayBuffer());
registerFile('fonts/Roboto-Regular.ttf', fontData);
const doc = new PDFDocument();
// register an alias for the font path
doc.registerFont('Roboto', 'fonts/Roboto-Regular.ttf');
// or use the path directly
doc.font('fonts/Roboto-Regular.ttf');
// Optionally unregister the path when it is no longer needed.
registerFile('fonts/Roboto-Regular.ttf', undefined);toBytes and toBlob output helpersExperimental toBytes(document) and toBlob(document) output helpers are available under pdfkit/output. They return a Promise that resolves to a contiguous Uint8Array or a Blob, respectively. These helpers are useful when a binary API, worker or parser needs one contiguous Uint8Array or a Blob.
import { PDFDocument, registerStdFonts } from 'pdfkit';
import Helvetica from 'pdfkit/standard-fonts/Helvetica';
import HelveticaBold from 'pdfkit/standard-fonts/HelveticaBold';
import { toBlob } from 'pdfkit/output';
registerStdFonts(Helvetica, HelveticaBold);
const doc = new PDFDocument();
const output = toBlob(doc);
// Add your content to the document here, as usual.
doc.end();
const blob = await output;
const url = URL.createObjectURL(blob);
iframe.src = url;
// Revoke the URL when the iframe no longer needs the PDF.
// URL.revokeObjectURL(url);Added named exports for PDFDocument from the main Node and browser entry points while preserving the default PDFDocument export. This will make it easier to transition to ESM in the future. LineWrapper and registerFile are also exported as named exports.
by @blikblum (written by hand)
CHANGELOG
pdfkit/virtual-fs). Browser builds no longer depend on fs: use registerFile to register Uint8Array data under a path, pass a Uint8Array or ArrayBuffer directly to registerFont, image and file, or pass a data URL directly to image and fileregisterFile(path, data, options) to globally register in-memory files in Node and browsers, with optional birthtime and ctime metadata. Passing undefined as data unregisters the pathPDFDocument, LineWrapper and registerFile as named exports from the main Node and browser entry points while preserving the default PDFDocument exporttoBlob(document) and toBytes(document) output helpers under pdfkit/outputFont instances in doc.font() and registerFontpdfkit/standard-fonts/* and register it with registerStdFonts()addNamedEmbeddedFile when no ref is given, instead of writing an unparseable undefined token into the /EmbeddedFiles name treedoc.annotate() and its convenience methods (link, note, strike, lineAnnotation, rectAnnotation, ellipseAnnotation, textAnnotation, fileAnnotation)Uint8Array instead of Node's Buffer internallydate text field formatting emitting invalid JavaScript, so the format was never applied. Fixes #1546indentAllLines applying the indent again on every paragraph and every page break, and keep it applied across continued text. Fixes #1606Fix RGB JPEG embedded as DeviceGray (0.19.0 regression)
Highlights in this release are the bump in minimum supported environments (quite conservative), reduce of Node js specific dependencies (EventEmitter,
Highlights in this release are the bump in minimum supported environments (quite conservative), reduce of Node js specific dependencies (EventEmitter, Buffer), more granular image transparency and roundedRect options and the usual quality of life fixes
doc.image() to control image transparencyroundedRect with borderRadius as number for all corners or per-corner array (CSS order)Fix garbled text copying in Chrome/Edge for PDFs with >256 unique characters
indentAllLines: true when a new page is createdFix rendering lists that spans across pages
Fix null values in table cells rendering as [object Object]
[object Object]Fix precision rounding issues in LineWrapper
image() without x and y coordinatesAdd support to scale text horizontally
Name property for set custom icon for note()Fix index not counting when rendering ordered lists
Fix browserify transform sRGB_IEC61966_2_1.icc file
Fix for line breaks in list items
write methodAdd support for PDF/A-1b, PDF/A-1a, PDF/A-2b, PDF/A-2a, PDF/A-3b, PDF/A-3a
Add support for PDF/A-1b, PDF/A-1a, PDF/A-2b, PDF/A-2a, PDF/A-3b, PDF/A-3a
Update crypto-js to v4.2.0 (properly fix security issue)
Add support for EXIF orientation on JPEG images (#626 and #1353)
- Add tiling pattern support
Remove examples from published package
Fix for PDF accessibility check.
Update crypto-js to v3.3 (fix security issue)
Fix Buffer() is deprecation warning
forms.md to generate documentation filesFix infinite loop when an individual character is bigger than the width of the text.
Fix links to pages within the document
dash(...) is passed invalid lengthsFix setting printing permission
Convert to code base from coffescript to ES6+
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Your coding agent can read these notes before it upgrades. Set up the MCP server →