Skip to content

fix(docx): keep a row's fixed columns at their widths when they leave the rest of the row empty - #817

Merged
DemchaAV merged 2 commits into
2.5-devfrom
fix/docx-fixed-row-columns
Oct 2, 2026
Merged

DemchaAV merged 2 commits into
2.5-devfrom
fix/docx-fixed-row-columns

Conversation

@DemchaAV

@DemchaAV DemchaAV commented Oct 2, 2026 •

Copy link
Copy Markdown
Owner

Why

A row whose columns are all fixed, and add up to less than the row, leaves the rest of the row empty on the page. The last column's text wraps at that column's own width.

In DOCX the last column ran to the row's edge. EditorialProposal's deliverables are a 262.8pt column and a 193.8pt one in a 535pt band. The second came out 272pt wide (w:gridCol 5449). Word set "Responsive design for desktop, tablet & mobile" on one line where the page wraps it, so every item under it stood a line, 12.3pt, higher.

The same rows set the scope of work's descriptions on the first page (EditorialPageOne, three fixed columns) and PaymentsInvoice's item text (a fixed disc and a fixed text column): their last column wrapped later than the page in the same way. These account for six of EditorialProposal's eight newly found lines and all four of PaymentsInvoice's.

What changed

  • holdTheLastFixedColumn. Where a row's last column is fixed, the column keeps its stated width (with its cell margins) and does not run to the row's edge. The column starts where its child does, past the child's left margin, so that margin is taken out of the held width. It applies on the path where the layout placed the row's children (placedColumns).
  • applyRowGeometry: the table is no wider than its columns. That holds on both paths, placed and stated. A row whose columns fill it is written as before.
  • Baselines. word-windows and libreoffice-windows are rewritten for the documents this moves.
  • Docs. CHANGELOG.md, and the Rows row of docs/recipes/docx-export.md.

Verification

  • Template corpus, re-measured in full against 2.5-dev's baselines. Both gates pass.

    Editor Document Lines found Past 2pt Median p90
    Word 16.0.20430 EditorialProposal 120 → 128 of 132 22 → 19 0.58 → 0.50pt 2.20 → 2.09pt
    Word PaymentsInvoice 100 → 104 of 104 2 → 0 0.50 → 0.51pt 1.10 → 1.02pt
    LibreOffice (Windows) EditorialProposal 119 → 127 of 132 68 → 70 43.27 → 43.26pt 45.18 → 45.15pt
    LibreOffice (Windows) PaymentsInvoice 100 → 104 of 104 2 → 0 0.41pt 0.91 → 0.90pt

    No other document changes in either editor.

  • EditorialProposal in LibreOffice stands 42–46pt off on its second page for a cause of its own. Two of its eight newly found lines are past 2pt with the rest of that page. No line it found before moved further.

  • New test, DocxRowLayoutTest.fixedColumnsNarrowerThanTheRowLeaveTheRestOfItEmpty. A 100pt and an 80pt column in a 360pt row are written 2000 and 1600 twips, in a 3600-twip table. It is checked with the layout and without it.

  • New test, DocxRowLayoutTest.aLastFixedColumnHoldsItsChildsMarginInsideItsWidth. An 80pt last column whose child has a 10pt left margin is written 1400 twips.

  • New test, DocxRowLayoutTest.fixedColumnsNarrowerThanTheRowKeepItsPaddingAtTheirOuterEdges. With 12pt of row padding, the columns are 2240 and 1840 twips, the last cell's right margin is 240, and the table is 4080. Both paths are covered.

  • Rows whose columns fill the row keep the table at the row's width. aFixedColumnTakesItsWidthAndAWeightTakesTheRest asserts 7200 twips, and DocxEditorSlackTest.aRowWithNoAutoColumnIsWrittenAsPlaced asserts the placed width.

  • One expectation changed, and the test renamed: DocxEditorSlackTest.aRowWithNoWeightColumnHasNoPointToGiveItsFixedColumnAtItsWidth. Its row is an auto column and a fixed 80pt one. The fixed column is now written 1600 twips, at the width the layout gives it, where the test held it to the row's edge.

  • Each rule fails the test when taken out.

    • Without holdTheLastFixedColumn, the laid-out row's last column runs to its edge.
    • With the table at the row's width, the table is 7200 twips.
    • Without the child's margin taken out, the last column is 1600 twips, not 1400.
  • Full reactor gate: ./mvnw -B -ntp clean verify -pl :graph-compose-core,:graph-compose-render-pdf,:graph-compose-render-docx,:graph-compose-render-pptx,:graph-compose-templates,:graph-compose-testing,:graph-compose-qa,:graph-compose-coverage -am gives BUILD SUCCESS. render-docx runs 794 tests, qa 1814.

Known limits

  • EditorialProposal's timeline table still stands up to 2pt low in Word. A table text cell's line holds the line gap, and its text is not seated on the page's baseline: the first line of a wrapped cell stands about 1.2pt low. That is its own change.
  • The Linux LibreOffice baseline is taken from this PR's CI run.

Lane: shared-engine (render-docx). No public API change.

@DemchaAV
DemchaAV merged commit dbe368b into 2.5-dev Oct 2, 2026
13 checks passed
@DemchaAV
DemchaAV deleted the fix/docx-fixed-row-columns branch October 2, 2026 13:03
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