Skip to content

feat: use fflate instead of zlib in the browser build - #1760

Open
diegomura wants to merge 1 commit into
masterfrom
diegomura/pdfkit-fflate-zlib
Open

feat: use fflate instead of zlib in the browser build#1760
diegomura wants to merge 1 commit into
masterfrom
diegomura/pdfkit-fflate-zlib

Conversation

@diegomura

@diegomura diegomura commented Aug 9, 2026

Copy link
Copy Markdown
Collaborator

What kind of change does this PR introduce?

Dependency/build change. The browser build imported Node's zlib and left the consumer's bundler to shim it, which usually means browserify-zlib and pako. The two deflateSync call sites now import #zlib, which resolves to native zlib in Node and to fflate's zlibSync everywhere else — the same node/default condition split the rollup builds already use. png-js is bumped to v2 (does the same thing internally, API-compatible), so browserify-zlib and pako are gone from the dependency tree.

fflate returns a plain Uint8Array instead of a Buffer, so _write checks instanceof Uint8ArrayBuffer is a subclass, so nothing changes for existing inputs, and PDFReference.write already did this. Checked with the full test suite and by rendering both bundles' output (text + PNG, plain and encrypted) in pdf.js.

Why fflate rather than browserify-zlib + pako?

  • No polyfill config. The browser bundle no longer imports a Node builtin at all. webpack 5 dropped automatic core-module polyfills and Vite never had them, so today import zlib either fails the build or silently needs a resolve.fallback entry from every user. fflate is just a package.
  • Size. pdfkit needs exactly one function. Tree-shaken and minified, fflate's zlibSync is 6.5 KB (3.2 KB gzipped); pako's deflate is 46.6 KB (14.9 KB gzipped) — it's CJS and doesn't tree-shake. browserify-zlib then layers Node's stream API on top of pako, dragging in buffer, stream, util and assert shims for what is a one-shot synchronous call.
  • Output and speed. On a ~1.9 MB compressible content stream, fflate emitted 16.6 KB against pako's 18.1 KB (~9% smaller) at comparable speed; on already-compressed PNG data it was 9ms against pako's 21ms.
  • Maintenance. browserify-zlib's last release is 0.2.0 and it pins pako ~1.0.5, while pako itself is on 3.x. fflate is ESM-first, typed, and actively released.
  • It is also what png-js v2 uses, so image decoding and stream compression now share one compressor instead of two.

Sizes measured with esbuild --bundle --minify, timings on Node 22.

Checklist:

  • Unit Tests
  • Documentation N/A
  • Update CHANGELOG.md
  • Ready to be merged

build-standalone still browserifies the Node CJS bundle, so that one file keeps pulling browserify-zlib. Building it from the browser entry is a separate change.

@diegomura diegomura changed the title Use fflate instead of zlib in the browser build feat: use fflate instead of zlib in the browser build Aug 9, 2026
@diegomura
diegomura requested a review from blikblum August 9, 2026 23:12
The browser build imported Node's `zlib` and relied on the consumer's
bundler to swap in a polyfill (browserify-zlib, which pulls pako). Route
the two `deflateSync` call sites through a `#zlib` subpath import that
resolves to native zlib under the `node` condition and to fflate's
`zlibSync` otherwise, matching the split the existing rollup builds
already use for `exports`.

Bump png-js to v2, which does the same thing internally, so
browserify-zlib and pako leave the dependency tree entirely.

fflate returns a plain Uint8Array rather than a Buffer, so `_write` now
gates on `instanceof Uint8Array` (Buffer is a subclass) instead of
`Buffer.isBuffer`, matching what `PDFReference.write` already did.
@diegomura
diegomura force-pushed the diegomura/pdfkit-fflate-zlib branch from 4be1660 to 10b89a6 Compare August 9, 2026 23:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant