Skip to content

fix(docx): name in the report the geometry a block is written without - #857

Merged
DemchaAV merged 2 commits into
2.5-devfrom
fix/docx-report-geometry-losses
Oct 6, 2026
Merged

DemchaAV merged 2 commits into
2.5-devfrom
fix/docx-report-geometry-losses

Conversation

@DemchaAV

@DemchaAV DemchaAV commented Oct 5, 2026 •

Copy link
Copy Markdown
Owner

Why

The DOCX export left part of a block's own geometry out of the Word file and said nothing, while DocxExportReport promises to name every loss:

  • a logo given a left margin stood at the column's edge;
  • a spacer with a margin held only its height, and everything under it rose;
  • a section fixed to half the column ran its text the column's width;
  • a chart's data table stood against the blocks round it;
  • a table of contents' entry whose anchor has no bookmark was a number no edit updates, with nothing said on the entry itself.

DocxNodeFieldLedgerTest listed each of these as a gap.

What changed

  • sidesLost(node, marginWritten, left, right) names a block's side margins and padding that its paragraph or table leaves out. A side counts where it moves the block:
    • A picture, a barcode or a table is set from the left, where its right side moves nothing, so only its left side counts.
    • A page reference counts the side or sides its alignment sets it from.
    • A cell of a row the layout placed already starts past a block's left margin, so that margin is not named there.
  • insetSidesLost(node, left) names the sides a spacer or a chart's table loses its margin and padding on: above, below and, for a chart, on the left.
    • Below is not named for a block whose space below is measured from the page: the lowest block of a band, or of a layer another resumes after in its column.
    • DocxLayerColumns now hands back where those blocks are placed (Band.closing, Plan.closing), from the same measurement that sets the space. The measurement itself is unchanged.
  • Where each note lands:
    • an inline picture: written as an inline picture; …;
    • a barcode: added to the note it already had;
    • a picture drawn beside its text or over its badge (drawWhereThePagePutsIt): its padding. It is fitted to its placement, which holds the padding;
    • a table: its left padding. Its margin is its indent;
    • a page reference: a new written as a paragraph note, naming its sides and, where the export writes no bookmark for its anchor, that its number is written as text;
    • a spacer (writeSpacer): the sides above and below;
    • a chart: the sides above, below and on its left, added to the chart note;
    • an unpainted section or container that holds text, or a panel composed in a table cell (writeContainerChildren): a fixedWidth narrower than the room its margins leave it. The room is taken past a placed row column's left margin, with an auto column's point of slack, and only where the width is known. A panel the layout placed is a table that wide, so it loses nothing.
  • The ledger:
    • 11 node fields move from a gap to REPORTED. 23 node-field gaps remain, each named.
    • A table's padding stays a gap. DocxLayoutMetrics.ownRows matches a table's rows by the table's placement width, which holds its side padding. A table with side padding therefore matches none of its rows, and loses its grid, row heights, unbroken rows and the anchoring of drawings in them. That is fixed in a change of its own, since it changes what is written.
    • The fixed width of a section or container written as a layer stack's column stays a gap.
  • The recipe (mapping rows for images, tables, sections and containers, spacers; the chart fallback; page references), the capability matrix (image, barcode, page reference) and the CHANGELOG say what each note names.

Verification

  • ./mvnw -B -ntp install -pl :graph-compose-render-docx → BUILD SUCCESS: 1008 tests, 0 failures, 1 skipped (the property-gated fidelity probe).
  • DocxBlockGeometryReportTest is new, 18 tests.
    • A new note is checked whole; a phrase added to a note the block already had is checked by how the note ends.
    • Where the output can show it, the test also checks that the file does not carry the inset. The body's XML with the inset equals the XML without it for a picture, a barcode, a page reference, a spacer, a chart and a fixed-width section. For a table, the w:tblInd of a table with a margin and padding equals that of one with the margin alone, which is not empty.
    • Where a side is written, the test checks it is: the body of a band whose closing spacer has a margin below differs from the body without it.
    • The band and column cases:
      • a spacer closing a band names only its insets above;
      • a spacer between two of a layer's blocks names both sides;
      • a spacer closing a layer that another resumes after in its column names neither, the stack being written as columns.
    • A panel composed in a table cell names its fixed width.
    • These get no note:
      • a picture's insets above, below and on its right;
      • a picture in a row column the layout placed, past its left margin;
      • a right-aligned page reference's left side;
      • a fixed width a panel, the column or an alignment wrapper holds;
      • a fixed-width section in an auto row column.
  • The tests fail without the code they cover. Removed in turn, each of these made its tests fail: the closing-block check (three tests), the auto column's slack, the placed row column's left margin.
  • The DOCX bytes do not change. The 62 corpus documents exported deterministically are byte-identical to the export before the change: DocxFidelityCorpusTest -Dgraphcompose.docxFidelity=export, SHA-256 per file, 0 of 62 differ.
  • Documentation and qa DOCX tests:
    • -pl :graph-compose-core -Dtest='com.demcha.documentation.**' → 166 tests, 0 failures;
    • qa documentation guards plus DocxPageZoneTest, DocxTransparentWrapperTest, TimelineRailAcrossBackendsTest and RtlAcrossBackendsTest → 52 tests, 0 failures.
  • The full reactor gate was not run; no public API, POM or workflow file changed.

Lane: render-docx backend (report only, no change to what is written) plus tests and docs.

A picture's, a barcode's and a table's left margin or padding, a page
reference's sides, a spacer's and a chart's insets above and below, and a
fixedWidth narrower than the column of an unpainted box or of a panel
composed in a table cell were left out of the Word file in silence. Each
block's note now names them, and a page reference whose anchor has no
bookmark says its number is written as text. In a band, where a spacer's
or a chart's insets are written as the space below, no note is made.
Nothing written changes.
…measures

A band measures the space below its lowest block from the page, and a
column the space before a layer it resumes after the one above, each
taking that block's margin and padding below it; nothing else in them
writes a spacer's or a chart's insets. DocxLayerColumns now hands back
where those blocks are placed, and only their side below goes unnamed:
the sides above, below a block in the middle and a chart's left are
named in a band too. A page reference counts the sides its alignment
sets it from, and a fixed width is named only where the column's width
is known and the box holds text.
@DemchaAV
DemchaAV merged commit 4767106 into 2.5-dev Oct 6, 2026
13 checks passed
@DemchaAV
DemchaAV deleted the fix/docx-report-geometry-losses branch October 6, 2026 06:53
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