Repository navigation
fix(docx): keep a table's top edge on the page the layout moves it to - #821
Merged
Merged
Conversation
…, and take the Linux LibreOffice baseline from CI
This was referenced Oct 3, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
The layout keeps a block's own top edge where a page break moves the block down.
LayoutCompilerstarts the new page and places the block atpageTop − margin.top. The gap before it, and the bottom edge of the block above, are used up at the foot of the page above.Word has no space above a table, so the export writes a table's or a row's top edge below the paragraph before it. Where the page broke between them, that space stayed on the page above.
The long
LumaStudioInvoicestarts its third page with the notes and payment pair: a row with a 12pt top margin, moved there by the break. In Word, and in LibreOffice, the pair and everything in it stood 12pt high. Those are 20 of Word's corpus lines more than 2pt off.What changed
startsAPageOfItsOwn. A table or row in the body whose placement starts on a later page than the last block written there ended on (lastEndPage).lastEndPageis updated as each block in the body is written, inwriteNode, which now wrapswritePlacedNode.writeTableWithItsOwnSpacing. For such a block, what is owed below the block above is written on the page above (flushSpacingAfter), a band's resumed gap among it (resumeHerefirst), and a border or text hanging below that block is left there, aswritePageBreakleaves it. Only the block's own top edge is then owed.holdTheSpaceAboveOnItsPage. That edge, with any container edge opening there, becomes the height of a line of its own (w:lineRule="exact"), kept with the table (w:keepNext), its mark a point tall.w:before="244"before the table left the pair where it was, and an exact 244-twip line put it where the page has it.word-windows,libreoffice-windowsandlibreoffice-linuxare rewritten forLumaStudioInvoice(long), the one document this moves. The Linux one is taken from this PR's CI run (docx-fidelity-37047051867), where it goes from 22 lines past 2pt to 2.CHANGELOG.md, and the Tables row ofdocs/recipes/docx-export.md.Verification
Template corpus, re-measured in full. Only the long
LumaStudioInvoicechanges, in both editors.The third page in Word. "NOTES" stands at 73.68 against the page's 73.43, where it stood 11.75 high.
New test class,
DocxSpaceOnANewPageTest, four tests.Each rule fails the first test when taken out.
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 -amgives BUILD SUCCESS. render-docx runs 817 tests, qa 1814.Known limits
ModernProfessional's "Additional Information" opens its second page 7.65pt high in Word, andExecutive's second page about 3pt.ClassicSerif's second page lost a line to its first, and stood 16pt high.raiseRows) is not raised on the new page. The spacer is a line, not space above, and gives it nothing to take from. No document in the corpus has one.Lane: shared-engine (render-docx). No public API change.