Skip to content

fix(docx): seat a composed chip's label where the page sets it - #879

Merged
DemchaAV merged 4 commits into
2.5-devfrom
fix/docx-composed-label-seat
Oct 11, 2026
Merged

DemchaAV merged 4 commits into
2.5-devfrom
fix/docx-composed-label-seat

Conversation

@DemchaAV

@DemchaAV DemchaAV commented Oct 11, 2026 •

Copy link
Copy Markdown
Owner

Why

A rota's shift chip — a shape composed in a table cell — is written as a one-cell table. Its row is held at the outline's height, and its one centred label line sits in it. In CobaltRota the chips are most of the sheet's lines, and the label stood off where the page sets it in both editors:

  • A label taller than its chip stood high. The page lays a shape's layers out in its outline. It starts a line taller than the outline at the outline's top and draws it past the outline below (LayerStackGeometry.verticalOffset clamps at zero). The export seated the cut line from the middle the cell centres it on, which is half of what the line is taller above where the page draws it. CobaltRota's stacked chips (8.17pt Carlito on a 9.97pt line, in a 9.18pt chip) stood 0.43pt high in their chips in Word and about 0.4pt in LibreOffice on Windows.
  • A label its chip holds whole stood low. Word and LibreOffice stand an exact line's baseline four fifths of the way down it. The page sets it its face's descent above the line's foot, and for 10.4pt Carlito the two are 0.26pt apart. A position counts in half points and is not written under one, and the label has no space above it to rise into. The single-shift chips' labels stood 0.29pt low in Word and 0.23pt in LibreOffice 24.2; the legend's stood 0.33pt low.

This was measured on the PDFs: each label's baseline against the chip's own box, page against editor, so that a misplaced chip separates from a misseated label.

What changed

In DocxSemanticBackend:

  • cutTheLabelToItsOutline seats a line taller than the outline from the outline's top. Its stacked line's topAbove is low - cut, where low is half of what the line is taller than the outline.
    • The cut (cutAlike, the old inline expression) keeps the letters inside the line where the page sets them: the top that much further from them, the foot that much nearer.
    • This applies only where Word sets the line on the shape's middle (setOnTheShapesMiddle): the line fits the height the shape's row holds, or the cell round the shape gives back all the shape grows by (giveBackWhatTheLabelNeeds).
    • Beside another cell, Word grows the shape downwards and sets the line from its top. So that case, and a label whose letters no line on that middle holds, are read from the middle as before.
  • seatTheLabelByItsLine writes a label the shape holds whole at the exact line height that stands its text on the page's baseline.
    • The cell centres the line, and the baseline stands three tenths of the line below the middle in both editors. CobaltRota's 12.7pt single-shift line is written 11.85pt; Lato, which the page sets lower, on a taller line.
    • The line is no shorter than keeps the letters INK_MARGIN inside it, measured from the line's own baseline. It is no taller than the room, and the chip and its row keep their height.
    • The page's own seat off the baseline (seatShift) moves with it, and what a bound leaves is the text's position.
    • It is skipped under a tenth of a point, and for a standard face the export does not embed: those letters are read through a stand-in, and a 10pt Helvetica line would come out 26pt.
    • The cell's margins are not used: LibreOffice 24.2 counts them inside the height a row holds, and moving them shortens the row there.
  • composedLabelOf is the label lookup the cut used, pulled out of it.

Docs: CHANGELOG.md (v2.5.0), the recipe's composed-shape paragraph, and the capability matrix's clip row.

Verification

  • ./mvnw -B -ntp clean verify → BUILD SUCCESS: render-docx 1374 run, 0 failures, 1 skipped, qa 1820, core 818, examples 93.

  • DocxComposedCellTest, over the stacked chips:

    • a 9.18pt chip's Carlito label is cut to 183 twips and lowered half a point;
    • a padded outlined chip's label is seated on the outline's middle;
    • an outlined chip alone in its row keeps 172 twips, and its cell gives back 16 and 16; an odd 29 twips go back 14 above and 15 below;
    • beside another cell, or with margins too small to give it all back, the label is read from the middle (160 twips), as before;
    • an "ÉÅ" label no middle line holds is written whole.
  • DocxComposedCellTest, over labels the chip holds whole:

    • a 17.5pt chip's 10.4pt Carlito label is written 237 twips, with the row 350, margins 0 and no position;
    • Lato is written 212 twips, the row still 280;
    • a 10.3pt chip clamps the line to 206 twips;
    • a 10pt chip leaves it at 197 (under a tenth);
    • descenders keep it at 244;
    • Spectral is written 238 with no position;
    • a padded chip's margins stay 40 and 40;
    • TextVerticalAlign.CENTER is written 222;
    • TextVerticalAlign.TOP is held at 298 by its capitals, with the rest a position of 8;
    • Helvetica is left at 185.
  • Seventeen sabotages, each caught by its own test.

    • On the over-tall seat:
      • the page centring the line;
      • the ink bound symmetric;
      • the line seated from the middle;
      • the inner box less the padding;
      • the seat on the middle with the line taller than its room, beside another cell or past the margins;
      • the letters' fallback dropped.
    • On the line seat:
      • the seat dropped;
      • four fifths for three tenths;
      • the letters' floor dropped;
      • the room ceiling dropped;
      • the tenth dropped;
      • seatShift dropped;
      • the line started the other way;
      • the seat counted twice for the letters;
      • a standard face seated.
  • DOCX fidelity corpus against 2.5-dev: only rota-cobalt moves.

    rota-cobalt Median p90 Lines nearer Lines further
    Word 0.38 → 0.17pt 0.46 → 0.38pt 87 5 (up to 0.14pt)
    LibreOffice on Windows 0.31 → 0.08pt 0.39 → 0.30pt 97 7 (up to 0.15pt)

    Windows baselines rewritten.

  • LibreOffice 24.2.7.2, the Linux job's build, also run in an ubuntu:24.04 container to measure the labels in their chips; its rota-cobalt matches the job's to the hundredth.

    • The job: rota-cobalt p90 0.90 → 0.52pt, median 0.17 → 0.22pt; 40 of its lines nearer and 53 further, by up to 0.3pt, all of them single-shift labels; no line of any other document moves.
    • In their chips, the single, legend and filled pair's labels stand within a tenth of a point of the page's in Word, LibreOffice on Windows and LibreOffice 24.2.
    • The Linux baseline is rewritten from this PR's artifact (run 38153180539) in its own commit.
  • Report: the same 969 notes. The corpus DOCX are byte-identical to the measured export.

Notes for review

  • The outlined pair's labels (7 lines) call for 0.82pt lower and are lowered a whole point by a position.
    • Word: from 0.43pt high to 0.3pt low.
    • LibreOffice on Windows, which moves text further than its position says: from 0.42pt high to 0.52pt low.
  • The single-shift labels in LibreOffice 24.2 now stand about 0.4pt high with their chips.
    • In their chips they are seated. What is left is where each editor sets the chip in its row: LibreOffice 24.2 0.25pt higher than the page, Word 0.15pt lower.
    • The label 0.23pt low had hidden that in LibreOffice 24.2. Where the chip stands is its own change.
  • The line's height, not the cell's margins, moves the text. Taken out of the height a row holds, margins keep the row in Word and LibreOffice on Windows, but LibreOffice 24.2 counts them inside an at-least height and shortens the row: the single-shift chips came out half a point shorter there. An exact line's seat four fifths down it moves the text alike in all three.
  • The text cells beside composed cells — the staff names, the covers — are left. Their row's margins are not moved where a composed cell would move with them.
  • A label whose letters leave between nothing and a hundredth of a point to cut on an over-tall line is seated from the top. That band of glyph metrics is too narrow to pin by a test.

Lane: shared-engine (DOCX backend) — render-docx, with the qa fidelity baselines; no public API.

- A label line taller than its shape's outline is seated from the
  outline's top, where the page starts it (the layer box clamps an
  over-tall child at its top), not from the middle the cell centres the
  written line on: CobaltRota's stacked chips' labels stood 0.43pt high
  in Word. The cut keeps the letters, set that much lower, inside the
  line (cutAlike), and only where Word sets the line on the shape's
  middle (setOnTheShapesMiddle): it fits the held height, or the cell
  round the shape gives back all the shape grows by. Elsewhere, or where
  no line on that middle holds the letters, it is read from the middle
  as before.
- A label the shape holds whole is moved towards the page's baseline by
  the margins of the shape's cell, out of the height its row holds
  (seatTheLabelThroughItsMargins): Word's exact-line seat four fifths
  down and the page's differ by a quarter point in Carlito, which a
  half-point position does not move. CobaltRota's single-shift labels
  stood 0.29pt low, its legend's 0.33pt. The page's own seat off the
  baseline moves with it; not a label whose text a position moves, not
  past the room the held height leaves, not under a tenth of a point.
- Docs: CHANGELOG (v2.5.0), the recipe's composed-shape paragraph, the
  capability matrix's clip row.
- Fidelity baselines rewritten (Word, LibreOffice on Windows): only
  rota-cobalt moves, median 0.38 -> 0.20pt in Word, 0.31 -> 0.09pt in
  LibreOffice.
- Tests: DocxComposedCellTest - a label taller than its chip seated from
  its top, raised and lowered by the margins, the room clamp, the tenth,
  a padded chip, the page's seat, a label a position moves, a line
  taller than the held height, letters no middle line holds, and the
  outlined chip read from the middle beside another cell or where the
  margins give back less.
- A label a composed shape holds whole is written at the exact line
  height that stands its text on the page's baseline
  (seatTheLabelByItsLine). Word and LibreOffice stand an exact line's
  baseline four fifths down it, and the cell centres the line, so the
  text stands three tenths of the line below the middle.
  CobaltRota's single-shift labels: the 12.7pt line is written 11.85pt.
- This replaces moving the line by the shape cell's margins out of the
  row's held height. LibreOffice 24.2, the build the Linux fidelity job
  runs, counts a cell's margins inside an at-least row height. The
  single-shift chips came out half a point shorter there, every staff
  row with them, and 77 lines stood further.
- The line is no shorter than keeps the letters three quarters of a
  point inside it, their reach read from the line's own baseline, and
  no taller than the room. A label in a standard face the export does
  not embed is left as it was, since its letters are read through a
  stand-in.
- Docs: the CHANGELOG entry, the recipe's composed-shape paragraph and
  the matrix's clip row.
- Baselines: Word and LibreOffice on Windows rewritten; only rota-cobalt
  moves.
- Tests: DocxComposedCellTest covers a label seated by a shorter line,
  a taller line, the room ceiling, the tenth, the letters' floor, a
  deep face, a padded chip, the page's CENTER and TOP seats, and a
  standard face left alone.
…posed chip label seat

Taken from the docx-fidelity artifact of this branch's CI run 38153180539 (LibreOffice 24.2.7.2). Only rota-cobalt moves: median 0.17 -> 0.22pt, p90 0.90 -> 0.52pt; 40 of its lines nearer, 53 further by up to 0.30pt (the single-shift labels, seated in their chips, which LibreOffice 24.2 sets 0.25pt above the page).
…umbers

The CHANGELOG entry, the recipe and the capability matrix name the tenth-of-a-point floor and the standard faces the line seat leaves alone, the margin case read from the middle, and LibreOffice 24.2's counts; the Javadoc of seatTheLabelByItsLine says which faces get a taller line, and cutTheLabelToItsOutline's measures from the outline.
@DemchaAV
DemchaAV merged commit 84c194e into 2.5-dev Oct 11, 2026
13 checks passed
@DemchaAV
DemchaAV deleted the fix/docx-composed-label-seat branch October 11, 2026 16:26
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