Repository navigation
fix(docx): read a table's cells, headers and list runs; check page references - #906
Merged
Merged
Conversation
…ferences The faces a document ships, the heading styles it defines and the anchors a page reference was written as a field for were read from the document's tree before the body, walking a node's children. A table holds a cell built from a node in the cell: a face set only there was named on its runs and not shipped, a heading there named a HeadingN style the styles part did not define (Word set it as Normal, at no outline level), and a page reference to an anchor there was written as a number with a note saying the bookmark was missing, while the cell wrote it. DocxWrittenTree.under gives a node's children and a table's composed cells' content, and the font and outline passes walk it. The font table also reads the page zones and the header and footer bands of every section, a page reference's and a page number's style, and the runs of a list's items and markers. A page reference no longer asks the tree whether its anchor gets a bookmark: it is written as a PAGEREF field, and once the body is written each field whose bookmark the file does not hold for its anchor (a section holding only shapes the export draws writes no paragraph to put one round) is taken back to the number it reads, its note saying so, where Word would show "Error! Bookmark not defined." on its first update.
This was referenced Oct 15, 2026
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
Before writing the body, the DOCX export read the document's tree three times:
HeadingNstyle;PAGEREFfield.All three walked
node.children(). A table keeps a cell built from a node (DocumentTableCell.node(...)) in its rows, not in its children, so every such cell was invisible to these passes.Faces. A face set only in a composed cell was named on its runs and not shipped, so a reader without it installed saw a substitute. In the corpus:
rota-cobaltships no Spectral at all;cv-slate_orangelacks Fira Sans Condensed Bold;cv-violet_gridlacks Lato Bold.The font table also skipped page zones and header/footer bands of text (their parts are not written from the tree), a page reference's and a page number's style, and the runs of a list's items and markers.
Headings. A heading in a composed cell named a
HeadingNstyle thatstyles.xmldid not define. Measured in Word, the paragraph reads asNormalat outline level 10 (body text), outside the Navigation Pane.Page references. A page reference to an anchor in a composed cell was written as a number that an edit does not update. Its note said the anchor had no bookmark, although the cell writes one. A tree walk cannot say which anchors get a bookmark anyway. A section holding only shapes the export draws writes no paragraph to put one round, yet the walk counted its anchor, so its field would read "Error! Bookmark not defined." the first time Word updated it. A stack holding only drawing inside a composed cell was read as an overlay; the writer writes it as a stack of one layer, rule and bookmark included.
What changed
DocxWrittenTree.under(node)returns a node's children plus, for a table, each composed cell's content, row by row.collectOutlineLevelsandDocxFontTable.collectFontsnow walk it.weighTextStyles, which picksNormal, already read composed cells and is unchanged.Font table.
DocxFontTable.writetakes every section's page-zone content and band styles. The zone and band writers build these through the same helpers (zoneContentOf,bandStyleOf), so the font table reads exactly what is written.collectFontsalso reads:PageReferenceNodeandPageFieldNodestyles;A list's runs are read after its own style, so a document with nothing new to ship keeps its font-table order.
Page references.
bookmarkedAnchorsInis gone. Every page reference is written as aPAGEREFfield and recorded. Once every section is written,unfieldTheReferencesWithoutABookmarkchecks each field against the bookmarks the body actually wrote (openAnchor,bookmarkAround), by name and by the anchor each was written for, since two anchors can be minted one name. A field whose bookmark is missing loses its field marks and code but keeps its text and style. Its note goes where the reference's note stands, or would stand, and says the reference is text.DocxExportReport.Buildergainssetandinsertto do this.Docs. The recipe now says where shipped faces come from and when a reference is checked. The CHANGELOG entry is under 2.5.0 Public API.
Verification
DocxFontTableTest: faces from a composed cell and from a nested table's cell; from a footer paragraph, a header page number and a footer band; from a page reference, a plain list's marker runs, and a nested list's item runs and depth-1 marker.DocxHeadingRoleTest: a heading in a nested composed cell has itsHeading2style defined.DocxPageReferenceTest:PAGEREFfields with no "no bookmark" note;1, with no field marks;a.anda_2both becomea_2).2.5-dev:./mvnw -B -ntp clean verify→ BUILD SUCCESS (render-docx 1625, qa 1820).Lane: shared-engine (render-docx semantic DOCX backend), no public API change.