Skip to content

fix(docx): write a chip's side padding as the room it takes on the page - #844

Merged
DemchaAV merged 3 commits into
2.5-devfrom
fix/docx-receipt-status-chip
Oct 5, 2026
Merged

DemchaAV merged 3 commits into
2.5-devfrom
fix/docx-receipt-status-chip

Conversation

@DemchaAV

@DemchaAV DemchaAV commented Oct 4, 2026 •

Copy link
Copy Markdown
Owner

Why

A chip's fill is written as run shading (w:shd), and shading covers only the letters. So a chip's horizontal padding, which widens its run on the page, was not in the file. ModernReceipt's status chip is right-aligned, with 9pt of padding on each side. In Word it stood 9.16pt right of the page's position; it was the corpus's largest remaining across offset in Word.

What changed

DocxSemanticBackend writes the padding as character spacing (w:spacing), on the paragraph path and the list path alike.

  • spaceAfterTheLastLetter puts the room after a run's last letter.
    • That letter goes into a run of its own with a copy of the run's rPr, so the letters before it keep their spacing.
    • The new run stays inside the same w:hyperlink (runAfter), so a linked phrase stays one link.
    • The split is by grapheme (BreakIterator), so an accent stays with its letter.
    • In text broken over lines, it is the last letter of the last line.
  • roomAfter decides how much room goes after a run's last letter:
    • the run's own right padding, when the run is a chip — Word shades the spacing with the chip, so the right padding is shaded as on the page;
    • plus the left padding of a chip that comes next — this space sits after the text before the chip, so it is unshaded, or in the fill of the chip before when that text is a chip.
  • lastSpaceableLetter says where no space can be written:
    • After right-to-left letters — in a right-to-left paragraph, and Hebrew or Arabic in a left-to-right one. In run order such a letter stands at the word's left, and an Arabic letter in a run of its own would lose its join.
    • After a symbol or an emoji. The JDK 17 baseline's BreakIterator does not keep a flag or a joined emoji sequence whole.
  • setTheLineAsWideAsThePage now adds its letter spacing to what a run already carries instead of overwriting it. This changes nothing for other lines: twentiethsToThePage returns 0 for tracked lines and lines with shapes, which are the only lines that already carry spacing.
  • widthAtWordsSize no longer scales a chip's padding to Word's half-point type size, because the padding is now written in points.
  • chipLost says, chip by chip, how each side was written. The left padding is unshaded space, space in the fill of the chip before it, or not in the file (leftPaddingAs). The right padding is reported where it is not written. The padding above and below the letters is reported as before. This replaces the old "its padding is not in the file". A chip's own text never holds a line break: InlineRun.textRuns turns one into a space.

The Javadoc, the recipe's chip section, the capability matrix row and CHANGELOG are updated.

Verification

  • Word corpus. scripts/docx-visual/word-fidelity.ps1 -Update (Word 16) → BUILD SUCCESS.

    • ModernReceipt's status chip: +9.16 → +0.16pt across.
    • No other row changes, down or across.
  • LibreOffice (Windows). The same chip goes 9.21 → 9.26pt; no other row changes. LibreOffice sets no spacing after the last letter of a line, and this chip ends its line.

  • Mid-line chips. The corpus has no chip with side padding other than this one, so mid-line chips were measured on an ad-hoc right-aligned document of five lines, not committed. The chips there had 9pt padding:

    • on both sides, mid-line;
    • on the left only;
    • on the right only;
    • ending the line;
    • mid-line on a centred line.
    Word LibreOffice (Windows)
    With the change every line within 0.1pt the four mid-line lines within 0.3pt
    Without it (LibreOffice only) — 18.25 / 9.25 / 9.25 / 9.18 / 9.19pt

    The line ending in a chip stayed at 9.23pt in LibreOffice with the change.

  • LibreOffice (Linux). This PR's CI artifact matches the committed Linux baseline line for line, so that baseline is unchanged.

  • Tests: DocxInlineBackgroundTest, 15 new tests.

    • Right padding is spacing on a split-off, shaded last letter (180 twentieths).
    • Left padding is spacing on the unshaded letter before the chip.
    • Two adjacent chips add both paddings.
    • In a list item, both right and left padding are written.
    • A centred line's spacing adds to the padding's.
    • Padding is not grown to Word's size: an 8.3pt "Paid" with 40pt padding gets no line spacing.
    • A linked chip stays one w:hyperlink holding two runs.
    • A decomposed é stays whole.
    • Text broken over lines spaces the last letter of its last line.
    • A chip with no padding stays one run with no spacing.
    • A chip in a right-to-left paragraph is not spaced.
    • Hebrew in a left-to-right line is not spaced.
    • A flag ending a chip is not cut in two.
    • The report, chip by chip, covers five cases:
      • after text;
      • opening its line;
      • after another chip;
      • right-to-left;
      • right padding only, which gets no note.

    DocxRunStyleTest and DocxPanelEdgeCasesTest now expect the chip's last letter in its own run.

  • Sabotage. Each change fails a test:

    • no spacing at all (spacing set to 0): 12 tests fail across the three classes;
    • the line spacing overwriting the padding's;
    • a second hyperlink;
    • a code-point split;
    • the padding grown to Word's size;
    • spacing after right-to-left letters;
    • a flag split by its last code point.
  • render-docx. 909 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

  • LibreOffice and a chip that ends its line. LibreOffice drops the right padding of a chip that ends its line, so a right-aligned one stands that far right of Word's. The status chip is that case, so it stays 9.26pt right in LibreOffice. A centred one should be off by half as much, but that case was not measured.

Lane: shared-engine (render-docx).

@DemchaAV
DemchaAV merged commit 6768353 into 2.5-dev Oct 5, 2026
13 checks passed
@DemchaAV
DemchaAV deleted the fix/docx-receipt-status-chip branch October 5, 2026 06:54
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