Skip to content

fix(docx): keep the space above a spacer opening a stack of one layer - #930

Merged
DemchaAV merged 2 commits into
2.5-devfrom
fix/docx-one-layer-spacer-band
Oct 20, 2026
Merged

DemchaAV merged 2 commits into
2.5-devfrom
fix/docx-one-layer-spacer-band

Conversation

@DemchaAV

@DemchaAV DemchaAV commented Oct 20, 2026 •

Copy link
Copy Markdown
Owner

Why

A spacer the layout moves to a new page holds the space the layout leaves above it in its own
line (writeSpacer → holdTheGapAboveInTheLine), since Word drops a paragraph's space above at
the top of a page. That was gated on overlayDepth == 0, outside every stack. #929 held a stack
of one layer's first paragraph or picture, but not its first spacer.

Measured in Word 16 and LibreOffice on Windows: a stack with a 14pt margin, its layer a section
of an 8pt spacer and a paragraph, moved whole to page 2.

Page Before, Word / LibreOffice After, Word / LibreOffice
the paragraph's baseline on page 2 108.2 85.3 / 85.3 107.0 / 107.0
the same, the spacer in a section of its own 108.2 — 107.0 / 107.0

Before, the space above the text was gone: the stack's margin and the spacer's height, 22.9pt
in all. Nothing was named. With the spacer's line holding 14pt alone, Word set that line at the
foot of page 1 and the text at page 2's top as before. The layout moves the stack, an atomic
node, whole, but Word breaks between its paragraphs.

What changed

  • writeSpacer holds the gap above in its line where overlayDepth == oneLayerDepth, a
    stack of one layer's included, as fix(docx): keep the space above a stack of one layer opening a page #929 does for a paragraph and a picture. A band is left as
    it was.
  • holdTheGapAboveInTheLine takes off the layer's offset in its stack, which is named and
    not written (layerOffsetNotWritten, 0 in the flow).
  • A spacer opening such a stack on a new page is kept with what follows it in the stack
    (w:keepNext), via a new followedInItsStack.
    • That is something after it, or after a container it ends, up to the outermost stack of one
      layer. Examples: a spacer closing a section that the layer's text follows, or one closing a
      stack inside the stack. The layout moves the outermost stack whole.
    • A spacer with nothing after it in its stack is kept with nothing.
    • In the flow a spacer is not kept, as before.
  • Docs:
    • the recipe's spacer row;
    • CHANGELOG v2.5.0.

Verification

  • Corpus, 62 DOCX before and after: all 62 are byte-identical, and the report's 1246 notes
    are identical.
  • Example: word-export-companion.docx was regenerated. It differs from the committed one only
    in docProps/core.xml, the export's time.
  • DocxStackOfOneLayerOnANewPageTest, 6 new tests:
    • the spacer's line holds the stack's margin and the spacer is kept with the text;
    • a spacer ending a section the layer's text follows is kept with it;
    • a spacer closing a stack inside the stack is kept with the outer stack's text;
    • a spacer alone in its stack, with a paragraph after the stack, is kept with nothing;
    • under a layer offset, the line holds the margin alone and the offset is named;
    • opening a band: left to the band.
  • DocxSpaceAboveOnANewPageTest: a flow spacer opening a page is still not kept with what
    follows.
  • 9 sabotages, all caught:
    • held in the flow only;
    • held in any overlay;
    • not kept;
    • kept with nothing following;
    • the offset held;
    • kept in the flow too;
    • followed in its own parent only;
    • the walk past the stack;
    • the walk stopping at the innermost stack.
  • ./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.

What this leaves, each recorded as a separate step:

  • A band nested in a stack of one layer that opens a page loses the stack's margin (measured,
    page 100.2, Word and LibreOffice 85.3). A band in the flow does the same, so it goes with the
    band step.
  • Paragraphs inside a stack of one layer are kept together only as the line holding an edge
    and the first block. Word can still split a stack whose start fits at the foot of the page
    above, as it could before. Not measured.

Lane: render-docx (semantic backend). DOCX output changes only for a spacer opening a stack
of one layer that the layout moves to a new page.

A spacer the layout moves to a new page holds the space above it in
its line, but only outside every stack. A stack of one layer opening
with a spacer, moved to a new page, lost the space above its text:
measured in Word and LibreOffice, a stack with a 14pt margin and an
8pt spacer before its text stood 22.9pt high, the spacer written at
the foot of the page above.

The spacer's line now holds that space there, less its layer's offset,
which is named, and the spacer is kept with what follows it in the
stack, which the layout moves whole.
@DemchaAV
DemchaAV marked this pull request as draft October 20, 2026 14:21
A spacer opening a stack of one layer inside another was kept only
with what follows it in the inner stack. The layout moves the outer
stack whole, so a spacer closing the inner one was not kept with the
outer stack's text after it, on the same page. It is now kept with
anything that follows it up to the outermost stack of one layer.
@DemchaAV
DemchaAV marked this pull request as ready for review October 20, 2026 14:32
@DemchaAV
DemchaAV merged commit 70ba645 into 2.5-dev Oct 20, 2026
13 checks passed
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