Skip to content

feat(ooxml): style a cell of an xlsx file - #937

Merged
andiwand merged 3 commits into
mainfrom
feat/xlsx-cell-style
Sep 28, 2026
Merged

andiwand merged 3 commits into
mainfrom
feat/xlsx-cell-style

Conversation

@andiwand

@andiwand andiwand commented Sep 28, 2026 •

Copy link
Copy Markdown
Member

🤖 Generated with Claude Code

Step 3 of the cell-formatting plan (#934): setCellStyle and Sheet::set_cell_style write into .xlsx.

Change

  • The writer starts from the xf that the cell shows: its own s, else the row s where the row states customFormat, else the column style.
  • It copies that xf and its font, and applies the delta. It appends a font, a fill and an xf only where no equal one exists, so a range of cells with one delta adds one xf. It sets applyFont, applyFill and applyAlignment.
  • A solid fill is patternFill solid with fgColor, the colour the reader of fix(ooxml): an xlsx fill shows its colour, and a font its toggles #935 takes. fill: null is patternType="none".
  • A font keeps the child order of CT_Font, and new collections keep the order of styleSheet. An empty styleSheet first gets the entries that every xf needs: a font, the two reserved fills, a border, and one xf in cellStyleXfs and cellXfs.
  • A cell that the file does not state is made as for a value. A cell covered by a merge refuses.
  • styles.xml is now a written part, so every save writes it from its DOM.

A fix found on the way

  • Column in a gap: the registry looked up a column by the first <col> range that ends at or after it, and it did not check where that range starts. A column in a gap between two ranges took the width and the style of the next range, and the new writer would have used that style as its base. The registry now keeps the first column of each range. The regression test fails without the fix.

Proof

  • 11 new OoxmlSpreadsheetStyleWrite tests: a fill survives a save of an empty style sheet, every text key, a shared xf is copied, one delta makes one xf/font/fill, a fill taken away, alignment, a cell that the file does not state, the row as base, the column range as base, a covered cell, and the JSON op.
  • The full suite passes (1894 tests). The full HTML output, public and private, is byte-identical to the pinned reference output.
  • sample.xlsx and multiline.xlsx styled with six ops each and saved: LibreOffice loads both files and shows the fills, bold, italic, underline, colour, left alignment, a fill taken away, and new cells past the sheet. I have no Excel here, so Excel has not opened these files.

Base automatically changed from feat/ods-cell-style to main September 28, 2026 19:01
andiwand and others added 3 commits September 28, 2026 21:02
setCellStyle now writes into .xlsx. The writer starts from the xf that the
cell shows (its own s, else the row s where the row states customFormat,
else the column style), copies it and its font, and applies the delta. It
appends a font, a fill and an xf only where no equal one exists, and sets
applyFont, applyFill and applyAlignment. A font keeps the child order of
CT_Font, and an empty styleSheet first gets the entries that every xf needs.

styles.xml is now a written part, so every save writes it from its dom.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NXnz6EZY8YpyiyyE1GpsWy
The registry looked up a column by the first range that ends at or after
it, and it did not check where that range starts. So a column in a gap
between two `<col>` ranges took the width and the style of the next one.
The registry now keeps the first column of each range, and the style
writer asks the registry and does not walk `<cols>` again.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tigcf9he3w1aHfNPJoEGsd
The odf and the xlsx style writers each had a copy of `set_attribute`,
so `xml::set_attribute` now serves both. The xlsx writer inserts a child
through `insert_in_sequence` and spells a colour through `hex_color`,
which `ooxml_util` already has.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tigcf9he3w1aHfNPJoEGsd
@andiwand
andiwand force-pushed the feat/xlsx-cell-style branch from eea7d7a to 197bec9 Compare September 28, 2026 19:08
@andiwand
andiwand merged commit 0b034e9 into main Sep 28, 2026
34 checks passed
@andiwand
andiwand deleted the feat/xlsx-cell-style branch September 28, 2026 19:28
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