Skip to content

fix(docx): break a table across pages where the layout breaks it - #724

Merged
DemchaAV merged 2 commits into
2.5-devfrom
fix/docx-table-pagination
Sep 23, 2026
Merged

DemchaAV merged 2 commits into
2.5-devfrom
fix/docx-table-pagination

Conversation

@DemchaAV

Copy link
Copy Markdown
Owner

Why

Word re-paginates a DOCX itself, so it only breaks a table where the layout does if the file says where. The layout:

  • splits a table only between rows. A row that does not fit what is left of a page moves to the next page whole.
  • repeats the first repeatHeader(n) rows at the top of every page the table continues on.
  • never leaves those header rows at the foot of a page with no body row under them (splitTable refuses a head that is only header).

The export wrote none of it. Word's default lets a row break mid-line wherever its own page ends, and a long table printed its header once.

What changed

  • DocxSemanticBackend.breakRowsWhereTheLayoutDoes, run after each table is written:
    • Every row the layout placed gets w:cantSplit.
    • The first repeatedHeaderRowCount rows get w:tblHeader, and their cell paragraphs get w:keepNext, so the last header row stays on the page of the first body row.
    • A table that is all header gets no keep-with-next: with no body row under it, that would tie the table to whatever paragraph follows.
  • writeRow keeps a row of blocks whole the same way. RowDefinition lays a row out as one atomic piece.
  • Rows are gated on the layout having placed them, not on a height check. The layout refuses a row taller than the page (AtomicNodeTooLargeException), and the session then exports without a layout. So a placed row is always one a page can hold. In a document that could not be laid out, rows stay free to break, because no page holds such a row whole.
  • DocxLayoutMetrics:
    • placedRow(table, row) and placed(node) are new.
    • The "rows that span this table" filter moved out of tableColumns into ownRows, so both callers skip a composed cell's nested table. That table emits its rows under the owner's path.
  • Recipe table rows, capability-matrix DOCX table cell, and a CHANGELOG entry under v2.5.0.
  • assets/readme/examples/word-export-companion.docx is re-rendered. Its document.xml diff is exactly six <w:trPr><w:cantSplit/></w:trPr> additions, one per table row, and nothing else.

Verification

Full reactor gate (clean verify over core, render-pdf/docx/pptx, templates, testing, qa, coverage) → BUILD SUCCESS. Examples after install: 93 green; CommittedAssetDriftTest went red on the stale preview until it was re-rendered.

New DocxTablePaginationTest (7 tests):

  • Placed rows are cantSplit.
  • repeatHeader(2): both header rows are tblHeader and keep with the next row, and the body rows are neither.
  • No repeatHeader: no header row and no keep.
  • An all-header table keeps no keep-with-next.
  • A table with a row too tall to lay out has no cantSplit row.
  • A bare export without a layout has no cantSplit but still gets tblHeader.
  • addRow is kept whole.

Sabotaging both guards (placedRowtrue, header keep → always) fails 3 of them.

Lane: shared-engine (render-docx) — table row properties only; no public API change.

The layout splits a table only between rows, repeats its header rows
on every page it continues on, and never leaves them at the foot of a
page alone. The export stated none of it. Every row the layout placed
is now w:cantSplit, repeatHeader(n) rows are w:tblHeader and keep with
the row under them, and a row of blocks is kept whole the same way.

The committed word-export-companion.docx preview is re-rendered: its
six table rows gain w:cantSplit and nothing else changes.
@DemchaAV
DemchaAV force-pushed the fix/docx-table-pagination branch from f1d4409 to f3cb1f9 Compare September 23, 2026 12:18
A row name's index was parsed with Integer.parseInt, which throws on a
name that only resembles the pattern. The index is kept as text and a
row is looked up by writing its index the same way, so such a name
matches nothing instead of failing the export.
@DemchaAV
DemchaAV merged commit a90ce94 into 2.5-dev Sep 23, 2026
12 checks passed
@DemchaAV
DemchaAV deleted the fix/docx-table-pagination branch September 23, 2026 12:52
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.

2 participants