9.0.0 (2026-04-08)
⚠ BREAKING CHANGES
pdf(),readPdf(), andexcelToPdf()now returnPromiseinstead of synchronous results. All call sites must useawait.pdfAsync(),readPdfAsync(), andexcelToPdfAsync()have been removed — the base names are now async.
Features
- Make PDF APIs async to avoid blocking the event loop (2f521cd)
Bug Fixes
- Improve Excel-to-PDF style fidelity and fix cell style mutation leaks (0448604)
PDF Rendering
- Type-based default alignment: numbers/dates right-align, booleans/errors center, formulas align by result type — matching Excel behavior when no explicit alignment is set
- Merged cell border propagation: borders set on boundary cells (right column, bottom row) are now correctly rendered on the merged region
- Text overflow: non-wrapped text overflows into adjacent empty cells, stopped by cells with content (not just fill)
- Double border: renders as two parallel thin lines with a gap, instead of a single line
- Vertical stacked text:
textRotation: 255now renders characters top-to-bottom instead of at a skewed angle - Rotated text rewrite: dedicated rendering paths for 90°, −90°, and arbitrary angles with correct anchoring, multi-line support, and wrap
- Column width: uses Excel's actual formula
(excelWidth × maxDigitWidth + columnPadding) × px-to-ptinstead of the roughwidth × 7approximation - Row height: auto-sizing now uses actual font metrics and
wrapTextLines()to match rendered output exactly - fitToPage: no longer double-applies
pageSetup.scaleon top of fit-to-page calculation - Image scaling: EMU offsets and extents are now multiplied by
scaleFactor - Theme colors: updated to Office 2019+ default palette (e.g. accent1 =
#4472C4) - Border widths: tuned to closer match Excel's visual rendering (hair 0.1pt, thin 0.25pt, medium 0.5pt, thick 1.0pt)
- Shared constants:
CELL_PADDING_H/V,LINE_HEIGHT_FACTOR,INDENT_WIDTHextracted toconstants.tsto prevent layout/render drift - Bordered empty cells: cells with borders/fill/font but no value are now included in PDF output
- Explicit newlines: non-wrapped text now splits on
\nto produce multiple lines - Rich text type guard: replaced unsafe cast with proper
"richText" in valuecheck - Scale-aware rendering: cell padding, indent, and clip regions all respect page
scaleFactor
Number Formatting (cell-format.ts)
?placeholder: pads integer part with leading spaces and decimal part with trailing spaces#placeholder: suppresses leading zeros in integer part and strips trailing zeros in decimal part#.##with zero: produces empty string instead of a bare.- Zero-value sections: accounting formats like
"-"??correctly produce dash + spaces
Cell Style Isolation
Cell.merge(): deep-copies master style so each cell in a merge range has an independent style object — setting border on one cell no longer mutates all othersCell.set model: clones shared cached style from XLSX deserialization, preventing cross-cell mutation when modifying style after loading a fileRow._applyStyle(): clones object values before broadcasting to cellsColumnstyle setters: clone values before broadcasting to cellsColumn.set defn: clones style from model during XLSX load andspliceColumnsCell._mergeStyle(): clones sub-properties inherited from row/column styles at construction timeWorksheetReader(streaming): clones cached styles for both rows and cells
Tests
- 40 new tests across 7 files (6623 total, all passing)
- New integration test file:
pdf-edge-cases.test.tswith 20 tests covering alignment, merge borders, overflow, double borders, zero formats, fitToPage, row heights, newlines, error values, rich text, hyperlinks, and vertical text - New example:
pdf-edge-cases.tsdemonstrating all advanced rendering scenarios