v0.20.1
- 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 fonts) instead of the browser bundle
v0.20.0
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.
The 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);
Experimental 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
- [BREAKING CHANGE] Remove the virtual file system (
pdfkit/virtual-fs). Browser builds no longer depend onfs: useregisterFileto registerUint8Arraydata under a path, pass aUint8ArrayorArrayBufferdirectly toregisterFont,imageandfile, or pass a data URL directly toimageandfile - Add
registerFile(path, data, options)to globally register in-memory files in Node and browsers, with optionalbirthtimeandctimemetadata. Passingundefinedas data unregisters the path - [BREAKING CHANGE] Export
PDFDocument,LineWrapperandregisterFileas named exports from the main Node and browser entry points while preserving the defaultPDFDocumentexport - Add experimental
toBlob(document)andtoBytes(document)output helpers underpdfkit/output - Accept already-parsed fontkit
Fontinstances indoc.font()andregisterFont - Load the PDF/A ICC profile from disk only when needed in Node, while continuing to bundle it in browser builds
- Add tools to convert raw AFM standard-font definitions into parsed or compact runtime JavaScript modules
- [BREAKING CHANGE] Use generated standard-font data instead of parsing raw AFM definitions at runtime. Node loads font metrics lazily; browser applications must import each font they use from
pdfkit/standard-fonts/*and register it withregisterStdFonts() - [BREAKING CHANGE] Restrict AcroForm options to documented mappings and explicit escape hatches.
- [BREAKING CHANGE] Stop automatically uppercasing annotation option keys.
- [BREAKING CHANGE] Throw from
addNamedEmbeddedFilewhen no ref is given, instead of writing an unparseableundefinedtoken into the/EmbeddedFilesname tree - Do not mutate options passed to
doc.annotate()and its convenience methods (link, note, strike, lineAnnotation, rectAnnotation, ellipseAnnotation, textAnnotation, fileAnnotation) - Persist font options when adding a new page. Fixes #1739
- Use
Uint8Arrayinstead of Node'sBufferinternally - Fix
datetext field formatting emitting invalid JavaScript, so the format was never applied. Fixes #1546 - Fix
indentAllLinesapplying the indent again on every paragraph and every page break, and keep it applied across continued text. Fixes #1606 - Fix a hole in a sparse array being skipped entirely, which shifted every later entry down one
- Encrypt strings inside name trees. Fixes #1513
0.19.1
- Fix RGB JPEG embedded as DeviceGray (0.19.0 regression) (#1734)
0.19.0
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
- [BREAKING] Bump node version requirement to 20+
- [BREAKING] Bump minimum supported browsers to Firefox 115, iOS/Safari 16
- Fix text with input x as null
- Add opacity option to
doc.image()to control image transparency - Fix corrupted PDF when mixing standard and embedded fonts that share postscript name
- Fix PDF/UA compliance issues in kitchen-sink-accessible example
- Add bbox and placement options to PDFStructureElement for PDF/UA compliance
- Extend
roundedRectwithborderRadiusas number for all corners or per-corner array (CSS order) - Fix accessibility: scope in TH element
- Fix PDF Name escaping for spot colors with spaces (#1644)
0.18.0
- Fix garbled text copying in Chrome/Edge for PDFs with >256 unique characters (#1659)
- Fix Link accessibility issues
- Fix Table Accessibility Issue: Operator CS/cs not allowed in this current state
- Fix Interlaced PNG with indexed transparency rendered incorrectly
- Fix SVG path parser incorrectly handle arc flags without separators
- Add pageLayout option to control how pages are displayed in PDF viewers
- Preserve existing PageMode instead of overwriting when adding outlines
- Add userUnit option for custom page units (PDF 1.6)
- Support outlines that jump to specific page positions with custom zoom level
- Add robust handling of null byte padding in JPEG images
- Replace outdated jpeg-exif with minimal implementation
- Replace outdated crypto-js with maintained small alternatives
- Fix issue with indentation with
indentAllLines: truewhen a new page is created
0.17.1
- Fix null values in table cells rendering as
[object Object] - Fix further LineWrapper precision issues
- Optmize standard font handling. Less code, less memory usage
0.17.0
- Fix precision rounding issues in LineWrapper
- Fix fonts without a postscriptName
- Add support for dynamic sizing
- Add support for rotatable text
- Fix page cascade options when text overflows
- Add table generation
- Fix y position when using
image()without x and y coordinates - Improve Prettier configuration
0.16.0
- Update fontkit to 2.0
- Update linebreak to 1.1
- Add support for spot colors
- Add support to scale text horizontally
- Add an option to keep the indentation after a new line starts and allow to indent a whole paragraph/text element
- Add
Nameproperty for set custom icon fornote() - Fix sets tab order to "Structure" when a document is tagged
- Fix font cache collision for fonts with missing postscript name or bad TTF metadata or identical metadata for different fonts
- Fix for embedding fonts into PDF (font name must not contain spaces)
- Fix measuring text when OpenType features are passed in to .text()