Repository navigation
fix(docx): set a later layer where the page does when its edge reaches above - #934
Merged
Merged
Conversation
…s above A layer written after another in a band or a column starts at the page's gap below the layers before it, less what its first block writes above its content itself. Where the block held more than that gap, the difference was dropped and the block wrote all of it. Measured in Word and LibreOffice, a caption with a 20pt margin or padding 7pt under the line above stood 12.7pt low, a table with a 20pt margin 12.6pt, and a picture contained in a box level with that line 12.9pt, the report saying it was written under the line. The resume is now measured to the block's content, its padding and a contained picture's room included, and may fall below zero, which the block's own edge gives back as it gives back a pull out of the block above. That holds for a paragraph, a picture, a list or a table written in its layer's own flow; any other first block keeps the old gap. The overlap the report names is measured to the content too. A list keeps its own margin and padding after the layer's gap, as a table does: the gap took their place, and a list with a 20pt margin opening a band stood 20pt high.
DemchaAV
marked this pull request as draft
October 21, 2026 09:08
…s back The overlap the report names was measured to every leaf's content, past its padding, while only a paragraph, a picture, a list or a table in its layer's own flow gives that padding back. A caption padded 20pt in a painted panel, its box over the line above, is written that much lower and was named before; it was no longer. Such a block is measured from its box again, as it is written. A block with an edge below zero keeps the old gap too: it takes that edge out where it is written, by its own writer's rules. An alignment or anchor wrapper, written as its content, counts as plain flow: a centred caption with a 20pt margin stood 12.7pt low in Word and LibreOffice, with nothing named, and now stands where the page sets it.
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
A stack's layers that overlap are written as a band, one after the other; side-by-side ones as
columns, where the layers sharing a column follow one another in its cell. Each later layer resumes
at the page's gap below the layers before it, less what its first block writes above its content
itself (
DocxLayerColumns.flow).The gap was measured to the block's box and kept at zero or more. Where the block held more above
its content than the gap, the difference was dropped and the block wrote all of it. Measured in
Word 16 and LibreOffice on Windows, against the page's distance from the line above:
and so did one centred by an alignment wrapper;
that line (25pt of room above it) 12.9pt. The report named each as written under the line, though
the page sets it below;
A list lost its own margin in a band. Its margin is carried until its first item, where the layer's
resume replaced it. A list with a 20pt margin opening a band stood 20pt high, one with 10pt 10pt
high, and one in a later layer 7pt high.
What changed
DocxLayerColumns.resumemeasures the gap to the block's content: its box's top, its paddingand a contained picture's room. It subtracts what the block writes above that content and may
fall below zero, never below minus that edge.
givesItsEdgeBack: a paragraph, a picture, a list or a table writtenin its layer's own flow, under plain containers alone (
inThePlainFlowOf): unpainted sectionsand containers, and the alignment and anchor wrappers the backend writes as their content. Its
margin and padding above must be at zero or more.
panel or a nested stack writes its edge inside them; a spacer writes neither its margin nor its
padding; and a block with an edge below zero takes it out where it is written.
contentInset(backend) is the content's offset in the box, aswriteImagewrites it:padding plus
roomRound. It is threaded throughDocxLayerColumns.of,bandandflow.highestContentreplaceshighestLeaffor the overlap the report names (Band#over). For ablock that gives its edge back, the overlap is measured to its content too, so a box whose padding
or room alone reaches above the line is no longer named. Any other block is measured from its box,
as before: it is written lower by that edge, and stays named.
resumeLayer(backend), at both the band and the column site, turns a resume below zero intopullBelow. That is the existing pull out of the block above, whichnewBodyParagraphandnewTabletake out of the space owed, then out of the block's own top edge.writeListresumes before adding its own margin and padding, aswriteTableWithItsOwnSpacingalready does.Verification
Probe, 18 cases, Word and LibreOffice alike:
a centred caption;
Every measured defect now stands within 0.4pt of the page's distance from the line above, and the
picture exactly where the page draws it. True overlaps are still written under and named: the
6pt-margin caption, which stood 5.7pt lower again, now stands where the 0pt one does, as does
the −4pt one. A row, a section, the spacers, the −4pt caption and the padded panel write byte for
byte as on the base, and the panel's caption is named as there.
Bands at a page's foot, re-run from the probe of the band keep: the bands the page keeps there
with a later layer's margin reaching above now stay there in Word, rather than moving to the next
page whole.
Corpus: all 62 DOCX are byte-identical to the base, and the report's 1246 notes are identical.
Example:
word-export-companion.docxwas regenerated. It differs from the committed one onlyin
docProps/core.xml, the export's time.Tests:
DocxLayerResumeTest(new, 14 tests):table, list in a later layer, list opening a band (no position raise);
below zero (the old gap), a block in a row (named, no spurious
space abovenote), a paddedcaption in a painted panel (named from its box), a spacer (its height kept).
DocxListItemLowerTest.anItemInAStackOverADrawingKeepsTheSpaceAboveItnow expects thelist's 10pt margin, nothing grown;
DocxListItemSeatTest.anItemOpeningABandWithNoSpaceWrittenAboveItIsRaisedByItsPositiondrops the list's margin, so its premise of no space above holds again.
13 sabotages, all caught:
./mvnw -B -ntp clean verify: BUILD SUCCESS.Knowledge pack: the API surfaces, stability doc, claims and routes checks pass, and core javadoc
passes with no warnings.
Notes for review
writes it nowhere, and the report names it with the spacer. A spacer with a 20pt margin 7pt under
the line above stands 7pt high, as before.
above. A caption with a 20pt margin in a row's cell stands 12.7pt low, as before.
contentInsetresolves a contained picture's data twice more while the stack is planned, forthe resume and for the overlap, on top of
writeImage's own resolution: a byte copy and a hasheach, per such picture in a later layer. A failure to resolve surfaces either way, only earlier.
Lane: render-docx (semantic backend).