Skip to content

fix(docx): name what a table's cell is composed of by its cell in the report - #953

Merged
DemchaAV merged 2 commits into
2.5-devfrom
fix/docx-composed-cell-note-paths
Oct 24, 2026
Merged

DemchaAV merged 2 commits into
2.5-devfrom
fix/docx-composed-cell-note-paths

Conversation

@DemchaAV

@DemchaAV DemchaAV commented Oct 24, 2026 •

Copy link
Copy Markdown
Owner

Why

A DOCX export's report names each node it could not carry as authored by the node's path, the one the layout graph addresses it by. The layout lays a composed cell's content (DocumentTableCell.node(...)) out under its table and gives it no path of its own, so every note about a node there had path == null.

That was 216 of the corpus's 1384 notes: a rounded panel's corners in CobaltRota's schedule, inline icons and rating dots drawn as pictures in CV sidebars, a nested chip's right border and left edge. Two cells composed alike could not be told apart, nor which cell a note was about.

What changed

  • DocxLayoutMetrics names what each table cell holds, after the flow is named, under the table's path by its cell, then by its own segment (indexCells):
    • row[r]/cell[c], where r is the row's index among the table's rows and c the cell's among the cells authored for that row, from 0 — not its column in the grid, which a cell spanning columns before it or rows into its row from above moves on;
    • nested tables in cells are named the same way, under their cell;
    • e.g. CobaltRota[0]/Schedule[0]/row[7]/cell[7]/ShiftBlock[0]/row[1]/cell[0]/Chip[0].
  • Placement lookups are unaffected. No layout path lies under a table's path: a table node has no children, and composed fragments carry the table's path. So a composed node still finds no placement, fragments or colour of its own. pathsWithin, which the page-zone lookups use, stays the layout's paths alone.
  • Repeated nodes. A node the flow names keeps that path, which the layout knows, and what it holds is named under it — a table in the flow and in another table's cell included. A node in several cells takes the last, as one placed twice in the flow does.
  • Report.
    • A section composed in a cell whose anchor has no bookmark now adds that loss to its own note, as one in the flow does (DocxExportReport.Builder.addTo, which tells nodes by path). With its outline entry not written either, it gave two notes, and gives one.
    • DocxExportReport's contract now also names the paths a note may carry that are not a node's — page N, and in a document of several sections section N or section N, page N — and that a note about the whole document or about a node of a page zone has none.
  • matchComposedText skips a table with no fragments of its own: a table composed in a cell, whose text the table holding it lays out.
  • Docs:
    • the path contract in DocxExportReport's Javadoc and in the recipe;
    • a CHANGELOG entry;
    • comments that said composed content has no path, reworded.

Verification

  • Corpus:
    • The 62 DOCX are byte for byte the same, and so is the text of their 1384 notes.
    • Notes without a path went from 233 to 17. The 17 are about the whole document: the paragraph closing it (15) and a text footer band reaching past the margin (2).
  • DocxComposedCellPathTest (8 new tests):
    • through the export, a note on a node in a cell, and on one in a table in a cell, names it by its cells;
    • two cells composed alike are told apart;
    • a composed node still finds no placement or fragments of its own;
    • a node in the flow and in a cell keeps the flow's path and placement, before the table and after it;
    • a table in the flow and in another table's cell names what it holds under its flow path;
    • cells behind a column span and under a row span are named by their place among the row's authored cells;
    • a node in two cells, and a table placed twice in the flow, are named by the last.
  • Updated tests:
    • DocxCellRuleRoomTest and DocxTableColumnsLeftToTheEditorTest asserted a null path for composed content; they now assert the cell path.
    • DocxPanelLayerAlignTest asserts a composed layer's path.
    • DocxCarriedWithoutReportTest tells the composed capsules apart by their paths.
    • DocxAnchorWithoutABookmarkTest gains a composed section with one note.
  • Fail-closed: 7 sabotages, each run alone, all caught:
    • no cell walked;
    • a node the flow names taking the cell's path;
    • the cells' paths not kept;
    • the wrong cell index;
    • no cell segment;
    • the first place kept instead of the last;
    • the text match skipping tables that have fragments.
  • Gate:
    • ./mvnw -B -ntp clean verify -pl <core, render-pdf, render-docx, render-pptx, templates, testing, qa, coverage> -am → BUILD SUCCESS: core 823, render-pdf 339, render-docx 2137, testing 5, render-pptx 142, templates 127, qa 1830.
    • The JDK 17 knowledge checks pass, with the API surfaces unchanged, and core javadoc has 0 warnings.
    • ./mvnw -f examples/pom.xml test → BUILD SUCCESS, 93 tests.

Lane: shared-engine (render-docx). DocxExportReport is @Beta; its record is unchanged, and what Note.path() holds is documented.

… report

- The layout lays a composed cell's content out under its table and
  gives it no path of its own, so every note about a node there carried
  none: 216 of the corpus's 1384 notes. Two cells composed alike could
  not be told apart, nor which cell a note was about.
- DocxLayoutMetrics names such a node under its table's path by its
  cell, row[r]/cell[c] (the cell's index among those authored for the
  row), then by its own segment, nested tables included. No layout path
  reads so, so the node still finds no placement of its own. The cell
  paths are merged in only where the flow names nothing: a node in the
  flow too keeps the flow's path, and one at several places the last.
- Told by its path, a composed node's losses go into one note, as a
  flow node's do.
- The file is unchanged: the 62 corpus DOCX are byte for byte the same
  and so is the text of their notes. The 17 notes left without a path
  are about the whole document.
@DemchaAV
DemchaAV marked this pull request as draft October 24, 2026 08:29
- What a table's cells hold is named once the flow is named
  (indexCells), under the table's own path: a table in the flow and in
  another table's cell named what it held under that cell, the table
  itself under the flow. pathsWithin, which the page-zone lookups use,
  is the layout's paths alone again.
- The text match skips a table with no fragments of its own: one
  composed in a cell, whose text the table holding it lays out.
- The report's contract names the paths that are not a node's: page N,
  section N and section N, page N; and that a note about the whole
  document or a page zone's node has none. The cell index is the
  cell's among those authored for the row, which a row span from above
  moves on too.
- CHANGELOG: only an anchor's loss joins a composed node's note.
- Tests: through the export, a table in a cell; a table in the flow and
  in a cell; a row span; the composed capsules told apart by path.
@DemchaAV
DemchaAV marked this pull request as ready for review October 24, 2026 08:46
@DemchaAV
DemchaAV merged commit 05df8a8 into 2.5-dev Oct 24, 2026
13 checks passed
@DemchaAV
DemchaAV deleted the fix/docx-composed-cell-note-paths branch October 24, 2026 09:22
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