Skip to content

feat(document): style a cell of an ods file - #936

Merged
andiwand merged 2 commits into
mainfrom
feat/ods-cell-style
Sep 28, 2026
Merged

andiwand merged 2 commits into
mainfrom
feat/ods-cell-style

Conversation

@andiwand

@andiwand andiwand commented Sep 28, 2026 •

Copy link
Copy Markdown
Member

Step 2 of the cell-formatting plan (#934).

Change

  • Sheet::set_cell_style(column, row, TableCellStyle, TextStyle) and the setCellStyle op:

    {"op": "setCellStyle", "sheet": 0, "column": 1, "row": 2,
     "style": {"fill": "#ffff00", "bold": true, "align": "center"}}

    The keys are those of setTextStyle, plus fill (#rrggbb or null) and align (left, center, right). highlight refuses, and so does any style field that no engine writes.

  • The ods writer claims the cell as a value write does, and points table:style-name at a fresh automatic ce<n> style. That style copies the automatic style that the cell shows (its own, else the row or column default) with the delta, or it inherits from a named style. One base and one delta make one style, so formatting a range adds one style and not one per cell.

  • The fill is fo:background-color, the alignment is fo:text-align with style:text-align-source="fix", and the text keys are written as setTextStyle writes them.

  • Other engines refuse, because the hook's default throws. xlsx comes in the next PR.

Two fixes found on the way

  • Covered position: a value written into a covered cell was written into the sheet outside the merge. The index holds no covered cell, so is_covered now walks the row in the DOM, and both writes refuse.
  • Empty cell after a merge: claim_cell took the start of a run from the end of the cell before it. After a span that is wrong, so a write into an empty cell right of a merge threw. It now takes the start from the run itself. The regression test fails without the fix.

Proof

  • 13 new OdfSheetStyle tests on inline flat sheets: a shared style is copied, a named one is inherited from, a repeated run is cut, a cell past the sheet is made, one delta makes one style, a fill taken away is transparent, alignment, a formula cell, refusals and the JSON op.
  • The full suite passes (1883 tests). The reference output does not change.
  • Senza nome 1.ods styled with seven ops and saved: LibreOffice loads it and shows every op as written: fills, bold, italic, underline, colour, strikethrough at 16pt, a fill taken away, and a cell grown past the table. Our own render of the saved file matches.

🤖 Generated with Claude Code

https://claude.ai/code/session_01NXnz6EZY8YpyiyyE1GpsWy

@andiwand
andiwand force-pushed the fix/xlsx-cell-style-read branch from 2f0eaa3 to 1eb3554 Compare September 28, 2026 18:31
Base automatically changed from fix/xlsx-cell-style-read to main September 28, 2026 18:52
andiwand and others added 2 commits September 28, 2026 20:53
Sheet::set_cell_style and the setCellStyle op write the fill, the
horizontal alignment, and bold, italic, underline, strikethrough, colour
and size onto one cell. The op takes the keys of setTextStyle, and fill and
align in place of highlight.

The ods writer claims the cell as a value write does, and points
table:style-name at a fresh automatic ce<n> style. That style copies the
automatic style the cell shows (its own, else the row or column default)
with the delta, or inherits from a named one. One base and one delta make
one style, so a range of cells adds one.

A covered position now refuses a value and a style. The index holds no
covered cell, so is_covered walks the row. claim_cell took the start of a
run from the end of the cell before it, which is wrong after a span; it
now takes it from the run itself.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NXnz6EZY8YpyiyyE1GpsWy
`write_text_properties` had its own copy of `set_attribute`, so it now
calls that. The lambda that builds the key of a cell style is `key_of`,
because `optional` reads like `std::optional`.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tigcf9he3w1aHfNPJoEGsd
@andiwand
andiwand merged commit 9929e89 into main Sep 28, 2026
25 checks passed
@andiwand
andiwand deleted the feat/ods-cell-style branch September 28, 2026 19:01
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