Skip to content

fix(docx): let a line of pictures alone be as tall as its pictures where they fill the page's line - #814

Merged
DemchaAV merged 2 commits into
2.5-devfrom
fix/docx-slate-orange
Oct 2, 2026
Merged

DemchaAV merged 2 commits into
2.5-devfrom
fix/docx-slate-orange

Conversation

@DemchaAV

@DemchaAV DemchaAV commented Oct 2, 2026 •

Copy link
Copy Markdown
Owner

Why

In Word, SlateOrange's sidebar ran lower the further down it went: 36 of its 87 lines were more than 2pt off, p90 4.99pt.

Each skill is a table row: a 12.4pt icon in a paragraph of its own, no text in it, beside its label. That paragraph is composed in a table cell, so the layout lays out no line for it. It is written with no line height, and Word sizes the line itself. The line is its tallest run, and the paragraph's mark is a run. Set in the icon cell's style at 10.5pt, the mark's depth below the baseline went under the icon. Each row came out 0.2pt taller and the tenth 2.5pt low, with everything in the sidebar under them.

What changed

  • letThePicturesSetTheLine, at the end of writeParagraphRuns. A paragraph that holds only pictures and has no exact or least line has its mark and every run set at 1pt (ONE_POINT_IN_HALF_POINTS). Word then makes the line the pictures' height.
    • "Only pictures" is read off every run, a link's included (holdsOnlyPictures): no letter, break, tab, symbol or field. An internal link's runs are not among the paragraph's own, so getText() cannot see their text.
  • Only where the pictures fill the page's line (PICTURE_FILLS_ITS_LINE, 0.5pt).
    • The page's line is the laid-out one where there is one. For a paragraph the layout laid out none of — composed in a table cell, a page zone's, one of an export without a layout — it is its style's line height, measured with the fonts the layout measured with (styleLineHeight).
    • A face those fonts do not hold measures 0, and the line is left as it was. An export whose layout failed is written without one, and measuring here does not fail it instead.
    • A smaller picture, in a line the page sets at its font's height, keeps the mark as it was. Applied to every picture line, VioletGrid's bullet dots had their lines cut to the dots, and every bullet stood 5.8pt high in Word. That is why the rule is scoped.
  • Baselines. word-windows is rewritten for SlateOrange, the one document this moves.
  • Docs. CHANGELOG.md, and the Line height row of docs/recipes/docx-export.md.

Verification

  • Template corpus, re-measured in full against 2.5-dev's baselines. Every row of both editors' measurement was compared, not only the gate's regression check. The figures are the gate's own: before is the committed baseline, which 2.5-dev's output re-measured to byte for byte.

    Editor Document Pages Lines found Past 2pt Median p90
    Word 16.0.20430 SlateOrange 1 85 of 87 36 → 9 0.62 → 0.54pt 4.99 → 3.46pt

    No other document changes in Word. No document changes in LibreOffice.

  • The cause, measured before the code. In the exported DOCX the ten icon paragraphs were set by hand, once with a 1pt mark and runs and once with an exact 12.4pt line. Converted in Word, both gave 8 lines past 2pt, and "Time Management" went from +2.50pt to +0.58 by line origin. The exact line is not used: Word stands a picture on a baseline four fifths down an exact line, and cuts its top on screen.

  • New tests, DocxPictureLineTest:

    • a 12pt icon in a 10pt style, composed in a table cell, has its mark and its run at 1pt;
    • a 4pt dot in a 12pt style keeps its mark;
    • an icon beside an internal link's text, written without a layout, keeps the link's size and its mark;
    • a line of pictures in a face no font holds, written without a layout, exports.
  • Each rule fails its own test when taken out.

    • Without the call, the icon test fails.
    • Without the fill check, the dot test fails.
    • With getText() in place of holdsOnlyPictures, the link test fails.
    • Without the catch in styleLineHeight, the unknown-face test throws.
  • 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. render-docx runs 785 tests, qa 1813.

Known limits

  • 9 of SlateOrange's lines are still past 2pt in Word:
    • the three achievement titles, at +4.5pt;
    • "Availability" and "Willing to relocate", at +4.1pt;
    • the four job titles, at −3.5pt. A side-by-side rendering shows Word setting them where the page does. Most likely the gate's pairing of that line produces the number; this is not yet confirmed.
  • In LibreOffice SlateOrange stands about 3pt low throughout, from its masthead down. That is a separate cause.
  • A paragraph Word sizes and that wraps onto a second line of smaller pictures would have that line cut to them too. No corpus document has one.
  • The Linux LibreOffice baseline needs no change, since LibreOffice is unchanged.

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

…d, and measure a face no font holds as unknown

An internal link's runs are not among the paragraph's own, so getText()
missed their text and set it at a point; holdsOnlyPictures reads every
run for a letter, break, tab, symbol or field. A face the measured fonts
do not hold measures 0 instead of failing an export written without a
layout.
@DemchaAV
DemchaAV merged commit 0422765 into 2.5-dev Oct 2, 2026
13 checks passed
@DemchaAV
DemchaAV deleted the fix/docx-slate-orange branch October 2, 2026 07:50
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