Skip to content

fix(docx): give a centred or right-aligned line that fills its box Word's room - #832

Merged
DemchaAV merged 2 commits into
2.5-devfrom
fix/docx-violet-wrap
Oct 3, 2026
Merged

DemchaAV merged 2 commits into
2.5-devfrom
fix/docx-violet-wrap

Conversation

@DemchaAV

@DemchaAV DemchaAV commented Oct 3, 2026 •

Copy link
Copy Markdown
Owner

Why

Word states a type size in half points (w:sz), so a size the page sets to the tenth is written to the nearest half: 6.8pt as 7pt, a line 2.9% wider than the page's. measureAtWordsSize already gave a flush-left paragraph that much more measure. A centred or right-aligned paragraph was left at the page's measure, because moving its one edge would move the line.

VioletGrid's skill tiles centre a 6.8pt heading in a box it fills. In Word "INFORMATION ARCHITECTURE" broke onto two lines, and the tile's text stood 7.5pt low under it. These were the last two lines in the Word corpus more than 2pt off.

What changed

DocxSemanticBackend.measureAtWordsSize:

  • Which paragraphs it now covers. A centred or right-aligned paragraph of one laid-out line gets room, but only when its line at Word's size, plus the 3% ONE_LINE_FACE_SLACK, does not fit its box (oneLinesMeasure).
  • Where the room comes from. It goes on the edge the text does not lean on:
    • a centred line takes half from each side indent, so its centre stays where the page sets it;
    • a right-aligned line takes it all from the left indent, so its right edge stays.
  • In a table cell. The left indent gives no more than it already has, less a hanging first line, because Word draws no text past a cell's left edge (leftIndentTwips). The right indent gives the rest: the line fits, and moves by that much.
  • A line of pictures alone gets no room: a picture is written at its size and takes the same room in Word.
  • What stays as it was:
    • A centred or right-aligned paragraph of several lines. A wider measure there pulls another word onto a line: ClassicSerif's centred summary, which Word already breaks at other words than the page, took "br…" up a line when I tried it.
    • A short line whose box has room. wordsMeasure's one-line rule also widens by room × share, which pushed even a short centred "0.00" cell's indents past the cell's edge, and Word then set it 0.12pt off its place.
  • Refactor. widthAtWordsSize(line) is pulled out of wordsMeasure, and both measures use it; wordsMeasure's result is unchanged.

Scaling the glyphs (w:w) instead was measured and rejected. Word breaks scaled text differently from how it draws it: CompactMono's 99%-scaled line drew at the page's width and still broke "8" onto the next line. TerracottaRail lost a line, and the Word corpus went from 2 lines past 2pt to 34.

Verification

  • Word corpus. scripts/docx-visual/word-fidelity.ps1 (Word 16.0.20430) → BUILD SUCCESS, with the baseline rewritten by -Update.
    • Only VioletGrid moved. Its lines more than 2pt off fall from 2 to 0, and that is now 0 across the corpus.
    • Its p90 drops from 1.09 to 0.37pt, and one more line matches (77 → 78 of 80): the heading's.
  • Exported DOCX. Of the 62 corpus documents, only cv-violet_grid.docx changed.
  • Horizontal check. The corpus gate measures vertical drift only, so I compared Word's PDFs word by word before and after: 2 words nearer the page, none further.
  • LibreOffice (Windows). VioletGrid's median falls from 5.21 to 0.36pt, its lines past 2pt from 39 to 37, and one more line matches (76 → 77). LibreOffice still sets that CV on two pages. I rewrote the Windows baseline with update=true. The Linux baseline is this PR's CI artifact docx-fidelity-37145495395, which moved the same document the same way and nothing else.
  • DocxWordSizeMeasureTest, +7 tests (18 total):
    • a centred line filling its box is given room at both edges alike (centre kept, the line fits at 7pt);
    • a right-aligned one is given room at its left, its right edge kept;
    • a right-aligned line filling a cell takes no text past the cell's left edge and still fits;
    • in a cell, a left indent of half a point gives all of it and the right indent the rest;
    • a centred line of a picture alone is left alone;
    • a short centred line is left alone, at both edges;
    • a right-aligned paragraph of several lines is left alone.
    • The existing multi-line centred test still holds.
  • Sabotage. Each rule fails the test that pins it:
    • the old centre/right exclusion fails the two fill tests;
    • wordsMeasure in place of oneLinesMeasure fails the short-line test;
    • dropping the one-line condition fails the multi-line test;
    • a centred line taking it all on the right fails "both edges alike";
    • dropping the cell floor fails the cell test;
    • a cell's left indent giving nothing fails the half-point test;
    • dropping the text guard fails the picture test.
  • 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.
  • Examples. CommittedAssetDriftTest in examples → 3/3 green, no committed preview moved.

Known limits

  • A hanging first line in a cell. Its floor is untested: the case needs a centred, bulleted line that fills its cell.
  • A centred paragraph of several lines can break at other words in Word. VioletGrid's "Low to high-fidelity wireframes…" tile, and ClassicSerif's summary, keep their line count, so nothing below them moves.

Lane: shared-engine (render-docx). DOCX export geometry only, no public API change.

@DemchaAV
DemchaAV merged commit 6dcad23 into 2.5-dev Oct 3, 2026
13 checks passed
@DemchaAV
DemchaAV deleted the fix/docx-violet-wrap branch October 3, 2026 19:09
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