feat(ooxml.js): wire the new document-schema.js fields into docx, pptx, and xlsx - #1216
Merged
Conversation
Mearman
force-pushed
the
feat/ooxml-schema-field-wiring
branch
from
September 11, 2026 04:26
8be7ed8 to
f269f9a
Compare
…t table ContentSheetCell gains its uniform font on both directions of the xlsx pair. The reader resolves each cell's xf through <fonts> and diffs the entry its fontId names against the table's own entry 0, stating only genuine differences -- mirroring xls-codec's default-diffing policy for BIFF8's identical font-table-and-index mechanism, since xlsx gives a cell no way to say "no font", only an index, and entry 0 is what the format's default concretely means for a given file. The write side widens the cell-format interning tuple from (number format, decoration) to (number format, font, decoration): the writer's own single Calibri-11 font stays fixed at index 0 as the reserved scaffolding entry, one further <font> is interned per distinct cell font, and a ContentFont normalising back to the entry-0 fields references index 0 and mints nothing -- an explicit bold:false from a file whose default was bold is a restatement of this writer's own not-bold default, not a new font. A fonted xf carries fontId plus applyFont, the identical flag discipline applyFill and applyBorder already draw. What CT_Font states that ContentFont has no member for (vertAlign's superscript/subscript, outline, shadow, condense, extend, family, charset, scheme) is read past rather than half-modelled, and a colour carried only as theme/indexed/auto resolves to no colour statement, matching the fill and border colour policy.
…ways The workbook's defined names now ride the ContentDocument's own names field: readWorkbookNames reads every xl/workbook.xml <definedName> including the _xlnm built-ins, refersTo verbatim in Excel's own formula language, with localSheetId mapped onto scopeSheetIndex in the same sheets document order the reader resolves sheets in. The write side emits the names array verbatim and in its own order -- the file's own definedName order is the only order a same-format round trip can reproduce, and the array's refersTo (a multi-area print range, a quoted sheet name) is the higher-fidelity spelling of exactly the two _xlnm print names a structured printRange can restate. The print-settings derivation now fills in only the print names the array does not already carry, so a hand-built document stating a structured printRange still gets its reserved definedName while a read-back workbook writes each name exactly once, from the array. The namedRange half of the tree root's definitions table is retired: its content was exactly what names now carries across the flat/tree boundary natively, and a tree reader feeding both channels would double-write every general definedName on the way back out. The definitions option and table keep carrying Table/List objects, which genuinely have no flat spelling. A name's refersTo passes the same security gate a namedRange entry always did -- sheet-qualified internal A1 references only, refused by name -- since it is the identical live-formula surface.
w:vertAlign lands on ContentRun.verticalAlign and w:rtl on ContentRun.direction, both resolved through the same style cascade bold and italic already ride (docDefaults through the character-style chain to direct formatting), since both are ordinary rPr members a style can state and inherit. The one wrinkle is vertAlign val= "baseline": that is the explicit none-of-the-above a producer writes to turn an inherited position off, so it wins the cascade merge as a value of its own and then disappears -- ContentRun models baseline as the field's absence, and the writer materialises the resolved position as direct formatting, so the override survives as plain text. w:bidi (the genuine WordprocessingML paragraph-level spelling, right- to-left paragraph layout rather than run direction) lands on ContentParagraph.direction through the paragraph cascade the same way. Both writers spell an explicitly left-to-right value with the off form (w:rtl/w:bidi val="0"), mirroring how bold: false is written: an absent element is "inherit" to the read-side cascade, not "off", so a resolved ltr must be stated rather than omitted. Element placement follows CT_PPrBase (bidi between numPr and spacing) and CT_RPr (vertAlign and rtl after u).
The origin annotation channel names what a node's content IS, and the two graphic-frame kinds that establish it now state it: a chart's cached-model table (pptx) and embedded chart object (xlsx) carry origin: 'chart', and a SmartArt graphic frame's shape carries origin: 'diagram' -- distinguishing a native chart part's exact numbers and a diagram's own node text from the identical-looking content a pasted screenshot or freeform text box would produce, which no other field on those nodes says. Stated only where the reader genuinely knows it: an authored a:tbl, a plain text shape, an OLE fallback picture, and a sheet's own cells carry no origin at all, the channel's absent-means-ordinary-prose convention.
…ream Every image the docx reader lifts out of a paragraph's run stream now carries anchorRunIndex/anchorOffset: the run whose text the image originally followed, and the character position within that run after which it sat. The run walk records each w:drawing/w:object that is a direct child of an emitted run at exactly the position it occupied -- the same run-and-character indexing the mid-run page-break event already collects -- and the lifting pass resolves the boundary rule from it: an image mid-run anchors inside its own run at the preceding text's length, an image in a run of its own anchors to the previous run at that run's full length, and an image at the paragraph's very start anchors to (0, 0). A drawing nested inside a w:object (the AlternateContent preview spelling) anchors to the position the object itself sat at, the only position the reader honestly knows for it. An element the walk never emitted a run for (inside field code, or a shape the run walk cannot see) carries no anchor -- the schema's absent-when-unknown spelling rather than a guess. A paragraph that splits at a mid-run page break leaves its lifted images unanchored too: the split re-indexes both halves' run arrays, so a pre-split index would name one half or the other ambiguously, the identical no-clean-encoding rule the split applies to a construct spanning the break. The writer re-inlines a lifted image as the last run of its containing paragraph, so an image that already sat at the paragraph's end keeps its anchor through a round trip while one lifted from earlier in the text migrates to the end -- pinned by a test, and the honest statement of a writer that does not yet place runs by anchor.
…igin, and anchor coverage The xlsx bullet now describes the names field's both-ways ride (every definedName including the _xlnm built-ins, refersTo verbatim, the print-derivation fallback, and the internal-A1 security gate), the retirement of the namedRange half of the definitions table, and the per-cell font's default-diffed read and interned-table write. The docx bullet gains the vertAlign/rtl/bidi mappings, the lifted-image anchors and their end-of-paragraph-stable round trip, and the pptx and xlsx bullets name the chart/diagram origin classifications. Also corrects the same bullet's stale claim that a floating wp:anchor position is not recorded: floatPosition has been read since the anchor-position widening, and the parenthetical now says so.
Mearman
force-pushed
the
feat/ooxml-schema-field-wiring
branch
from
September 11, 2026 05:07
f269f9a to
89deff9
Compare
Mearman
marked this pull request as ready for review
September 11, 2026 05:07
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
Contributor
|
🎉 This PR is included in version 8.14.0 🎉 The release is available on: Your semantic-release bot 📦🚀 |
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.
Six schema fields landed on
mainwith no ooxml.js consumer; this wires each one, as separate atomic commits that each pass the package gate (pnpm --dir packages/ooxml.js lint && typecheck && test, plustest:workers).What lands
xlsx per-cell font (
ContentSheetCell.font, read+write). The reader resolves each cell's<cellXfs>xf through<fonts>and diffs the entry itsfontIdnames against the table's own entry 0, stating only genuine differences — mirroringxls-codec's default-diffing policy for BIFF8's identical font-table-and-index mechanism (its per-cell-fonts PR is the reference; verified against itscontentFontOf/xfFontFieldsOfboth-directions shape). The writer widens the cell-format interning tuple from (number format, decoration) to (number format, font, decoration): the established single Calibri-11 font stays fixed at index 0, one further<font>per distinct cell font,applyFonton a fonted xf, and aContentFontnormalising back to entry 0 references it and mints nothing. CT_Font members with noContentFontfield (vertAlign, outline, shadow, family, charset, scheme) are read past; theme/indexed/auto colours resolve to no colour statement, matching the fill/border policy.xlsx defined names (
names, read+write).readWorkbookNamesreads everyworkbook.xml<definedName>— the_xlnmbuilt-ins included — withrefersToverbatim andlocalSheetIdmapped ontoscopeSheetIndex. The writer emits the array verbatim and in its own order (so a same-format round trip reproduces the file's own definedName order, and a multi-area print range'srefersTosurvives verbatim), with the print-settings derivation filling in only the two reserved print names the array does not carry. ThenamedRangehalf of the tree root'sdefinitionstable is retired — its content is exactly whatnamesnow carries across the flat/tree boundary natively, and feeding both channels would double-write every general definedName; the definitions table/option keep Table/List objects. A name'srefersTopasses the same sheet-qualified-internal-A1 security gate anamedRangeentry always did (refused by name, never written into live formula context).docx
verticalAlign+ direction.w:vertAlignlands onContentRun.verticalAlignandw:rtlonContentRun.direction, resolved through the same style cascade bold rides;w:bidi— verified against ECMA-376 Part 1's own "Right to Left Paragraph Layout" definition as the genuine WordprocessingML paragraph-level spelling — lands onContentParagraph.directionthrough the paragraph cascade.vertAlign val="baseline"is the cascade's explicit override spelling: it wins the merge and then disappears, since the schema models baseline as the field's absence. Writers spell an explicitly left-to-left value with the off form, mirroringbold: false.pptx/xlsx origin. The chart cached-table reader sets
origin: 'chart'on itsContentTable(pptx) and on the xlsx chartContentEmbeddedObject; a SmartArt graphic frame's shape carriesorigin: 'diagram'. Checkedgit log/open PRs first: #1202's schema half is merged and no codec wiring is landing elsewhere, so this is the only origin wiring.docx image anchors. Every lifted image records
anchorRunIndex/anchorOffsetper the schema's boundary rule: the run whose text it followed and the character position within that run after which it sat. A paragraph that splits at a mid-run page break leaves its images unanchored (the split re-indexes both halves — no clean encoding, so dropped rather than mis-encoded). The writer re-inlines a lifted image as its containing paragraph's last run, so end-of-paragraph anchors are round-trip-stable and earlier ones migrate to the end — pinned by a test.README. The docx/pptx/xlsx gotcha bullets state all of the above; the same docx bullet's stale "floating wp:anchor position not recorded" claim is corrected to name
floatPosition, which the reader has recorded since that widening.Deliberately out of scope
ContentSection.watermarks— the schema field has not landed onmain(verified by grep atorigin/main); wpd's wiring is another PR's, and no ooxml page-furniture change was in this task's items.