Skip to content

fix(docx): give a paragraph opening a row's column the gap above its first line out of the space above the row - #899

Merged
DemchaAV merged 3 commits into
2.5-devfrom
fix/docx-cell-last-line-lowered
Oct 14, 2026
Merged

DemchaAV merged 3 commits into
2.5-devfrom
fix/docx-cell-last-line-lowered

Conversation

@DemchaAV

@DemchaAV DemchaAV commented Oct 14, 2026 •

Copy link
Copy Markdown
Owner

Why

Word sets a paragraph's exact lines one height apart, so the page's gap between lines goes inside each line. A paragraph of n lines then holds one gap too many. applyLineGap takes that gap off the space above the paragraph, or off a table cell's top padding (takeFromTheTopOfItsCell).

A paragraph opening a column of a row of columns, or of a stack's columns, has neither. Its gaps were shared out over its lines instead, so its lines stood closer together than on the page:

  • EditorialProposal's goal cells, three lines each, stood 1.46 and 0.45pt low and 0.56pt high in Word, and 1.35 and 0.35pt low and 0.65pt high in LibreOffice.
  • OrangeOps' degree, two lines beside its badge, stood 0.45pt low and 0.45pt high in Word, and 0.30pt low and 0.55pt high in LibreOffice 24.2.

What changed

  • The gap from above the row. takeFromAboveItsRow takes what neither the space above nor a padding holds from the space above the row. That space is the paragraph before the table: its space below, and its space above too when it is an empty separator.
  • The lift. The paragraph notes the amount in raisedRows, as text standing above its cell does. raiseRows then lifts the row out of that space, and liftEachCell starts the row's other cells as much lower inside, so they keep their place. The paragraph's lines are written with the whole gap, the page's distance apart.
  • Held rows. A row held to a height (trHeight) is held as much taller (holdTheLiftedRowTaller). Held as it was, a lifted row that its content does not fill ends higher by the lift. SerifHeadline's certifications, five columns of one row held at the page's height, otherwise stood the achievements under them 1.3pt high in Word.
  • A floor. The lift only applies where it raises the first line half a point or more above where sharing the gaps out sets it. The line starts higher by the gap and grows by its share of it, and Word sets its baseline four fifths of the way down. A smaller rise left the lines higher than shared out where neither their position nor anything under them lowered them, and a cell's last paragraph has nothing under it. EditorialProposal's phase descriptions, 0.6pt short across two lines, stood 0.3pt high when lifted; their gaps stay shared out.
  • Where it applies. Only the first row of a row of columns or of a stack's columns (rowsLiftedForTheirGaps, new). Not a row opening its page, and not in an overlay.
  • Columns that open with a table. liftEachCell starts a cell that opens with a table lower inside below a hairline paragraph that holds the lift (startLowerAbove). A column painted as a panel is written as a table at the top of its cell, and a table has no space above it in Word, so before this such a column rose whole with its row. The existing lift for text standing above its cell goes through the same path.
  • One reading of the space above. raiseRows' reading of the space above a table is extracted into spaceAbove, which both callers use.

Measured

  • 5 of the corpus's 62 DOCX change: cv-orange_ops, cv-serif_headline, invoice-workspace, proposal-editorial and proposal-northline.

  • Lines that moved by more than 0.05pt:

    Editor Nearer the page Further from it
    Word 40 5
    LibreOffice on Windows 44 0
  • EditorialProposal's goal cells:

    Line Word LibreOffice on Windows
    first 1.46 → 0.14pt 1.35 → 0.25pt
    second 0.45 → 0.09pt
    third −0.56 → 0.16pt −0.65 → 0.25pt

    Its 90th percentile goes from 0.41 to 0.22pt in Word and from 0.65 to 0.35pt in LibreOffice.

  • OrangeOps' degree, first line and second:

    • Word: 0.45 / −0.45 → −0.27 / −0.21pt.
    • LibreOffice 24.2: 0.30 / −0.55 → −0.20 / −0.20pt.
  • NorthlineProposal's goal cells: Word 0.59 / −0.39 → 0.11 / 0.09pt.

  • SerifHeadline's certification titles: Word 1.33 → 0.49pt.

  • WorkspaceInvoice's note: Word 0.70 / −0.55 → −0.26 / −0.31pt.

  • Of the five lines further in Word:

    • three move one step of Word's 0.12pt grid: two in OrangeOps' entry under its degree, and one of NorthlineProposal's;
    • two of NorthlineProposal's descriptions move past the page. They stand 0.16 and 0.22pt low, where they stood 0.08 and 0.14pt high.
  • The report's 969 notes over the corpus are unchanged.

  • This PR's CI run measures LibreOffice 24.2 on Linux:

    • the same five documents move;
    • 46 lines nearer the page, 2 further by 0.05pt;
    • two medians nearer;
    • acceptance stays at 53 of 62.
  • The Word and Windows LibreOffice baselines are rewritten for the five documents. The Linux LibreOffice baseline follows from that run.

Verification

  • DocxLineGapAboveTheRowTest adds 7 tests:
    • a row's column takes the gap from above the row, its neighbour starting lower;
    • a row with no space above it shares its gaps out;
    • a column opening with a panel starts lower, below a hairline;
    • a row opening its page is not lifted;
    • a gap too small for the line's position to take back is shared out;
    • a stack's column takes the gap too;
    • a held row is held as much taller as it is lifted.
  • Undoing any one part fails the suite:
    • nothing taken above the row;
    • no floor;
    • a held row not grown;
    • a stack's columns not registered;
    • the space above not counted;
    • a column opening with a table left to rise;
    • any table's first row lifted.
  • ./mvnw -B -ntp clean verify → BUILD SUCCESS.

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

…first line out of the space above the row

A paragraph of more than one line holds one gap too many in Word, which comes off
the space above it or off its cell's padding. Opening a column of a row of columns
or of a stack's columns it had neither, and its gaps were shared out over its
lines: EditorialProposal's goal cells stood 1.46 and 0.45pt low and 0.56pt high in
Word, OrangeOps' degree 0.45pt low and 0.45pt high.

The row is now lifted out of the space above it by what the paragraph needs, as
for text standing above its cell. Its other cells start as much lower inside; one
opening with a table starts lower below a hairline paragraph holding the lift. A
row held to a height is held as much taller. Only where the lift raises the first
line half a point or more above where sharing the gaps out sets it, not on a
page's first row, and not in an overlay.
… rewrite the Word and Windows LibreOffice baselines

The line height row of the DOCX recipe and the changelog describe a paragraph
opening a row's column taking the gap above its first line out of the space above
the row. The Word and Windows LibreOffice fidelity baselines are rewritten for the
five documents the change moves.
…is inserted at with try-with-resources

As every other cursor the backend opens is closed, instead of the deprecated dispose.
@DemchaAV
DemchaAV merged commit 6683be9 into 2.5-dev Oct 14, 2026
13 checks passed
@DemchaAV
DemchaAV deleted the fix/docx-cell-last-line-lowered branch October 14, 2026 13:37
DemchaAV added a commit that referenced this pull request Oct 15, 2026
…ne (#908)

The Linux baseline held the five documents #899 moves (cv-orange_ops,
cv-serif_headline, invoice-workspace, proposal-editorial,
proposal-northline) as they stood before it, while every CI run since
measures them as #899's did. Both files now match the docx-fidelity
artifact of #907's run (38794754386) byte for byte: 45 lines nearer the
page, 2 further by 0.05pt, all others unchanged.
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.

2 participants