Skip to content

fix(docx): close a document ending in a table with an ordinary paragraph where its last page has room - #848

Merged
DemchaAV merged 6 commits into
2.5-devfrom
fix/docx-closing-paragraph
Oct 5, 2026
Merged

DemchaAV merged 6 commits into
2.5-devfrom
fix/docx-closing-paragraph

Conversation

@DemchaAV

@DemchaAV DemchaAV commented Oct 4, 2026 •

Copy link
Copy Markdown
Owner

Why

Word requires a paragraph after a closing table. The export always wrote that paragraph a point tall, with its mark hidden, so that it could never open a blank page under a full one. That made the end of every such document impossible to type at. 33 of the 62 corpus documents end in a table.

Measured in Word on CompactMono:

  • Typed at the caret Word gives the document's end, 70 lines ran into the table's last cell. That cell is a narrow column, and the lines filled five more pages of it.
  • Inserted at the very end, the same lines went into the hidden paragraph. None of them showed, and the CV stayed one page.

The editing protocol (#847) failed "type at the end" on all 33 documents.

What changed

DocxSemanticBackend.dropTheSpaceAtTheEnd now writes that paragraph as an ordinary one when the section's last page has room for it.

  • Room comes from the layout. roomBelowTheFlow takes the bottom of the lowest content fragment on the last page, less the bottom margin. Fragments under a path of the layout's own (@page-background, @page-zone…, @timeline-rail) are drawn elsewhere and do not count.
  • Enough room is twice a line of the document's dominant text size, at 1.25 the size (roomForAClosingLine): one line for the paragraph and one to spare for an editor setting the content above lower than the page does. Where the document has a dominant text style, Normal is written in that size, so the ordinary paragraph is Normal's line; where it has none — all its text in tables — 12pt is assumed.
  • No room: the hairline stays and is reported as APPROXIMATED under closing paragraph, naming the section in a document of several. The note says where text typed there goes: into the table's last cell or the hidden paragraph at the document's end, onto point-tall lines in an earlier section. In the corpus these pages are filled to 0–16pt above the foot, where an ordinary line could open a blank page, in LibreOffice first.
  • No layout (roomBelowTheFlow NaN): the hairline stays, and no closing paragraph note is added, since the section's "could not be laid out" note already says why.
  • Hiding (hideTheClosingMark) now applies only to that hairline (hairline(paragraph, points)), so an ordinary closing paragraph keeps a visible mark. Before, any empty paragraph after the closing table was hidden; one that is not the hairline — a rule drawn in a fully transparent colour, an empty paragraph with an exact line and no border — now keeps the height the layout gave it. The cell separator check uses the same helper, with its behaviour unchanged.

The recipe ("Measured geometry"), the capability matrix's table row and CHANGELOG are updated. Javadoc that described the paragraph as always a point tall now says when it is.

Verification

  • Editing protocol (test(docx): run the Word editing protocol over every document of the fidelity corpus #847, Word 16.0.20430). "Type at the end" on the 17 corpus documents that now close with an ordinary paragraph: 0 / 17 → 17 / 17. On 2.5-dev the typed lines ran into the closing table's last cell in all 17; now they run below the table onto a new page, visible at the body size. The other 16 documents ending in a table keep the hairline and are reported.
  • Word corpus. scripts/docx-visual/word-fidelity.ps1 -Update → BUILD SUCCESS, baselines unchanged: no line moved and no page was added.
  • LibreOffice (Windows). DocxFidelityCorpusTest -Dgraphcompose.docxFidelity=libreoffice → BUILD SUCCESS against the committed baseline.
  • Tests:
    • DocxVerticalSpacingTest:
      • a table ending a few points above the foot is followed by the 1pt exact paragraph with its mark hidden;
      • with room, the closing paragraph has no exact line and no hidden mark;
      • the report names closing paragraph only in the first case;
      • a page background filling the page to its foot takes no room from the closing paragraph.
    • DocxPanelHeightTest and DocxMultiSectionTest keep their hairline assertions on a table pushed to the page's foot by a spacer.
    • DocxMultiSectionTest: a middle section whose table leaves room below closes with an ordinary paragraph, and that paragraph carries the section's properties.
  • Sabotage. Each change fails a test:
    • always the hairline: 4 tests fail;
    • never the hairline: 4 tests fail;
    • hiding the mark of an ordinary paragraph too: 1 test fails;
    • counting @ fragments as content: 1 test fails.
  • render-docx. 898 tests green.
  • 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 → BUILD SUCCESS (qa 1820 tests).
  • Examples. CommittedAssetDriftTest → 3 / 3 green.

Notes

  • The 1.25 line share fits Lato and Calibri. A face set taller under Word's single spacing, such as Poppins at about 1.5, keeps one line of room, not two.
  • The 16 full pages keep the old behaviour: typing at their end still goes into the last cell. That is now reported rather than silent.

Lane: shared-engine (render-docx).

@DemchaAV
DemchaAV merged commit bb5b459 into 2.5-dev Oct 5, 2026
13 checks passed
@DemchaAV
DemchaAV deleted the fix/docx-closing-paragraph branch October 5, 2026 07:31
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