Skip to content

fix(docx): keep a block on one page in Word when the layout does - #725

Merged
DemchaAV merged 1 commit into
2.5-devfrom
fix/docx-keep-sections
Sep 23, 2026
Merged

DemchaAV merged 1 commit into
2.5-devfrom
fix/docx-keep-sections

Conversation

@DemchaAV

Copy link
Copy Markdown
Owner

Why

keepTogether() and keepWithNext() are resolved by LayoutCompiler:

  • A block that does not fit what is left of a page, but fits a fresh one, moves down whole.
  • A keepWithNext block moves down together with the first line of the block after it.

Word re-paginates a DOCX itself and the export told it neither. So a card the page holds together split across Word's page break, and a heading the page moves down with its body could be left at the foot of the page above.

What changed

  • DocxSemanticBackend.writeNode notes where the document body ends, writes the node, then hands everything the node wrote to keepOnOnePage:
    • w:keepLines on every paragraph.
    • w:keepNext on every paragraph but the last, which chains the block into one unit.
    • For keepWithNext, w:keepNext on the last paragraph too.
    • A table inside the block takes part row by row. Word reads keep-with-next on a row's paragraphs as keeping that row with the next one, so the last row ends the block like a last paragraph.
  • Only a block the layout placed on one page is kept (DocxLayoutMetrics.onOnePage: start page equals end page). The layout keeps a block together only when a page can hold it and lets a taller one flow. A block that ran over a page break is that taller block, so it is left to flow in Word too. With no layout there is nothing to go on, and nothing is written.
  • The check is generic over DocumentNode.keepTogether() / keepWithNext(), so it covers sections, modules and timelines. A LineNode with keepWithNext is dropped by this backend already, so it writes nothing.
  • Recipe row for sections and containers, a new capability-matrix row, and a CHANGELOG entry under v2.5.0.

Verification

In an editor. A 300×300 page with filler paragraphs above a six-line section, converted with LibreOffice (soffice --headless --convert-to pdf), card lines by page:

Filler Without keepTogether With keepTogether
16 lines 0–3 on page 1, lines 4–5 on page 2 all on page 2
18 lines 0–1 on page 1, lines 2–5 on page 2 all on page 2

The layout places the section the same way: pages 0–1 without the flag, page 1 with it.

Gate. Full reactor gate (clean verify over core, render-pdf/docx/pptx, templates, testing, qa, coverage) → BUILD SUCCESS. Examples after install: 93 green. No committed preview moved.

New DocxKeepTogetherTest (6 tests):

  • A kept block chains its paragraphs, leaves its last free, and leaves the next paragraph untouched.
  • A keepWithNext heading keeps with its body.
  • A plain section gets nothing.
  • A kept block taller than the page gets nothing.
  • A table inside a kept block chains row by row, and its last row ends the block.
  • A bare export without a layout gets nothing.

Sabotaging the one-page check and the last-paragraph exception fails 4 of the 6.

Lane: shared-engine (render-docx) — paragraph properties only; no public API change. Touches DocxLayoutMetrics and the CHANGELOG head next to #724, so whichever merges second needs a rebase.

keepTogether() and keepWithNext() are resolved by the layout, and Word
re-paginates on its own, so the export has to say them. A block the
layout placed on one page gives each of its paragraphs w:keepLines and
every one but the last w:keepNext, the last as well for keepWithNext;
a table inside it is chained row by row. A block that ran over a page
break in the layout is left to flow.
@DemchaAV
DemchaAV force-pushed the fix/docx-keep-sections branch from 5a528c8 to e8609fd Compare September 23, 2026 12:58
@DemchaAV
DemchaAV merged commit 98064a7 into 2.5-dev Sep 23, 2026
12 checks passed
@DemchaAV
DemchaAV deleted the fix/docx-keep-sections branch September 23, 2026 13:07
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