Skip to content

fix(docx): cut a panel at a page break in a section or panel inside it - #960

Merged
DemchaAV merged 1 commit into
2.5-devfrom
fix/docx-break-in-a-panel
Oct 25, 2026
Merged

DemchaAV merged 1 commit into
2.5-devfrom
fix/docx-break-in-a-panel

Conversation

@DemchaAV

@DemchaAV DemchaAV commented Oct 25, 2026 •

Copy link
Copy Markdown
Owner

Why

A panel — a painted section or container — is written as a table of one cell, and Word breaks no page inside a table cell. The export cut a panel into two tables at a page break among its own children. A break one level down, in a section or a panel inside it, went through writePageBreak, which writes its paragraph in the body: after the panel's table. A card holding a section with "Part A", a break and "Part B" came out with both parts in the card on the first page and the page breaking after the card, where the page sets "Part B" at the top of the second page, the card going on there.

Measured in Word 16 and LibreOffice 24.2 (CI's build):

  • A page break inside a table cell is ignored in both: a run's w:br w:type="page", a paragraph's w:pageBreakBefore, in a table of the body and in one nested in a cell, all left the text after it on the first page. No break can be written in its place in the cell.
  • The page goes on with a card a break runs through at the top of the next page, every container round the break drawn there again, fill and border on each piece, and no padding above the text there: a card padded 8pt holding a card padded 6pt set "Part B" at the page's top edge (engine PDF). What follows the break stands the spacing of the container it is in below that top.

What changed

  • DocxPanelPieces (new, package-private) cuts a container's children at page breaks among them and, at any depth, among the children of the sections and panels its cell writes them in: each piece a run of Parts, a child whole or the part of it between two breaks, marked where it opens and closes.
  • writePanel cuts a panel in the flow by those pieces. A panel inside one is written in the part of it each table holds (panelWindows), its top padding where that part opens; every part draws its fill and its four borders, as the page draws each piece of a card. Inside a table cell, a row's column or a stack's column a panel is written whole, as before.
  • writeContainerBody writes the part of a section a piece holds, its top edge where it starts and its bottom edge where it ends.
  • writeParts writes a piece's parts, owing the container's spacing above the first where it follows a break among the container's own children.
  • writePanelPiece gives a table after the first no top padding, as the page sets the text there. This also changes a break among a panel's own children, which kept the padding: its text after the break stood 8pt low in Word for a card padded 8pt.
  • writePlacedNode marks a cut container's anchor once, where it starts, and writeContainerChildren names it once: written once a table, a bookmark round each part kept only the last, the link and the page reference going to the page after the break.
  • Not cut: a break in an alignment, an anchor's wrapper or a stack of one layer inside a panel, and in a panel composed in a table cell, a row's column or a stack's column.
  • Docs: the recipe's Page breaks paragraph, the CHANGELOG.

Verification

  • Probes (scratch, not committed):
    • six documents in Word and LibreOffice 24.2: a run's break and a break-before, in a body table and a nested one, beside a break in the body;
    • five shapes through the engine and both editors: a section in a card, a card in a card, two sections deep, a break between blocks, a break among the card's own children. After the change every line stands on the page the PDF sets it on in both editors; in Word within 0.3pt of the page, 1.2pt under the inner card's 1pt border.
  • Tests:
    • DocxPanelPiecesTest (new): a break among a panel's children cuts it in two; one inside a section inside a panel inside cuts all three, the parts opening and closing where they should; a break in a node its cell does not write as a container is not cut; a break ending a panel leaves an empty piece; followsABreak reads only a break among the container's own children.
    • DocxPanelEdgeCasesTest (+6):
      • a break in a section inside a card cuts the card there: two tables, the break between, the second with no top padding;
      • a break in a card inside a card cuts both, each part written once;
      • what follows a break stands its section's spacing below the top, and the section's top and bottom edges stand only where it starts and ends;
      • a cut section's anchor is one bookmark, on its first part, and its corner note is said once;
      • a break opening a section, and two breaks in a row, leave nothing written twice;
      • a break in a card in a row's column is not cut;
      • a break among a card's own children now gives its second table no top padding.
  • Fail-closed: 9 sabotages, each run alone, all caught: no cut below the panel's own children; the continuation keeps its padding; a section's window not read; a nested panel's not read; a section's top edge, or bottom edge, at every piece; no spacing after a break; a cut section's anchor round every part; its notes at every part.
  • Corpus: no document of the 62 has a page break inside a panel; every document's word/document.xml is unchanged, and so is the report.
  • Gate:
    • ./mvnw -B -ntp clean verify -pl <core, render-pdf, render-docx, render-pptx, templates, testing, qa, coverage> -am → BUILD SUCCESS: core 823, render-pdf 339, render-docx 2195, testing 5, render-pptx 142, templates 127, qa 1830.
    • The JDK 17 knowledge checks pass with the API surfaces unchanged, and core javadoc has 0 warnings.
    • ./mvnw -f examples/pom.xml test → BUILD SUCCESS, 93 tests.

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

- DocxPanelPieces cuts a container's children at page breaks among them
  and, at any depth, among those of the sections and panels its cell
  writes them in; Word and LibreOffice break no page inside a table cell.
- writePanel cuts a panel in the flow by those pieces, a panel inside it
  written in the part each table holds; writeContainerBody writes a
  section's part, its edges where it starts and ends; what follows a
  break stands its container's spacing below the page's top.
- A table after the first has no top padding, as the page sets the text
  there; a cut container's anchor is marked, and its notes said, once.
- Tests: a section and a card inside a card cut there; spacing and edges
  at the cut; one bookmark and one note; nothing written twice; a card
  in a row's column not cut.
@DemchaAV
DemchaAV marked this pull request as draft October 25, 2026 12:06
@DemchaAV
DemchaAV marked this pull request as ready for review October 25, 2026 12:06
@DemchaAV
DemchaAV merged commit 0dbf225 into 2.5-dev Oct 25, 2026
13 checks passed
@DemchaAV
DemchaAV deleted the fix/docx-break-in-a-panel branch October 25, 2026 12:12
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