Repository navigation
Conversation
`defaultFont` is written as fonts[0], and its doc says every cell format
inherits from it. But a cell's fontId picks one complete <font>; it does
not layer on fonts[0]. A cell font that gave only part of itself, such
as `{ bold: true }`, was written as `<font><b/></font>` with no face or
size, so LibreOffice drew it in its own default (Cambria 11pt) rather
than the workbook's.
registerXf now passes the cell font through inheritBaseFont before
addFont. A missing `name` or `size` is taken from the base font, and
when the name is inherited the base's `family`, `charset` and `scheme`
come with it; a font that names its own face keeps it, because
`scheme="minor"` would make Excel draw the theme font instead. Colour
and the other parts are not inherited. Differential formats (addDxf,
used by conditional formatting) are left as given, since there a
missing part means "keep the cell's own".
This applies to writeXlsx, the streaming writers (Calibri 11, as they
take no defaultFont) and the round-trip writer. The parity register's
`columns` probe now reads the bold font back with Georgia 13 from the
fixture's defaultFont. WriteOptions.defaultFont gets a doc comment and
docs/PARITY.md says what comes back.
Closes productdevbook#594
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info
📝 Walkthrough
Merge Risk: ⚪ Minimal · up to
|
Closes #594
What was wrong
defaultFontis written asfonts[0], andWorkbook.defaultFont's doc says it is "the entry every cell format inherits from unless it names another". A cell'sfontIddoes not work that way. It picks one complete<font>, so a cell font that gave only part of itself was written without a face or size:Reading it back with
readStyles: true, the cell's font was{ bold: true, color: { rgb: "1F3A5F" } }before and is{ bold: true, color: { rgb: "1F3A5F" }, size: 10, name: "Arial" }after. According to the issue, LibreOffice drew the old file's bold cell in Cambria 11pt.Why the tests didn't catch it
No test wrote a partial font next to a
defaultFontand looked at the<font>that came out. The parity register did write one: itscolumnsprobe has{ bold: true }and the fixture hasdefaultFont: { name: "Georgia", size: 13 }. But it expectedfont: { bold: true }back, so it pinned the bug.What landed
inheritBaseFontinstyles-writer.ts, called fromregisterXfbeforeaddFont. A missingnameorsizecomes from the base font. When the name is inherited, the base'sfamily,charsetandschemecome with it. A font that names its own face does not get them, becausescheme="minor"would make Excel draw the theme font instead. Colour and the other parts are not inherited. A font that already has both a name and a size is passed through without a copy, because the streaming writers call this once per cell.addDxf, used by conditional formatting) are unchanged. There, a part that is left out means "keep the cell's own".writeXlsx, the streaming writers (Calibri 11, since they take nodefaultFont) and the round-trip writer, because they all use the same collector.WriteOptions.defaultFontgets a doc comment.docs/PARITY.mdhas a short section on what comes back. The parity register'scolumnsexpectation now includes Georgia 13, with a comment that explains why.Tests:
test/font-inherits-default.test.tsadds 9 tests, and 7 of them fail againstmain. The 2 that pass onmainare guards: the conditional formatting font stays unmerged, and the caller's style object is not mutated. The changed parity expectation also fails without the fix.bun run testpasses on Node 24.22.0, with 241 files and 10,707 tests.Checked in Excel
In Excel for Mac, with the issue's case written by
mainand by this branch. Both files open without a repair prompt. In both, Excel reports A1 as Arial 10, bold, dark blue. Excel fills a font's missingnameandsizefrom font 0 itself, so the bug does not show in Excel. The difference shows in apps that don't fill the gap that way.Not verified
Open question: rich text runs have the same problem. A run font without
name/sizeis written as given. I left that out of this PR.Summary by CodeRabbit
New Features
Documentation