Repository navigation
fix(docx): say what an export with no layout leaves to Word, and count an anchor's nodes off the document - #912
Merged
Conversation
… creating application, and count an anchor's nodes off the document Without the layout behind the export a drawing was dropped as having "no semantic Word analogue"; a drawing stands where the layout puts it, and the reason is now the layout it lacks. A drawing that paints nothing, with a layout or without, is said to paint nothing. The section's note named line heights, the space between lines and auto columns, and none of the rest only a layout places or keeps. It names, where the section holds them, a row its arrangement or a growing spacer divides, what an alignment or a stack's layer is set at across the width, written from the left, and the keeps Word is not told: a block kept together or with the next, a table's row in one piece, a row of columns. Where the session states metadata, its creator is written as the package's Application, which read "Apache POI"; a Word file has no producer, and one of the caller's own other than the application is named. A table POI makes has no w:tblGrid, and one whose columns were left to Word kept none, which a Word table may not be without. Every table now starts with an empty grid; Word and LibreOffice lay a table with an empty grid out as one without. An anchor two cells of one table declared was counted as one node, the layout registering both under the table: nodes are counted off the document now, and a node of the caller's own kind, which only the layout tells of, still counts. An anchor in a table's header row the page repeats goes, on the page, to the last repeat, and in Word to the one row the file holds, where the table starts; the report names it.
DemchaAV
marked this pull request as draft
October 16, 2026 23:14
… one The kinds a node's anchor is read from were a chain of nine type tests. They are a table of exact classes now, node records being final, and a test holds it to every node kind GraphCompose ships with an anchor but the page reference, which names one and declares none: a kind added with an anchor of its own fails the test rather than going uncounted. The changelog and the recipe say where a node of the caller's own kind is counted.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
An export with no layout behind it — a document the engine refused to lay out, whose export
falls back to what the document states, or a bare
export(graph, context)— told the callerless than it lost, and in part the wrong thing:
where the layout puts it, and with a layout the same shape is drawn; the reason was the missing
layout. An unpainted shape (
addCircle(60)) carried that reason with a layout too, though thepage draws nothing of it.
the rest only a layout places or keeps (measured on the merged tree): a centred alignment and a
stack's centred layer are written at the left, a
SPACE_BETWEENrow as columns Word sizes, andno keep is written —
keepNext0 andkeepLines0 against 2 and 3 with the layout, since eachwaits for the layout to show the page held the block on one page.
Three more things went unsaid or were missing, with a layout as well:
docProps/app.xmlstill read<Application>Apache POI</Application>; themetadata's creator and producer were not written.
w:tblGrid, and one whose columns were left to Word kept none: a rowwith no layout, top-level and nested, a composed row the layout does not place, a chart's data
table.
CT_Tblrequires the element.registers both markers under the table's path. The file kept one bookmark and the report said
nothing. An anchor in a table header row the page repeats is registered on every page the header
is drawn on; the page's link and page reference go to the last repeat (measured: a reference
reading "5" on a five-page table), Word's bookmark sits in the one row the file holds — on page
1, and the field reads 1 once Word updates it (measured with Word COM). That went unsaid too.
What changed
droppedBecause, now an instance method): a drawing that paintsnothing — no fill, no fill paint, no stroke wider than zero (
paintsNothing) — "paints nothinga shape could show"; with no layout, a drawing has "no layout behind the export, nothing says
where the page draws it". The gradient and unknown-kind reasons stay first. A horizontal rule is
still written as a rule without a layout.
DocxWithoutLayout.note, new): the old sentence stays as it was for asection that holds none of the rest, so existing exports read the same. Each clause is added
only where the section holds one: a row its arrangement or a growing spacer divides (an empty
row writes nothing and is not counted); an alignment other than
LEFT, or a stack's or a shapecontainer's layer set in by its alignment or its offset, "written from the left"; and "Word is
not told to keep what the page keeps from a page break" with the kinds the section has — a block
kept together or with the next, a table's row in one piece, a row of columns. A block or a row
composed in a table cell is kept by its table row, laid out or not, and is not counted; a
repeated header row is still kept with the row under it, which needs no layout. The keeps stay
unwritten, as before (
withoutALayoutNothingIsKnownToBeOnOnePage): only the layout knows ablock fits on a page, and a document it refused may hold a row taller than one, which no page
holds whole however Word is told to keep it.
applyMetadata): the creator is the extendedApplication(the producer where nocreator is set), as the PPTX backend writes it. A Word file has no producer field; a producer
the caller set, other than the application written, is named (
DROPPED,producer). Theproducer every
DocumentMetadatastarts with is not: a session setting only its creator wouldhave been told on every export.
withAGrid, at the two places tables are made): every table starts with an emptyw:tblGrid, which the column writers fill as before, betweenw:tblPrand the rows. Measured inWord and LibreOffice, word by word: a top-level and a nested
SPACE_BETWEENrow, and a table ofauto columns with and without a width, each with no grid and with an empty one, stand the same in
both editors (equal
gridCols moved LibreOffice's by 0.05pt); so do the committedWordExportCompanionfile and its re-render.DocxAnchorDeclarations, new): nodes are counted off the document — eachnode declaring an anchor, read off a table of the kinds that declare one (
DECLARING, exactclasses: node records are final), through the content a table's cells are built from — and the count is
the larger of that and the paths the layout registers the anchor under
(
DocxLayoutMetrics.anchorPaths), which a node of the caller's own kind declares through andthe document cannot tell.
DocxLayoutMetrics.anchorsDrawnAgaingives the anchors one pathregisters on more than one page. An anchor one node declares, in a repeated header row, drawn on
two pages or more and bookmarked, is named.
Verification
./mvnw -B -ntp test -f render-docx/pom.xml— BUILD SUCCESS, render-docx 1720 tests (+14):DocxWithoutLayoutTest(new, 8: the note unchanged for paragraphs; an arranged and a grown row;an alignment, a stack's centred layer, an offset one, a shape container's, and left ones not
named; each keep kind, all three together, an empty row; a row and a block composed in a cell
not named; each drawing's reason without a layout, a filled path and a paint fill included, a
rule not dropped; an unpainted circle, path and line and a zero-width stroke; an empty grid
top-level and nested, in schema order),
DocxSemanticBackendTest+2 (the creator asApplication, a producer named; the default, a producer standing alone and a creator set alonenot named),
DocxLinkTest+2 (two cells of one table, two paragraphs and a paragraph with adrawn circle; a repeated header — direct, inside a block, on one page, a drawn circle),
DocxReportedLossesTest+1 (an anchor a node of the caller's own kind declares),DocxAnchorDeclarationsTest(new:DECLARINGholds every node kind GraphCompose ships with ananchorcomponent but the page reference, read off the class scan the field ledger uses).them, a header drawn once, the bookmark condition, the repeat note, the document count, the
layout's paths, each drop reason, the zero-width stroke, each paint field, the old note, each
clause and its conditions (stacks, shape containers, offsets, left layers, cells, empty rows),
the
Application, the producer's fallback, its note both ways and the default producer, thegrid in the body and in a cell, a kind left out of
DECLARING(two) — all caught.docPropsincluded; report notes 1244 → 1244 (no corpusdocument is exported without a layout, sets metadata or declares an anchor twice).
WordExportCompanion's committed DOCX is re-rendered: itsApplicationnow reads GraphCompose,and its chart's data table has an empty grid. Converted with Word and LibreOffice, the old and
the new file put every word (186 and 183) at the same place.
./mvnw -B -ntp clean verify— BUILD SUCCESS (core 821, render-docx 1720, examples 93, qa 1820);the knowledge pack's
--check, stability-doc, claims and routes guards pass.Lane: render-docx (semantic backend) — report and package metadata; no layout change.