Skip to content

fix(docx): hold a line of text and its icon at the page's height, taking the icon's reach from the gaps around it - #794

Merged
DemchaAV merged 2 commits into
2.5-devfrom
fix/docx-icon-lines
Sep 30, 2026
Merged

DemchaAV merged 2 commits into
2.5-devfrom
fix/docx-icon-lines

Conversation

@DemchaAV

@DemchaAV DemchaAV commented Sep 30, 2026 •

Copy link
Copy Markdown
Owner

Why

In Word, TimelineMinimal's contact lines each stood 0.9pt taller than on the page, and the page under them stood 4.4pt low. Each line is a 10.5pt icon lowered beside smaller text.

  • The icon passes its text, so makeRoomForPictures wrote the line "at least" the picture's reach, and Word grew it to its own natural height.
  • LibreOffice measures the text taller still: each line grew 2.2pt there, and the page drifted 11pt low.
  • The page itself makes the line tall enough for the icon. The line needs to stay exact at that height, with Word setting its text and icon where the page does.

What changed

  • holdPicturesInTheLine. Where a picture passes its text, a paragraph of one line of text written in its own Word paragraph keeps an exact line at the page's height of it (ParagraphLine.lineHeight).
    • seatInTheLine sets the text on the page's baseline, and the pictures move with it. The seat is applied however small it is.
    • Ink passing the page's line is given room from the gaps around it, where the page draws it. Room above comes out of the line's w:before, and the Word line starts that much higher, which the seat is computed from. Room below goes to hangingBelow, which the next paragraph or table takes off its space.
    • The line also keeps INK_ROOM_POINTS (half a point) past the ink on either side, taken the same way as far as the space above goes. Word rounds the picture's position and the text's seat to half points each, and without the room an icon as tall as its line lost 0.2 to 0.4pt at an edge.
    • The vertical seat (seatShift) moves the pictures with the text, and counts towards their reach.
  • PictureReach. It now carries pageLine, and above/below: signed distances from the ink to the page's line, where Word puts the ink.
  • Unchanged. These still fall back to makeRoomForPictures as before:
    • a line whose space above is shorter than the ink's own reach above it, which the first line of a page or cell can be;
    • a paragraph of several lines;
    • a line holding only a picture: it has no text to seat, and held exact its picture would stand on Word's baseline, its top cut on screen;
    • list items, table text cells, line pairs and stacks.
  • Docs. CHANGELOG.md, docs/recipes/docx-export.md (the Line height row and the "Pictures and icons in a line" section) and the inline-images row of docs/architecture/backend-capability-matrix.md are updated.

Verification

  • 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 gives BUILD SUCCESS (1791 + 127 tests).
    • After install, examples run 93 green.
    • The knowledge checks and extract-api --check pass.
    • render-docx runs 702 tests.
  • DocxInlinePictureTest (new tests). An icon line sits between two lines of text 3pt apart, so the room it takes above and below can be read from the space written above it and above the line after it:
    • Ink above, from a centred icon raised 1.5pt: the line starts 2pt higher, and it is the page's line plus that.
    • Ink below, as in TimelineMinimal: the line ends 1.85pt lower, and the next line's space is that much shorter.
    • An icon as tall as its line keeps half a point at each edge.
    • A held line is seated even when the shift is under half a point.
    • A first line whose ink meets its top is held and takes nothing above it.
    • A first line whose ink passes its top is grown "at least".
    • A line holding only a picture is not written exact.
    • Contact lines step as the page's to within 0.1pt.
    • PictureReach unit tests cover signed reach and how several pictures combine.
  • Changed tests:
    • The icon tests that pinned "at least" for one-line paragraphs now pin the exact line; "at least" stays pinned for a wrapped paragraph.
    • Two paragraph-mark tests keep their "at least" premise by wrapping to several lines.
    • A shape test reads the shape's position relative to its text's, since the seat moves both.
  • Each rule fails its own test when reverted: the room taken above; the hang handed below; the half-point room; the forced seat; holding a line whose ink fits under no space above; and the text requirement.
  • Template corpus (62 documents), against 2.5-dev, lines more than 2pt off:
    • In Word, 572 → 406:
      • TimelineMinimal 67 → 0 (median 4.1 → 1.4pt);
      • its cover letter 8 → 0;
      • MerchantInvoice 27 → 1;
      • CharcoalGold 28 → 0;
      • SidebarPortrait 20 → 0;
      • MintEditorial 14 → 0.
    • In LibreOffice, 933 → 737:
      • TimelineMinimal median 10.5 → 1.4pt;
      • MerchantInvoice 8.4 → 0.2pt.
    • Page counts are unchanged.
    • On Word's screen at 400%, every contact icon of TimelineMinimal shows its full height, as before.

Known limits

  • In LibreOffice, which ignores w:position on a picture and stands it on the baseline, a lowered icon in such a line stands higher than its text and loses what passes the line's top. Its text had drifted up to 11pt before. No exact geometry seats the icon for LibreOffice within TimelineMinimal's 3pt gaps.
  • Ink below a line that the space after it cannot hold makes Word's step that much longer, as the grown line did.
  • At the end of a cell, ink below the last line is not taken from the cell's bottom, so the row is that much taller.
  • In NavySidebar, LibreOffice's contact lines now step as the page's, which uncovers a constant 3.6pt offset of the sidebar that was there before.

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

@DemchaAV
DemchaAV merged commit 0041490 into 2.5-dev Sep 30, 2026
12 checks passed
@DemchaAV
DemchaAV deleted the fix/docx-icon-lines branch September 30, 2026 19:52
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