feat(page-number): per-field PAGE value-format switches & case-insensitive field dispatch (SD-3006)#3599
feat(page-number): per-field PAGE value-format switches & case-insensitive field dispatch (SD-3006)#3599luccas-harbour wants to merge 11 commits into
Conversation
OOXML field type names are case-insensitive, but the field-reference preprocessors dispatched on the raw first token (e.g. only "PAGE", not "page"). A lowercase PAGE/NUMPAGES field in a repeated footer fell through to the cached static text and showed the same number on every page. Add a shared extractFieldKeyword helper that normalizes the dispatch token to upper case while leaving the original instruction text intact for downstream processors, and route fldSimple/fldChar dispatch and the header/footer page-field scan through it. Make the HYPERLINK target regex case-insensitive and anchored. Cover the new behavior with unit tests and a behavior spec asserting a lowercase PAGE footer resolves per page.
Parse the `\*` value-format switches on PAGE field instructions (Arabic, Roman/roman, ALPHABETIC/alphabetic, ArabicDash) into a run-local pageNumberFormat override, and apply it independently of section numbering when resolving page-number tokens. - add parsePageInstruction / pageNumberFormatToInstructionSwitch in a new page-instruction.js; page-preprocessor stores the original instruction and parsed format on sd:autoPageNumber - round-trip instruction + pageNumberFormat through the autoPageNumber translator and the page-number extension node (preserve imported instruction text, synthesize a switch for new formatted nodes) - add pageNumberFormat to TextRun and thread it through layout-bridge, layout-resolved, painters (resolveRunText), and stamp section-aware displayNumber on pages so formatting uses the pre-format numeric value - move formatPageNumber + PageNumberFormat into @superdoc/contracts as the single source of truth; re-export from pageNumbering - include pageNumberFormat in block-version, merge, and hash signatures so format changes invalidate cached layouts upperLetter/lowerLetter now render as repeated letters (AA, BB, CC) to match Word instead of the previous Excel-style sequence (AA, AB).
|
I tried to verify against the ECMA-376 MCP server, but the spec tool calls were blocked (permission not granted in this environment) after several attempts. I completed the review against ECMA-376 §17.16.4.1 (General-Formatting-Switch) from reference knowledge instead — flagging that so you can re-run the MCP check if you want the live citation. Status: PASS The two changed handler files ( What I checked on the OOXML-facing side (the
One thing worth noting (not a failure):
No non-existent elements/attributes, no missing required attributes, and no incorrect defaults in the changed handler. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 63d72a5e31
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
There was a problem hiding this comment.
cubic analysis
3 issues found across 43 files
Linked issue analysis
Linked issue: SD-3006: Feature: Page Number Fields
| Status | Acceptance criteria | Notes |
|---|---|---|
| ✅ | Visible PAGE output matches Word in representative corpus docs | The PR centralizes page formatting (formatPageNumber), threads run-local pageNumberFormat through the layout + painter, stamps section-aware displayNumber, and includes unit + behavior tests validating formatted output in headers/footers and token resolution. |
| Header/footer page numbers render in the correct place and format | The PR fixes per-page formatting and per-field format overrides (format applied at render/measurement time) and adds tests that validate the formatted content in footers. However, there is no clear change or test specifically asserting layout placement (geometry) adjustments: the work targets correct content/format and caching invalidation rather than explicit placement adjustments, so placement aspect remains unverified by the diff. | |
| ✅ | Section-aware page number behavior remains stable after pagination settles | The PR plumbs a section-aware numeric value (displayNumber) through pageResolver → header/footer resolution → painter; includes changes to incrementalLayout/pageResolver, layoutHeaderFooter, resolved-layout, and tests that exercise per-page resolution and caching invalidation ensuring section-aware formatting is applied to the correct numeric value. |
Reply with feedback, questions, or to request a fix.
Fix all with cubic | Re-trigger cubic
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
Only dispatch the SEQ pre-processor for uppercase SEQ instructions so lowercase `seq` fields keep their cached visible result runs instead of being re-resolved. Also recurse into run-wrapped content when extracting resolved text so cached numbers nested inside runs are captured.
There was a problem hiding this comment.
2 issues found across 11 files (changes from recent commits).
Reply with feedback, questions, or to request a fix.
Fix all with cubic | Re-trigger cubic
According to ECMA-376, the Placement comes from normal WordprocessingML layout:
So for SuperDoc: render |
Summary
Two related fidelity fixes for
PAGEfields in headers/footers, plus a refactor that makesformatPageNumberthe single source of truth.Case-insensitive field dispatch. OOXML field type names are case-insensitive, but the field-reference preprocessors dispatched on the raw first token (
"PAGE"only, not"page"). A lowercasePAGE/NUMPAGESfield in a repeated footer fell through to cached static text and showed the same number on every page.PAGEvalue-format switches. The\*value-format switches on aPAGEinstruction (Arabic,Roman/roman,ALPHABETIC/alphabetic,ArabicDash) are now parsed into a run-localpageNumberFormatoverride and applied independently of section numbering. Previously a{ PAGE \* roman }footer ignored its own switch and rendered using the section's format.Changes
Field dispatch (super-converter)
extractFieldKeywordhelper normalizes the dispatch token to upper case while leaving the original instruction text intact for downstream processors.fldSimple/fldChardispatch and the header/footer page-field scan (preProcessPageFieldsOnly) through it.HYPERLINKtarget regex case-insensitive and anchored.Value-format switches
page-instruction.js:parsePageInstruction(parse\*switches →pageNumberFormat) andpageNumberFormatToInstructionSwitch(inverse, for export).page-preprocessorstores the originalinstructionand parsedpageNumberFormaton thesd:autoPageNumbernode.instruction+pageNumberFormatthrough theautoPageNumbertranslator (preserving imported instruction text, synthesizing aPAGE \* <switch>for new formatted nodes) and thepage-numberextension node.Layout pipeline
pageNumberFormatto theTextRuncontract and threaded it through the v1 layout-adapter (text-run/generic-tokenconverters), layout-bridge, layout-resolved, and the DOM painter'sresolveRunText.displayNumber(pre-format numeric value) onPage/HeaderFooterPage/ resolved pages, plumbed viapageResolver→resolveHeaderFooterTokens→ render context, so the format applies to the correct numeric value.pageNumberFormatin cache keys:block-version,versionSignature, run merge-hash, and header/footer content hash — format changes now invalidate cached layouts.Refactor
formatPageNumber+PageNumberFormatinto@superdoc/contractsas the single source of truth;pageNumberingre-exports them.Behavior changes
upperLetter/lowerLetternow render as repeated letters (AA,BB,CC) to match Word, instead of the previous Excel-style sequence (AA,AB).ArabicDashrenders as- N -(with spacing) and unknown formats fall back to decimal.Tests
field-keyword,page-instruction/page-preprocessor,preProcessNodesForFldChar,preProcessPageFieldsOnly,autoPageNumber-translator,resolvePageTokens,resolveHeaderFooterTokens,cacheInvalidation, paintertext-run, and the layout-adapter token converters.PAGEfooter resolves per page (footer-page-keyword-case.spec.ts) and formatted footer page fields, with new sharedstory-fixtureshelpers.