Skip to content

Releases: ABCrimson/modern-pdf-lib

v0.27.0 — Spot Colors, Streaming Parser, Builder Pattern, PDF/X, Plugins

08 Mar 18:40

Choose a tag to compare

What's New in v0.27.0

The biggest release yet — 29 roadmap items across patch, minor, and major tiers. 82 files changed, 19,668 insertions.

Major Features

  • Spot Colors & DeviceN — Separation and DeviceN color spaces for professional print production (spotColor(), deviceNColor(), CMYK↔RGB conversion)
  • Streaming PDF Parser — Process multi-GB PDFs without loading into memory via StreamingPdfParser (Web Streams API)
  • PdfDocumentBuilder — Fluent builder pattern with method chaining: .create().setTitle().addPage().save()
  • PDF/X Compliance — ISO 15930 validation and enforcement (validatePdfX, enforcePdfX, output intents)
  • Plugin System — Lifecycle hooks (onBeforeAddPage, onAfterSave, etc.) with PdfPluginManager + 3 built-in plugins (timestamp, metadata, accessibility)
  • Advanced Text Layout — Knuth-Plass line breaking, rich text spans, multi-column layout engine
  • Unified Async APIembedPng() is now async (matching embedJpeg()) for consistent API
  • PDF/UA Accessibility — Full validator with enforcement for accessible document creation

Minor Features

  • Header/Footer Engine — Template variables ({page}, {pages}, {date}, {title}), Roman/alpha numbering
  • Batch ProcessingprocessBatch(), batchMerge(), batchFlatten() with error strategies (fail-fast/continue/collect)
  • SVG Gradient Interpolation — Linear RGB interpolation with sRGB gamma correction
  • Enhanced Redaction — Overlay alignment options for redaction annotations
  • Page Labels & Bookmarks — Full API for document navigation
  • Inline WASM PreloadingpreloadInlineWasm() for eager initialization

Patch Fixes

  • ES2025+ modernization audit — zero anti-patterns across entire codebase
  • TypeScript strict mode compliance (exactOptionalPropertyTypes)
  • Operator builder helpers with modern camelCase aliases

Stats

  • 5,904 tests passing across 236 test suites
  • 82 files changed, +19,668 / -675 lines
  • 1 runtime dependency (fflate)

Full Changelog: https://github.com/ABCrimson/modern-pdf-lib/blob/master/CHANGELOG.md

v0.26.0

08 Mar 09:30

Choose a tag to compare

Added

  • PDF form flattening (src/form/formFlatten.ts):

    • flattenForm() — burn all form fields into page content, remove AcroForm
    • flattenField() / flattenFields() — flatten specific fields by name
    • FlattenOptions with preserveReadOnly support
  • Bookmarks / Outlines API (src/core/outlines.ts):

    • addBookmark() — add nested bookmarks with bold/italic/color/position
    • getBookmarks() — return the full bookmark tree
    • removeBookmark() / removeAllBookmarks() — bookmark management
  • Page labels (src/core/pageLabels.ts):

    • setPageLabels() — set label ranges (decimal, roman, alpha, custom prefix)
    • getPageLabels() / removePageLabels() — label management
    • Catalog integration with /PageLabels number tree
  • AES-256 encryption (PDF 2.0, V=5 R=6):

    • Full writer integration — doc.encrypt({ algorithm: 'aes-256' }) now works end-to-end
    • Algorithm 2.B key derivation (ISO 32000-2), SASLprep password normalization
    • %PDF-2.0 header for AES-256 encrypted documents
    • Trailer /Encrypt and /ID dictionary generation
  • SVG-to-PDF vector conversion (enhanced):

    • Text rendering with standard font resolution (Helvetica/Times/Courier families)
    • Fill-rule support (nonzero/evenodd → f/f*/B/B*)
    • Stroke properties: linecap, linejoin, miterlimit, dasharray, dashoffset
    • 60 new SVG conversion tests
  • Redaction application (src/annotation/applyRedactions.ts):

    • applyRedactions() — apply all redaction annotations (fill area, overlay text, remove annotation)
    • applyRedaction() — apply a single redaction by page/annotation index
  • Batch processing (src/batch/batchProcessor.ts):

    • processBatch() — process multiple PDFs with bounded concurrency
    • batchMerge() / batchFlatten() — parallel merge and flatten operations
    • Progress callbacks, error isolation, runtime-aware concurrency
  • PDF linearization (fast web view) — complete rewrite:

    • linearizePdf() — full page classification, hint tables, xref streams
    • delinearizePdf() — convert linearized PDF back to normal
    • getLinearizationInfo() — extract linearization parameters
    • Page offset and shared object hint tables per PDF spec §F
  • PDF/UA accessibility validation (src/accessibility/pdfUaValidator.ts):

    • validatePdfUa() — 12 checks: structure tree, language, title, headings, alt text, tables, lists, reading order, fonts, contrast, bookmarks, tab order
    • enforcePdfUa() — auto-fix language, title, structure tree, tab order
    • Page tab order API (setTabOrder())
  • WASM-by-default infrastructure:

    • getInlineWasmBytes() — decode base64-embedded WASM at runtime
    • scripts/generate-inline-wasm.ts — build-time code generation for inline WASM
    • generate:wasm-inline npm script
  • 586 new tests across 20+ test files:

    • Form flattening (26), outlines (24), page labels (31), AES-256 (55)
    • SVG conversion (60 new), redaction (17), batch processing (18)
    • Linearization (44), PDF/UA validation (51), inline WASM (17)
    • PDF/A enforcement (24), sRGB ICC profile (20), transparency flattener (18)
    • JPEG2000 tiles (25), JPEG2000 bit depth (41)
    • Object pool (26), PDF value helpers (24), validation (60)

Changed

  • WASM loader — now resolves dist/wasm/ layout first (npm consumers), falls back to dev src/wasm/ layout
  • tsdown config — removed duplicate IIFE config (esbuild script handles it)
  • Coverage threshold — branch coverage raised from 80% to 85%
  • Modernization audit — 32 changes across 14 source files:
    • 22× isNaN()Number.isNaN(), isFinite()Number.isFinite()
    • .indexOf() >= 0.includes() in generated Acrobat JS
    • Removed 4 as unknown casts via proper typing
    • .split('')[...str] spread for string iteration
  • PdfWriter — now async with full AES-256 encryption integration
  • API audit — comprehensive audit doc at docs/plans/api-audit-results.md
  • Test count: 5,135 tests passing across 227 test suites

Fixed

  • IIFE test failures from tsdown config removal
  • fieldVisibility test updated for .includes() modernization

Full Changelog: v0.25.0...v0.26.0

v0.25.0 — Test Coverage, Complete Exports, CI/Build Pipeline

08 Mar 08:24

Choose a tag to compare

Added

  • 267 new tests — comprehensive coverage for previously untested security-critical and core modules:

    • signatureVerifier.test.ts (23 tests) — digital signature verification with tamper detection, multi-sig, hash algorithms
    • sha256.test.ts (28 tests) — NIST FIPS 180-4 test vectors for SHA-256/384/512
    • textShaper.test.ts (32 tests) — Unicode text shaping, RTL, glyph mapping, WASM fallback
    • jpegEmbed.test.ts (42 tests) — SOF parsing, color space detection, Adobe APP14, error handling
    • cffEmbed.test.ts (36 tests) — OpenType table directory parsing, tag matching, all standard tags
    • freeTextAnnotation.test.ts (16 tests) — creation, parsing, alignment, font size
    • inkAnnotation.test.ts (12 tests) — ink list management, multi-path, clear workflow
    • redactAnnotation.test.ts (13 tests) — overlay text, interior color, quad points
    • stampAnnotation.test.ts (13 tests) — all 14 standard stamp names, custom stamps
    • detect.test.ts (16 tests) — runtime detection for Node/Deno/Bun, caching, adapter creation
    • adapter.test.ts (18 tests) — runtime adapter interface, override, stream creation
    • cli.test.ts (18 tests) — help text, argument parsing, validation, optimize command
  • Complete sub-entry point exports:

    • modern-pdf-lib/create — added ~30 missing exports: gradients, patterns, page manipulation, PDF embedding, QR code options, transparency, document metadata
    • modern-pdf-lib/parse — added Operand type, AnalyzeImagesOptions, PdfParseError, formatHexContext
    • modern-pdf-lib/forms — added 7 appearance generators + 9 option types for complete form appearance API

Fixed

  • CI/CD branch mismatch.github/workflows/ci.yml triggered on main but repo uses master; CI was never running on pushes or PRs
  • Missing sidebar entriesImage Formats and Multi-Party Signing guides were invisible in VitePress navigation
  • CI WASM build missing cratesbuild-wasm-ci.sh was missing jbig2 and jpeg modules (only had 4/6)
  • WASM binaries excluded from npm package — added dist/wasm/** to package.json files field
  • No WASM copy step in build pipeline — added copy:wasm script to copy built WASM from src/wasm/*/pkg/ to dist/wasm/
  • IIFE bundle not in build pipeline — added build:iife to build:all script chain

Changed

  • Code deduplication — inlined .toHex() at 8 call sites, removed 3 identical wrapper functions from ocsp.ts, crl.ts, chainValidator.ts
  • Test count: 4,549 tests passing across 211 test suites (up from 4,282 across 199)

Full Changelog: v0.24.10...v0.25.0

v0.24.10

08 Mar 00:47

Choose a tag to compare

Added

  • Incremental save with signature preservation (src/signature/incrementalSave.ts):

    • saveIncrementalWithSignaturePreservation() — preserves all existing signature byte ranges
    • appendIncrementalUpdate() — pure append-only incremental updates
    • parseExistingTrailer() — parse trailer info from existing PDF
    • findExistingSignatures() / validateByteRangeIntegrity() — signature detection and validation
  • Multi-signature chain validation (src/signature/multiSignatureValidator.ts):

    • validateSignatureChain() — validates ordered chain of signatures and byte range coverage
  • MDP certification policy (src/signature/mdpPolicy.ts):

    • MdpPermission enum (NoChanges, FormFillAndSign, FormFillSignAnnotate)
    • setCertificationLevel() / getCertificationLevel() — DocMDP transform methods
  • Modification detection (src/signature/modificationDetector.ts):

    • detectModifications() — checks if modifications exceed MDP-permitted level
  • Signature field locking (src/signature/fieldLock.ts):

    • addFieldLock() / getFieldLocks() — lock specific fields after signing
  • Document diff (src/signature/documentDiff.ts):

    • diffSignedContent() — compares signed version vs current content
  • Counter-signatures (src/signature/counterSignature.ts):

    • addCounterSignature() / getCounterSignatures() — sign existing signatures
  • LTV embedding (src/signature/ltvEmbed.ts):

    • embedLtvData() — embeds CRL + OCSP + certs in Document Security Store (DSS)
    • hasLtvData() / buildDssDictionary() — LTV data detection and building
  • Incremental save optimization (src/signature/incrementalOptimizer.ts):

    • optimizeIncrementalSave() — FNV-1a object hashing, only append changed objects
    • findChangedObjects() / computeObjectHash() — object-level diff detection
  • WebP image embedding (src/assets/image/webpDecode.ts):

    • decodeWebP() — pure TypeScript VP8 (lossy) and VP8L (lossless) decoder
    • Alpha channel support via ALPH chunk parsing
    • DCT coefficient decoding, YUV420→RGB, macroblock processing
    • LZ77, Huffman coding, spatial prediction (13 modes) for lossless
  • TIFF image embedding (src/assets/image/tiffDecode.ts):

    • decodeTiff() / decodeTiffPage() / decodeTiffAll() — full TIFF decoder
    • Uncompressed, PackBits, LZW, Deflate, JPEG-in-TIFF compression support
    • Multi-page TIFF (IFD chain following)
    • getTiffPageCount() / parseTiffIfd() — TIFF introspection
  • TIFF CMYK support (src/assets/image/tiffCmyk.ts):

    • embedTiffCmyk() — native DeviceCMYK embedding (no RGB conversion)
    • convertTiffCmykToRgb() — CMYK→RGB conversion when needed
  • Image format auto-detection (src/assets/image/formatDetect.ts):

    • detectImageFormat() — detects PNG/JPEG/WebP/TIFF from magic bytes
    • Updated embedImage() to support all 4 formats automatically
  • WebP optimization (src/assets/image/webpOptimize.ts):

    • webpToJpeg() / webpToPng() — format conversion
    • recompressWebP() — decode → JPEG/PNG re-encode pipeline
  • TIFF direct embedding (src/assets/image/tiffDirectEmbed.ts):

    • embedTiffDirect() — direct strip/tile mapping (skip decode/re-encode)
    • canDirectEmbed() — format compatibility check
  • Documentation:

    • Multi-signature workflow guide (docs/guide/multi-sign.md)
    • Image format support guide (docs/guide/image-formats.md)

Stats

  • 4,282 tests passing across 199 suites (+285 new tests)
  • Zero TypeScript errors (strict mode + exactOptionalPropertyTypes)
  • 15 new source files, 15 new test files, 2 new guides

Full Changelog: v0.22.9...v0.24.10

v0.22.9

07 Mar 20:06

Choose a tag to compare

Added

  • CRL/OCSP revocation checking (src/signature/):

    • OCSP request building, response parsing, and certificate status checking
    • CRL parsing, download, and revocation checking with distribution points
    • Certificate chain building and validation with Web Crypto signature verification
    • TTL-based OCSP/CRL response caching with automatic expiry
    • OCSP stapling — embed/extract OCSP responses in PKCS#7 signatures
    • Delta CRL parsing and merging with base CRLs (RFC 5280)
    • Enhanced verification with structured results (chain, revocation, timestamps)
    • Offline revocation data extraction and verification without network
    • Custom trust store class for enterprise PKI certificate management
    • Key usage, extended key usage, and certificate policy validation
    • New guide: docs/guide/verification.md
  • JPEG2000 (JPXDecode) decoder (src/parser/):

    • Full JP2/J2K decoder with MQ arithmetic coder and discrete wavelet transform
    • JPXDecode filter integration in the stream decoder pipeline
    • JP2 container vs J2K codestream detection and parsing
    • Alpha channel detection, separation, and premultiplication
    • JP2-to-JPEG transcoding for downstream JPEG workflows
    • 16-bit to 8-bit bit depth normalization
    • Tiled and region-of-interest decoding for large images
    • WASM bridge with automatic JS fallback
    • New guide: docs/guide/jpeg2000.md
  • Form field JavaScript evaluation (src/form/):

    • Arithmetic expression evaluator and AFSimple_Calculate parser
    • Field calculation order with dependency graphs and topological sort
    • AFNumber_Format/Keystroke and AFDate_FormatEx/KeystrokeEx builtins
    • Field validation: email, phone, range, regex, and length constraints
    • AFPercent_Format, AFSpecial_Format (ZIP, SSN, phone number)
    • Field visibility toggle with condition-based show/hide
    • getField() cross-field references with field proxy objects
    • Document-level scripts: open, close, print, and save actions
    • Sandboxed script execution with reserved word filtering and strict mode
    • New guide: docs/guide/form-scripts.md

Changed

  • Test count: 3,260 → 3,997 across 184 test suites (was 158).

Full Changelog: v0.19.9...v0.22.9

v0.19.9

07 Mar 18:59

Choose a tag to compare

Added

  • Table layout engine (src/layout/): Full-featured table rendering with PDF operators.

    • renderTable() — single-page table rendering to content-stream operators
    • renderMultiPageTable() — automatic page breaks with header row repetition
    • Column width modes: fixed, percentage, flex (weighted), auto-fit (content-based)
    • Cell padding (per-cell or table-wide, four-sided or uniform)
    • Horizontal alignment (left, center, right) and vertical alignment (top, middle, bottom)
    • Colspan and rowspan with 2D occupation grid tracking
    • Alternating row colors, header background/text color
    • Nested tables as cell content (recursive rendering)
    • Rich cell content with TextRun[] (per-run font, color, size)
    • Text overflow modes: wrap, truncate, ellipsis, shrink-to-fit
    • Styling presets: minimalPreset(), stripedPreset(), borderedPreset(), professionalPreset()
    • applyPreset() and applyTablePreset() for named preset selection
    • PdfPage.drawTable() convenience method
    • Overflow utilities: estimateTextWidth(), wrapText(), truncateText(), ellipsisText(), shrinkFontSize()
    • 200 tests across 11 test files
  • QR code & barcode engine (src/barcode/): 9 barcode formats with full encoding.

    • QR code (ISO 18004): versions 1–40, all EC levels (L/M/Q/H), 8 mask patterns, GF(256) Reed-Solomon
    • Code 128 (A/B/C auto-switching, mod-103 check digit)
    • EAN-13 / EAN-8 with L/G/R patterns and parity tables
    • UPC-A (delegates to EAN-13 with leading zero)
    • Code 39 with optional mod-43 check digit
    • ITF (Interleaved 2 of 5) with bearer bars
    • PDF417 (text/byte compaction, RS over GF(929), clusters 0/3/6)
    • Data Matrix ECC200 (RS over GF(256), Utah placement algorithm)
    • renderStyledBarcode() for styled rendering with text, borders, colors
    • readBarcode() / readCode128() / readEan13() for round-trip verification
    • PdfPage.drawQrCode() convenience method
    • 204 tests across 10 test files
  • Browser utilities (src/browser/):

    • saveAsDownload(), saveAsBlob(), saveAsDataUrl(), openInNewTab()
    • Service Worker helpers: handlePdfRequest(), createPdfResponse(), isCacheAvailable()
    • PdfWorker class for Web Worker PDF generation
    • CSP compatibility: disableWasm option, isWasmDisabled(), runtime detection
    • WASM streaming: loadWasmModuleStreaming(), instantiateWasmModuleStreaming()
  • PDF/A full enforcement (src/compliance/):

    • enforcePdfAFull() — complete pipeline chaining all PDF/A fixes
    • sRGB ICC profile generation (~3KB ICC v2 profile)
    • OutputIntent builder with OutputIntentOptions
    • ToUnicode CMap generation (WinAnsi, Symbol, ZapfDingbats)
    • Transparency detection and flattening
    • XMP metadata validation and generation for PDF/A conformance
    • PDF/A profile definitions (1a/1b, 2a/2b/2u, 3a/3b/3u)
    • Associated files for PDF/A-3 (/AF key)
    • Prohibited feature stripping (JavaScript, Launch, Sound, Movie, RichMedia)
    • veraPDF CLI wrapper for CI validation
  • Documentation: New guides for tables, barcodes, PDF/A, CSP, and browser integration.

Changed

  • Test count: 2,323 → 3,260 across 158 test suites (was 110).

Full Changelog: v0.15.1...v0.19.9

v0.15.1

01 Mar 02:51

Choose a tag to compare

Fixed

  • CLI build: Added src/cli/index.ts as a tsdown entry point so dist/cli/index.mjs is produced during build. Previously, npx modern-pdf optimize would fail because the CLI file was never emitted.
  • CJS compatibility: Replaced top-level await in CLI entry with an async main() wrapper to avoid Top-level await is not supported with CJS output build errors.

Full Changelog: v0.15.0...v0.15.1

v0.15.0 — JPEG WASM & Image Optimization

01 Mar 01:35

Choose a tag to compare

Added

  • JPEG WASM module: New Rust WASM crate (src/wasm/jpeg/) using jpeg-encoder 0.7 + jpeg-decoder 0.3 for high-performance JPEG encoding and decoding. TypeScript bridge (initJpegWasm(), encodeJpegWasm(), decodeJpegWasm()) with graceful JS fallback when WASM is unavailable.
  • JPEG quality auto-detection: estimateJpegQuality(jpegBytes) analyzes DQT (quantization table) markers to reverse-engineer the original JPEG quality level (1–100). Pure TypeScript — no WASM required.
  • Progressive JPEG support: progressive option for JPEG encoding produces progressive scan-order JPEGs (better for web delivery).
  • Chroma subsampling control: chromaSubsampling option ('4:4:4' | '4:2:2' | '4:2:0') for JPEG encoding. Default '4:2:0' matches industry standard for smallest files.
  • CMYK JPEG handling: Automatic CMYK→RGB conversion before JPEG encoding using the standard formula R = 255 × (1 − C/255) × (1 − K/255).
  • Batch image optimization API: optimizeAllImages(doc, options) walks all image XObjects in a parsed PDF, recompresses them as JPEG, and replaces stream data in-place. Returns detailed OptimizationReport with per-image breakdown. Options: quality, maxDpi, progressive, chromaSubsampling, skipSmallImages, minSavingsPercent, autoGrayscale.
  • Image extraction API: extractImages(doc) collects metadata for all image XObjects across all pages. decodeImageStream(imageInfo) decodes stream data for further processing.
  • Image deduplication: deduplicateImages(doc) detects identical images by FNV-1a hashing and replaces duplicate references with a single canonical copy. Returns DeduplicationReport with bytes-saved statistics.
  • Grayscale auto-detection: isGrayscaleImage() detects RGB images where all pixels are effectively grayscale (R ≈ G ≈ B within tolerance). convertToGrayscale() converts using ITU-R BT.601 luma formula, saving ~66% for RGB→grayscale.
  • DPI-aware downscaling: computeImageDpi() and computeTargetDimensions() calculate effective DPI from pixel dimensions and display size in points. Used by batch optimizer for automatic DPI-based downscaling.
  • CLI tool: npx modern-pdf optimize input.pdf output.pdf [options] — command-line interface for image optimization with --quality, --progressive, --grayscale, --dedup, --chroma, --verbose options.
  • Image optimization VitePress guide (docs/guide/image-optimization.md): Comprehensive guide covering the full image optimization API, CLI usage, and options reference.

Changed

  • WASM modules: 5 → 6 (added jpeg for JPEG encode/decode).
  • Test count: 2,243 → 2,323 across 110 test suites (was 103).
  • package.json: Added "bin": { "modern-pdf": "./dist/cli/index.mjs" } for CLI support.

Full Changelog: v0.14.1...v0.15.0

v0.14.1

28 Feb 23:03

Choose a tag to compare

Added

  • Unified embedImage() method: Auto-detects PNG (magic bytes 89 50 4E 47) vs JPEG (FF D8 FF) from the raw file data — no need to call embedPng() or embedJpeg() separately. Accepts Uint8Array or ArrayBuffer, throws descriptive errors for unsupported formats.
  • Image optimization API exports: downscaleImage(), recompressImage(), and optimizeImage() are now exported from the main entry point along with their option types (DownscaleOptions, RecompressOptions, ImageOptimizeOptions, RawImageData, OptimizeResult).
  • SASLprep password normalization (RFC 4013): V=5/R=6 (AES-256) password preparation now follows the full SASLprep profile — B.1 "mapped to nothing" characters are stripped, non-ASCII spaces are normalized to U+0020, NFKC normalization is applied, and prohibited characters (control chars, private use, surrogates, tagging) are rejected. This ensures correct password handling for internationalized passwords per ISO 32000-2.
  • Visible signature appearances: signPdf() now accepts an appearance option with rect, text, fontSize, backgroundColor, borderColor, and borderWidth. When provided, the signature renders as a visible box on the page with auto-generated text (signer name from certificate CN, reason, location, date) or custom text lines. Uses a PDF Form XObject appearance stream with Helvetica.
  • Popup annotation type (PdfPopupAnnotation): Floating window annotation that displays parent annotation text. Supports isOpen()/setOpen() and setParent()/getParent() for linking to parent annotations. Reference: PDF 1.7 §12.5.6.14.
  • Caret annotation type (PdfCaretAnnotation): Marks text insertion points in review workflows. Supports getSymbol()/setSymbol() ('None' | 'P' for paragraph) and getCaretRect()/setCaretRect() for inner rectangle (RD) insets. Reference: PDF 1.7 §12.5.6.11.
  • File attachment annotation type (PdfFileAttachmentAnnotation): Embeds a file as a clickable icon on a page. Supports getIcon()/setIcon() ('GraphPushPin' | 'PaperclipTag' | 'Paperclip' | 'Tag'), getFileName(), and buildFileSpec(registry) for building the embedded file stream, EF dictionary, and file specification. Reference: PDF 1.7 §12.5.6.15.
  • Digital signatures guide (docs/guide/signatures.md): Comprehensive guide covering invisible and visible signing, verification, low-level ByteRange API, external signers, RFC 3161 timestamping, and key preparation with OpenSSL.
  • Accessibility & tagged PDF guide (docs/guide/accessibility.md): Guide covering document language, title metadata, font embedding for Unicode, color contrast, multilingual content, PDF/UA compliance checklist, and XMP metadata.
  • Annotations guide (docs/guide/annotations.md): Complete reference for all 18 annotation types with code examples — Text, Link, FreeText, Highlight, Underline, Squiggly, StrikeOut, Line, Square, Circle, Polygon, PolyLine, Stamp, Ink, Redact, Popup, Caret, FileAttachment. Includes annotation flags, appearance generation, and parsing from existing PDFs.

Performance

  • Per-object encryption key caching: deriveObjectKey() in PdfEncryptionHandler now caches computed keys in a Map<number, Uint8Array> keyed on (objNum << 16) | genNum. For V=1-4 encrypted PDFs, this eliminates redundant MD5 computations when decrypting multiple strings/streams in the same object.
  • File-level encryption key caching: computeFileEncryptionKey() now maintains an LRU cache (max 32 entries) keyed on password + encryption dictionary parameters. Re-opening the same PDF with the same password skips the expensive key derivation (especially Algorithm 2.B for R=6 which runs 64+ rounds of AES+SHA).

Changed

  • Annotation count: 15 → 18 annotation types (added Popup, Caret, FileAttachment).
  • Test count: 2,199 → 2,243 across 103 test suites (was 100).

Full Changelog: v0.14.0...v0.14.1

v0.14.0 — Performance Audit & Bug Fixes

28 Feb 21:29

Choose a tag to compare

Performance

Comprehensive internal performance audit across the entire codebase. All changes are internal hot-path optimizations — zero API surface changes.

Lexer & Content Stream Parser:

  • String concatenation in readLiteralString(), readHexString(), readName() replaced with parts[] + .join('') pattern
  • bytesToAscii() / decodeAscii() replaced with batch String.fromCharCode.apply()
  • Hex string parsing rewritten to single-pass direct byte decoding with hexVal lookup table (eliminates intermediate string + parseInt)

LZW Decompression:

  • Complete rewrite with pooled flat buffer (Uint8Array + Int32Array index pairs) replacing per-entry Uint8Array[] allocations
  • Identity entries (0-255) initialized once and persist across table resets
  • Pre-allocated output buffer with manual growth instead of number[] + .push()

XRef Recovery & Parsing:

  • rebuildXrefFromScan() rewritten to scan raw Uint8Array bytes directly for obj pattern instead of TextDecoder.decode() + regex on the entire file
  • Standard xref entries parsed directly from bytes (fixed 20-byte format) without TextDecoder
  • Keyword checks (xref, trailer) replaced with direct byte comparison

PDF Object Serialization:

  • escapeLiteralString(): 5 chained .replace() calls replaced with single-pass character loop
  • PdfName.serialize(): String concatenation replaced with array + join
  • formatNumber(): Regex trailing-zero trim replaced with manual digit loop

Cryptographic Key Derivation:

  • Pre-allocated modKey buffer outside 19x RC4 iteration loops in owner/user password verification
  • Direct K1 buffer construction in Algorithm 2.B (eliminates intermediate concatenation)

Other:

  • ASCII85 decoder: Fixed-size group buffer + pre-allocated output
  • SVG color parser: Module-level result cache (Map<string, ParsedColor>)
  • XMP escapeXml(): Single-pass character loop replacing chained .replace()
  • Inline image EI scanning: data.indexOf(0x45) jump instead of byte-by-byte scan
  • Object stream header: Array + join replacing string concatenation

Fixed

  • CCITT Group 3/4 2D decode bug: read2DMode() returned HORIZONTAL for bit pattern 011 instead of VERTICAL_PLUS_1 — correct logic existed but was unreachable due to premature return. This could cause incorrect rendering of CCITT Group 4 and Group 3 2D compressed images (scanned documents).
  • customName font option ignored for empty strings: || operator treated empty string as falsy, falling through to postScriptName. Changed to ?? (nullish coalescing).
  • embedPages() unnecessarily async: Method was declared async but contained only synchronous code, wrapping return in an unnecessary Promise. Now returns EmbeddedPdfPage[] directly.
  • Duplicate hash computation in document merge: hashBytes() was called twice on the same stream data during cross-document page copy. Now computed once and reused.

Removed

  • Dead PdfArr import alias in pdfWriter.ts
  • Unused objectBuf variable allocation in object stream serialization
  • Unused objectContainsPageRef() function in linearization module

Full Changelog: v0.13.0...v0.14.0