Skip to content

fix(docx): space a centred or right-aligned line of its own to the page's width - #841

Merged
DemchaAV merged 2 commits into
2.5-devfrom
fix/docx-horizontal-offsets-7
Oct 4, 2026
Merged

DemchaAV merged 2 commits into
2.5-devfrom
fix/docx-horizontal-offsets-7

Conversation

@DemchaAV

@DemchaAV DemchaAV commented Oct 4, 2026 •

Copy link
Copy Markdown
Owner

Why

Word states a type size in half points, so it sets a size the page gives to the tenth a little larger or smaller, and a line that much wider or narrower. A line set from its left keeps its first letter where the page puts it; a centred line moves it by half the difference, a right-aligned one by all of it.

  • CenteredHeadline's 8.3pt contact line, set at 8.5, stood 10pt wider in Word, its first letter 4pt left of the page's.
  • ClassicSerif's 8.7pt one stood 4pt right.
  • Most of the lines still more than 2pt off across were of this kind: centred and right-aligned lines of their own, their aligned edge in place, their first letter not.

Scaling the glyphs (w:w) stays out: a scale would stay on the text a reader types next, and measured, it changed where Word broke CompactMono's lines.

What changed

  • twentiethsToThePage decides, from the layout's line, how far apart to space a centred or right-aligned left-to-right paragraph the layout set on one line: the difference between the page's width and Word's at its half-point size (askedWidth against widthAtWordsSize) spread over its letters, in twentieths of a point, to the nearest. Twentieths, not whole tenths: on the corpus they put 74 lines in Word nearer the page where whole tenths put 64.
  • setTheLineAsWideAsThePage (after writeParagraphRuns) writes that as character spacing (w:spacing) on every run, an internal link's included (runsIn). The size is left as Word sets it.
  • Left alone:
    • A line of text and a picture or a shape: the picture is written at its own size, and the run beside it may already be closed up to make room for it (giveBackWidth).
    • A line that already carries tracking: Word rounds tracking its own way and spaces the last letter too, which the size does not tell. Spaced on top, TealPulse's tracked tagline went from +0.44 to −1.00.
    • A difference under a point: measured, ObsidianInvoice's 9.34pt amounts, half a point wider by the size alone, came out as wide as the page's in Word, and spacing them moved them half a point off.
  • oneLinesMeasure. A centred or right-aligned line of its own is given room for the width Word ends up setting it at, a few hundredths past it: the page's where its letters are spaced, its own at Word's size where not. Reckoned from Word's wider width, a line condensed back to the page's would keep room it no longer needs, and in a cell, where the left edge cannot give it, that room comes off the right and moves a right-aligned line right.
  • Docs. Recipe (half-point sizes), capability matrix (paragraph row), CHANGELOG.

Verification

  • Word corpus. scripts/docx-visual/word-fidelity.ps1 -Update (Word 16.0.20430) → BUILD SUCCESS.
    • Lines past 2pt across: 23 → 12.
    • 74 lines nearer the page, one 0.02pt further (MerchantInvoice −0.29 → +0.31); the down column is unchanged in every row.
    • Among them: MeteredInvoice's amounts, BlueBanner, VioletGrid, CenteredHeadline's contact line (−3.96 → +0.60) and ClassicSerif's (+4.03 → −0.53).
  • LibreOffice (Windows). Lines past 2pt across: 29 → 18. 70 nearer, five MeteredInvoice amounts at most 0.11pt further (+0.17 → −0.28); nothing moved down.
  • LibreOffice (Linux). The baseline is this PR's CI artifact: past 2pt across 310 → 301, 71 lines nearer, 4 further. Those four are Panel's right-aligned contact lines: they move the same point right on every platform, nearer the page on Windows in both editors (Word −0.72/−0.42 → +0.36/+0.30) and further on Linux, where LibreOffice already set them 7.4pt right.
  • Tests. DocxWordSizeMeasureTest, four new:
    • a centred and a right-aligned line of their own are spaced the page's width less Word's, a letter at a time: narrowed at 8.3pt, widened at 6.2pt;
    • a line set from its left, a tracked line, and one off by under a point are not spaced;
    • a centred line Word sets narrower that fills its box is given room for the page's line and a few hundredths more;
    • a right-aligned line Word sets wider, spaced back, is given room for the page's line and a few hundredths, not Word's wider one.
  • Sabotage. Each rule fails its test:
    • spacing off;
    • a clearance of 0.3pt;
    • no tracking guard;
    • any alignment spaced;
    • the room as Word's width alone (109.3 against the 111.0 needed);
    • the room as the larger of the two widths whether spaced or not (132.3 against the page's 129.2).
  • render-docx. 894 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

  • Text typed into a spaced line takes the same spacing. Recorded in the recipe.
  • The expected spacing in the tests follows the same model as the code. The corpus is what measures it against Word.

Lane: shared-engine (render-docx).

…ge's width

Word sets a size the page gives to the tenth at the half point, and a
centred line moves its first letter by half the width that makes, a
right-aligned one by all of it: CenteredHeadline's 8.3pt contact line
stood 4pt left of the page's. Where the difference is a point or more,
the line's letters are now spaced by it (w:spacing), the size left as
Word sets it, and the line is given room for the page's width; a tracked
line and a difference under a point are left alone.

Lines more than 2pt off across fall from 23 to 12 in Word and from 29 to
18 in LibreOffice; nothing moves down.
…tting it at

twentiethsToThePage decides once, from the layout's line, whether and how
far a centred or right-aligned line's letters are spaced, and the room
oneLinesMeasure gives it follows: the page's width where it is spaced,
its own at Word's size where not. Reckoned from Word's wider width, a line
condensed back kept room it no longer needed, which in a cell comes off
the right and moved a right-aligned line right. A line with a picture or
a shape is left alone; the runs are wrapped through the non-deprecated
XWPFRun constructor. The Linux LibreOffice baseline is this branch's CI
measure.
@DemchaAV
DemchaAV merged commit 4d8e5b0 into 2.5-dev Oct 4, 2026
13 checks passed
@DemchaAV
DemchaAV deleted the fix/docx-horizontal-offsets-7 branch October 4, 2026 14:03
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