Skip to content

fix(docx): anchor a timeline's dot in its own cell of the entry's row, and let a dot rise above its line - #853

Merged
DemchaAV merged 4 commits into
2.5-devfrom
fix/docx-drawings-above-their-paragraph
Oct 5, 2026
Merged

DemchaAV merged 4 commits into
2.5-devfrom
fix/docx-drawings-above-their-paragraph

Conversation

@DemchaAV

@DemchaAV DemchaAV commented Oct 5, 2026 •

Copy link
Copy Markdown
Owner

Why

#852 anchors a DOCX's drawings in the paragraph whose text they stand beside, so they move when a reader edits the text above. Two cases still left most of the remaining shapes on the page:

  • A timeline's dot in a column of its own. CharcoalGold's entry is a row of five cells: date, gap, dot, gap, text. The dot's cell holds nothing but an empty paragraph. The nearest text is in the text's cell, which does not hold the dot across, so nothing took the dot and it stayed on the page. Among the shapes the export still left on the page, 247 were turned away by the nearest paragraph's cell.
  • A dot centred on a line. It stands above its paragraph's top, and fix(docx): anchor a drawing in the paragraph whose text it stands beside, so it moves with that text #852 let a shape rise above it by a stroke's width only.

What changed

The cell of the same row (DocxDrawingAnchors.Seat.place, inTheRow, openingPlace). When the nearest paragraph's cell does not hold a shape across, the cell of the same row that does takes it, in its first paragraph.

  • Across. Placed from that cell's text column. Its left edge is found by adding up the cells' written widths (tcW) from the paragraph's own cell. The walk stops at a cell with no width in twips rather than guess.
  • Down. Placed from the paragraph's own top.
  • Measured in Word 16.0.20430 and LibreOffice: in a row whose cells set their content from the top, each cell's first paragraph starts at the row's top. A dot in an empty 6pt cell beside a two-paragraph text cell stood at the text's first line top, to 0.05pt, in both editors. Both give every cell of a row the row's largest top margin, as DocxSemanticBackend.evenTheRowsMargins records, so the first paragraphs of a row start together whatever margin a cell states.
  • When no other cell is used:
    • the paragraph is not the first thing in its cell;
    • either cell sets its content from the middle or the bottom;
    • the other cell is part of a vertical merge;
    • the other cell opens with a table, or its first paragraph has a border above it.

Rising above the paragraph's top.

  • In the body, a shape may now rise above its paragraph's top by its own height, 24pt at most (FURTHEST_RISE), as a dot or an icon's disc centred on a line does. NavySidebar's 22.6pt badge is the largest such disc in the corpus.
    • Measured: Word and LibreOffice place a paragraph-relative offset of −4pt and −8pt exactly and draw the shape whole; Word's page metafile shows the same.
    • Why the 24pt cap. A taller shape rising further stands by other text: LibreOffice set an 11pt rule placed 45pt above its paragraph at the head of the next page.
  • In a table cell, a shape still rises by a stroke's width at most. A 20.5pt rule placed 1pt above its paragraph in TealPulse's contact-row cell moved that row's phone number 1.8pt in LibreOffice on Linux. It did not move in Word or in LibreOffice on Windows.
  • Rises of a point or less are placed at the paragraph's top. Word draws −0.13pt where it says, but its object model reports the offset as a Top of −999997.

Docs.

Verification

  • Editing protocol (Word 16.0.20430, the 35 corpus documents with drawings, -Scenario drawings-follow-text):

    Shapes beside moved text that follow it Documents passing
    2.5-dev (fix(docx): anchor a drawing in the paragraph whose text it stands beside, so it moves with that text #852) 104 of 228 19
    this PR 132 of 231 20

    54 more shapes are anchored in a paragraph than on 2.5-dev: 289 against 235.

  • Where shapes stand, unedited. Matched by kind and size in the Word and LibreOffice PDFs of the corpus before fix(docx): anchor a drawing in the paragraph whose text it stands beside, so it moves with that text #852 and with this PR:

  • Text fidelity. The Word, LibreOffice Windows and LibreOffice Linux gates pass, and every baseline is the same as on 2.5-dev: no line of text moves.

  • Tests: DocxDrawingAnchorsTest, 10 new tests:

    • a dot in a column of its own is anchored in its cell, from the row's top;
    • a neighbour's first paragraph starts where the paragraph does, whatever its margin;
    • no neighbour is used where the row's top is not known: the paragraph does not open its cell, or the neighbour centres its content;
    • no neighbour is used where the row cannot be walked:
      • the paragraph's own cell is centred;
      • the neighbour opens with a table, or is bordered above;
      • a cell between has no width in twips;
    • a neighbour to the right takes a shape, but one in a vertical merge leaves it on the page;
    • a dot centred on the first line rises above its paragraph's top by its offset;
    • in a cell a shape rises by a stroke at most;
    • a tall shape rises no further than a disc.

    Three earlier tests now state the new limits.

  • Sabotage. Each rule fails its test when broken:

    • the other cell not used, and the row's top not checked;
    • the paragraph's own alignment, and the neighbour's;
    • the neighbour's top, its border, and a table opening it;
    • a cell with no width walked past;
    • the rightward walk, and the merged cell;
    • the rise allowed only a stroke, or any rise;
    • the cell's stroke limit, and the cap;
    • the stroke clamp for all rises, or none.
  • render-docx: 948 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 (render-docx 948, qa 1820). CommittedAssetDriftTest 3 / 3 green.

Notes

  • Where cell widths come from. Only cells written with a width in twips are walked across: row columns and panels. A data table's cells carry none, so a shape beside them stays as fix(docx): anchor a drawing in the paragraph whose text it stands beside, so it moves with that text #852 left it.
  • A drawing run's size. The run carrying the drawings has no size of its own; every line it joins in the corpus is exact, and no line moves.
  • Still on the page:
    • shapes beside a table's own cell text or a list's items;
    • shapes in the gap between the page's two columns: TimelineMinimal's divider is one;
    • portraits far from any text;
    • shapes rising more than a stroke above a paragraph in a cell.

Lane: shared-engine (render-docx).

…, and let a dot rise above its line

A drawing in a table cell went into that cell only when the cell held it
across, and a dot set in a column of its own beside its entry's text had no
paragraph to go to, so it stayed on the page. It now goes into the first
paragraph of the cell of the same row that holds it, placed from the row's
top: Word and LibreOffice start each top-aligned cell's first paragraph
there, less its top margin. A drawing may also rise above its paragraph's top
by its own height, 24pt at most, as a dot centred on the first line does;
both editors place a negative offset exactly and draw the shape whole.
…raph, and start a neighbour cell at the row's top

A rule placed 1pt above its paragraph in a contact row's cell moved that row's
text 1.8pt in LibreOffice on Linux, though not in Word or LibreOffice on
Windows. A drawing in a cell rises above its paragraph's top by a stroke's
width at most again; the body keeps the rise by the drawing's height.

A neighbour cell's first paragraph is placed at the paragraph's own top: Word
and LibreOffice give every cell of a row the row's largest top margin, so the
first paragraphs of a row start together. One with a border above it is not
used. Tests cover the paragraph's own cell centred, a neighbour opening with a
table, a cell with no width in twips stopping the walk, and a cell in a
vertical merge leaving the drawing on the page.
@DemchaAV
DemchaAV merged commit 7c9808c into 2.5-dev Oct 5, 2026
13 checks passed
@DemchaAV
DemchaAV deleted the fix/docx-drawings-above-their-paragraph branch October 5, 2026 16:37
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