The fill of a table row is written, in both Writers - #911
Conversation
Progi1984
left a comment
There was a problem hiding this comment.
Small comment (which applies multiple times)
Need a rebase too
|
@Progi1984 Good question, and following it turned up something worth deciding together, so let me lay out what both formats actually do. What PowerPoint writes
Here is a deck saved by PowerPoint 16 — one table, the third row filled red, and the fourth cell of that row set to "No fill": <a:tr h="370840">
<a:tc>…<a:tcPr><a:solidFill><a:srgbClr val="C00000"/></a:solidFill></a:tcPr></a:tc>
<a:tc>…<a:tcPr><a:solidFill><a:srgbClr val="C00000"/></a:solidFill></a:tcPr></a:tc>
<a:tc>…<a:tcPr><a:solidFill><a:srgbClr val="C00000"/></a:solidFill></a:tcPr></a:tc>
<a:tc>…<a:tcPr><a:noFill/></a:tcPr></a:tc>
…
</a:tr>The two rows above it are untouched, and their cells are just What LibreOffice writesThe same deck, converted with <table:table-row table:style-name="ro1" table:default-cell-style-name="ce3">
<table:table-cell/>
<table:table-cell/>
<table:table-cell/>
<table:table-cell table:style-name="ce4"/>
…
</table:table-row>with The pointBoth formats distinguish "this cell was not given a fill" from "this cell was given no fill on purpose" — OOXML by an empty Options1. A
Everything else keeps the current 2. 3. 4. Leave it as this PR has it and document that a cell with no fill of its own takes its row's. Zero cost, and the limitation is narrow: a transparent cell inside a filled row, which was not expressible before this PR either, since a row's fill never reached the file at all. I lean towards 1, with 3 the close second — it is the smallest diff of the four. A second question, separable from the firstWhichever option you pick, there is a choice about what an unset cell should be written as. Today every untouched cell gets So Worth noting for the ODP side of any of this: the row's fill can go on The readers need the same decisionWhatever the writers end up meaning by "unset", the readers have to agree with them, and today neither does.
I would rather do the writers here and the readers in a follow-up, unless you want them together. Meanwhile I have rebased onto master (the only conflict was the changelog, both entries kept) and added three tests for the case you were asking about: a cell whose row has no fill either, in both writers, plus the gradient path in |
|
@Progi1984 As for the other PRs, just let me know what needs to be updated or rebased, and I’ll take care of it. |
|
I like the option 1. |
|
Option 1 it is — I'll implement it here. One thing that falls out of it and is worth flagging, because it is a deletion rather than an addition: It is already destructive today — walking past a cell that was given its own fill overwrites it with the row's — and under The rest, defaulted so you only need to answer if you disagree:
I'll push the |
dfe3ec6 to
66addb4
Compare
937b558 to
af2d5e0
Compare
`Row::getFill()` reached the file by accident or not at all. `Row::nextCell()` copies the fill of the row onto the cell it moves to, so a table walked with `nextCell()` came out banded, while the same table reached through `getCell()` came out blank -- and a fill set on the row after its cells were made was lost either way. The order of the calls decided it, silently. Neither format has a fill on a row to write it to. In OOXML `CT_TableRow` carries a height and nothing else; the row style of ODF takes a flat `fo:background-color`, no gradient, and LibreOffice does not draw even that one -- patching it in by hand changes no pixel of the render. So both Writers now paint a cell that has no fill of its own with the fill of its row, which is where the formats keep it. A cell that asks for a fill of its own still keeps it. The ODPresentation Writer needs the gradient of a row defined as well, or the style of the cell would name a `gradient_*` that is not in the file.
A row's fill reaches its cells, but `Fill::FILL_NONE` was doing two jobs: the state a cell starts in, and the state a cell is put in by asking for no fill. Reading the first as "take the row's" made the second impossible to express -- a transparent cell inside a painted row, which is what both applications write when you ask for one. PowerPoint leaves an untouched cell's `a:tcPr` empty and writes `a:noFill` for a refusal; LibreOffice leaves an untouched cell without a `table:style-name` and gives a refusal a style with `draw:fill="none"`. So a table `Cell` and a table `Row` are born at `Fill::FILL_UNSET`, and that is what the Writers resolve against the row. `FILL_NONE` now means what it says everywhere else in the library: no fill, and none inherited either. `Row::nextCell()` stops copying the row's fill onto the cell it moves to. The copy was already destructive -- walking past a cell replaced a fill it had been given -- and under `FILL_UNSET` it would turn "not asked" into an explicit value at navigation time, undoing the distinction as you read the row. Nothing needs it: the Writers resolve the row's fill when they write. `writeFill()` treats an unset fill as an empty one, or an `UNSET` arriving there would fall past solid and gradient into the pattern fill at the end.
af2d5e0 to
74aeda8
Compare
Description
Row::getFill()reached the file by accident or not at all.Row::nextCell()copied the fill of the row onto the cell it moved to, so a table walked withnextCell()came out banded, while the same table reached throughgetCell()came out blank. And because the copy happened at the moment of the walk, a fill set on the row after its cells were made was lost either way. Measured on one deck, three tables, same fill:$row->setFill(...), cells reached withgetCell()FFEEDD$row->setFill(...), cells walked withnextCell()FFEEDDFFEEDDFFEEDDFFEEDDNeither format has a fill on a row to write it to, so painting the cells is not a shortcut -- it is the only representation there is:
CT_TableRowistc*,extLstand a requiredh. A row has a height and nothing else. What PowerPoint shows as a banded row comes from the table style,a:band1Hand friends, which is a property of the table, not a fill on a row. Filling a row by hand in PowerPoint writes ana:solidFillinto every one of its cells.style:table-row-propertiesdoes accept a flatfo:background-color, but no gradient, and LibreOffice does not draw it in a presentation. I wrote the attribute into the row style by hand and converted both files to PDF: 0 pixels of 389 520 differ. What LibreOffice writes instead istable:table-row/@table:default-cell-style-name, a cell style the row's cells inherit unless they carry one of their own -- again, the fill lands on cells.So both Writers now paint a cell that has no fill of its own with the fill of its row. A cell that asks for a fill of its own keeps it -- the fill of the row is what a cell falls back to, not what it is overruled by. The ODPresentation Writer needs the gradient of a row defined as well, or the style of the cell would name a
gradient_*that is not in the file.A cell that was given no fill, and a cell that refused one
Following the review,
Fill::FILL_NONEturned out to be doing two jobs: the state a cell starts in, and the state a cell is put in by asking for no fill. Reading the first as "take the row's" made the second impossible to express -- a transparent cell inside a painted row, which is exactly what both applications write when you ask for one: PowerPoint leaves an untouched cell'sa:tcPrempty and writesa:noFillfor a refusal, LibreOffice leaves an untouched cell without atable:style-nameand gives a refusal a style withdraw:fill="none".So a table
Celland a tableRoware now born at a newFill::FILL_UNSET, and that is what the Writers resolve against the row.FILL_NONEmeans what it says everywhere else in the library: no fill, and none inherited either.Two consequences, both in the changelog under BC Breaks:
$cell->getFill()->getFillType()on an untouched cell returns'unset'where it used to return'none'.Row::nextCell()no longer copies the row's fill onto the cell it moves to. The copy was already destructive -- walking past a cell replaced a fill it had been given -- and underFILL_UNSETit would turn "not asked" into an explicit value at navigation time, undoing the distinction as you read the row. Nothing needs it now that the Writers resolve the row's fill when they write.One thing this PR does not change: an unset cell is still written as
a:noFill, as it always was, so no existing deck moves. Writing nothing instead, and letting the table style intableStyles.xmlpaint the cell the way PowerPoint would, is a separate decision and a separate PR. The readers are a follow-up too:Reader\PowerPoint2007::loadStyleFill()has noa:noFillbranch, so a refusal is dropped on read, andReader\ODPresentationhas no table handling at all.Checklist: