Skip to content

🔄 Sync stable → main#3670

Merged
caio-pizzol merged 7 commits into
mainfrom
sync/stable-to-main-20260606-000549
Jun 6, 2026
Merged

🔄 Sync stable → main#3670
caio-pizzol merged 7 commits into
mainfrom
sync/stable-to-main-20260606-000549

Conversation

@superdoc-bot
Copy link
Copy Markdown
Contributor

@superdoc-bot superdoc-bot Bot commented Jun 6, 2026

Summary

Merges latest stable patches into main with a real merge commit so stable release tags remain reachable from main.

This keeps @next prerelease version calculation ahead of the latest published stable release.

Merge requirement

This PR must be merged with GitHub's merge-commit option. Squash or rebase merging will discard stable ancestry and break @next version calculation again.

Conflict handling

Merge status: clean.

Version-only release artifact conflicts are auto-resolved to keep stable's already-published versions. Non-version conflicts fail this workflow instead of committing conflict markers.


Auto-created by sync-patches workflow.

semantic-release-bot and others added 7 commits June 5, 2026 23:31
### What's New

- **Structural tracked changes for tables** — Whole-table insert/delete tracked changes import, render, and export like Word; single "Added/Deleted table" bubble coalesces cell content
- **PAGEREF field resolution** — Cross-references display live target page numbers with numeric formatting (roman numerals, alphabetic, zero-padded)
- **SEQ sequence fields** — Sequential numbering with format switches, heading-level resets, section-aware counters, and custom field arguments
- **Embedded DOCX fonts as registry faces** — Register embedded fonts as document-owned to prevent cross-document bleed; each document renders its own subset bytes
- **Per-document font mapping** — Map logical family names to physical fonts via `superdoc.fonts.map()`; register custom faces via `fonts.add()`
- **Explicit column widths with per-column gaps** — Multi-column sections now render at authored widths with independent inter-column spacing instead of uniform gaps
- **Footnote pagination parity with Word** — Ordered-cluster rule ensures first N-1 anchored footnotes render fully; demand-aware body packing eliminates blank space above separator
- **Document background import and rendering** — Background fill properties from .docx now render in both editor and presentation modes
- **Nested content controls** — SDTs can nest within other SDTs; import and export preserve structure
- **Custom content control styling** — Hidden chrome (`chrome:'none'`) reads CSS variables (`--sd-content-controls-custom-*`) for appearance without `!important` overrides

### Improvements

- Column geometry resolves once at layout time, not re-derived at each consumer site
- Body pagination accounts for actual footnote demand per line, not block-level reserves
- Table cell borders resolve per ECMA-376 conflict rules — shared edges render once, not doubled
- Table cell shading resolves style-based percentage fills and auto-color matching Word
- Footnote numbering respects document and section-level configurations with per-section format overrides
- Copy-pasted text in suggested mode no longer reverts on next keystroke
- Find navigation stops jumping when focus returns to the search input
- Content control navigation skips hidden markers and atoms
- Block SDT deletion respects all lock modes throughout the document
- Anchored tables reposition correctly with wrap and margin settings
- Inline images top-align with text baseline
- Tracked changes bubbles suppress redundant inline changes inside structural changes

### Fixes

- Tracked structure changes project correctly to legacy public API surface without data loss
- Footnote demand deduped by id so repeated references on one page reserve correct space
- Footnote marker inherits family/size/color from surrounding body text, not bold/italic formatting
- Hit-testing for column assignment uses content-box coordinates, not full-page width
- Equal-width explicit columns with non-uniform gaps now balance and render correctly
- Rowspan continuation rows no longer show doubled interior borders in collapsed mode
- Nested table SDTs preserve chrome when splitting across page boundaries
- Image and SVG data URIs validate before rendering; SVG no longer requires base64 encoding
- Backspace at inline SDT boundaries no longer over-selects chrome
- Vertical merge cells inside SDTs mark correctly during layout
- Tracked deletion in suggesting mode prevents char-by-char duplication on adjacent deletes
- Multi-section documents with varying margins resolve column hit-testing to the correct section
- Anchored graphics use per-column origin but scalar width for measurement consistency with object placement
- Field underlines render flush with text underlines; tabs no longer show seams
- Per-section titlePg flag respected when inferring first-page header/footer variants
### What's New

- **Structural tracked changes for tables** — Whole-table insert/delete tracked changes import, render, and export like Word; single "Added/Deleted table" bubble coalesces cell content
- **PAGEREF field resolution** — Cross-references display live target page numbers with numeric formatting (roman numerals, alphabetic, zero-padded)
- **SEQ sequence fields** — Sequential numbering with format switches, heading-level resets, section-aware counters, and custom field arguments
- **Embedded DOCX fonts as registry faces** — Register embedded fonts as document-owned to prevent cross-document bleed; each document renders its own subset bytes
- **Per-document font mapping** — Map logical family names to physical fonts via `superdoc.fonts.map()`; register custom faces via `fonts.add()`
- **Explicit column widths with per-column gaps** — Multi-column sections now render at authored widths with independent inter-column spacing instead of uniform gaps
- **Footnote pagination parity with Word** — Ordered-cluster rule ensures first N-1 anchored footnotes render fully; demand-aware body packing eliminates blank space above separator
- **Document background import and rendering** — Background fill properties from .docx now render in both editor and presentation modes
- **Nested content controls** — SDTs can nest within other SDTs; import and export preserve structure
- **Custom content control styling** — Hidden chrome (`chrome:'none'`) reads CSS variables (`--sd-content-controls-custom-*`) for appearance without `!important` overrides

### Improvements

- Column geometry resolves once at layout time, not re-derived at each consumer site
- Body pagination accounts for actual footnote demand per line, not block-level reserves
- Table cell borders resolve per ECMA-376 conflict rules — shared edges render once, not doubled
- Table cell shading resolves style-based percentage fills and auto-color matching Word
- Footnote numbering respects document and section-level configurations with per-section format overrides
- Copy-pasted text in suggested mode no longer reverts on next keystroke
- Find navigation stops jumping when focus returns to the search input
- Content control navigation skips hidden markers and atoms
- Block SDT deletion respects all lock modes throughout the document
- Anchored tables reposition correctly with wrap and margin settings
- Inline images top-align with text baseline
- Tracked changes bubbles suppress redundant inline changes inside structural changes

### Fixes

- Tracked structure changes project correctly to legacy public API surface without data loss
- Footnote demand deduped by id so repeated references on one page reserve correct space
- Footnote marker inherits family/size/color from surrounding body text, not bold/italic formatting
- Hit-testing for column assignment uses content-box coordinates, not full-page width
- Equal-width explicit columns with non-uniform gaps now balance and render correctly
- Rowspan continuation rows no longer show doubled interior borders in collapsed mode
- Nested table SDTs preserve chrome when splitting across page boundaries
- Image and SVG data URIs validate before rendering; SVG no longer requires base64 encoding
- Backspace at inline SDT boundaries no longer over-selects chrome
- Vertical merge cells inside SDTs mark correctly during layout
- Tracked deletion in suggesting mode prevents char-by-char duplication on adjacent deletes
- Multi-section documents with varying margins resolve column hit-testing to the correct section
- Anchored graphics use per-column origin but scalar width for measurement consistency with object placement
- Field underlines render flush with text underlines; tabs no longer show seams
- Per-section titlePg flag respected when inferring first-page header/footer variants
### What's New

- **Structural tracked changes for tables** — Whole-table insert/delete tracked changes import, render, and export like Word; single "Added/Deleted table" bubble coalesces cell content.
- **PAGEREF field resolution** — Cross-references display live target page numbers with numeric formatting (roman numerals, alphabetic, zero-padded).
- **SEQ sequence fields** — Sequential numbering with format switches, heading-level resets, section-aware counters, and custom field arguments.
- **Embedded DOCX fonts as registry faces** — Register embedded fonts as document-owned to prevent cross-document bleed; each document renders its own subset bytes.
- **Per-document font mapping** — Map logical family names to physical fonts via `superdoc.fonts.map()`; register custom faces via `fonts.add()`.
- **Explicit column widths with per-column gaps** — Multi-column sections render at authored widths with independent inter-column spacing instead of uniform gaps.
- **Footnote pagination parity with Word** — Ordered-cluster rule ensures first N-1 anchored footnotes render fully; demand-aware body packing eliminates blank space above separator.
- **Document background import and rendering** — Background fill properties from .docx now render in both editor and presentation modes.
- **Nested content controls** — SDTs can nest within other SDTs; import and export preserve structure.
- **Custom content control styling** — Hidden chrome (`chrome:'none'`) reads CSS variables (`--sd-content-controls-custom-*`) for appearance without `!important` overrides.

### Improvements

- Column geometry resolves once at layout time, not re-derived at each consumer site.
- Body pagination accounts for actual footnote demand per line, not block-level reserves.
- Table cell borders resolve per ECMA-376 conflict rules — shared edges render once, not doubled.
- Table cell shading resolves style-based percentage fills and auto-color matching Word.
- Footnote numbering respects document and section-level configurations with per-section format overrides.
- Copy-pasted text in suggested mode no longer reverts on next keystroke.
- Find navigation stops jumping when focus returns to the search input.
- Content control navigation skips hidden markers and atoms.
- Block SDT deletion respects all lock modes throughout the document.
- Anchored tables reposition correctly with wrap and margin settings.
- Inline images top-align with text baseline.
- Tracked changes bubbles suppress redundant inline changes inside structural changes.

### Fixes

- Tracked structure changes project correctly to legacy public API surface without data loss.
- Footnote demand deduped by id so repeated references on one page reserve correct space.
- Footnote marker inherits family/size/color from surrounding body text, not bold/italic formatting.
- Hit-testing for column assignment uses content-box coordinates, not full-page width.
- Equal-width explicit columns with non-uniform gaps now balance and render correctly.
- Rowspan continuation rows no longer show doubled interior borders in collapsed mode.
- Nested table SDTs preserve chrome when splitting across page boundaries.
- Image and SVG data URIs validate before rendering; SVG no longer requires base64 encoding.
- Backspace at inline SDT boundaries no longer over-selects chrome.
- Vertical merge cells inside SDTs mark correctly during layout.
- Tracked deletion in suggesting mode prevents char-by-char duplication on adjacent deletes.
- Multi-section documents with varying margins resolve column hit-testing to the correct section.
- Anchored graphics use per-column origin but scalar width for measurement consistency with object placement.
- Field underlines render flush with text underlines; tabs no longer show seams.
- Per-section titlePg flag respected when inferring first-page header/footer variants.
### What's New

**Fonts API for custom registration and per-document mapping**
- `superdoc.fonts.add({ family, weight, style }, url)` registers custom fonts and awaits them before layout
- `superdoc.fonts.map({ 'Calibri': 'Carlito', ... })` maps logical families per editor — custom substitutes override bundled defaults
- `superdoc.fonts.preload(['Georgia', ...])` loads families before measurement
- `superdoc.fonts.getReport()`, `getMissingFonts()`, `getDocumentFonts()` surface font diagnostics
- Font changes emit `fonts-changed` events with load status and resolution reasons

**Bundled font substitutes for common Word families**
- Calibri, Cambria, Arial, Times New Roman, Courier New load metric-compatible open substitutes (Carlito, Liberation Sans, Liberation Serif, Liberation Mono) — no configuration required
- Helvetica aliases to bundled Liberation Sans
- All substitutes load before measurement — rendering is deterministic, no late-load reflow

**Structural tracked changes for tables**
- Import whole-table insert/delete as single tracked-change item (not per-row)
- Render visually — inserted tables green, deleted tables struck-through with red rows
- Cell text inside tracked table subsumed into structural change — one review bubble, one decision
- Accept cascades: accepting inserted table accepts contained cell text; rejecting removes whole table
- Right-rail bubble labels "Added table" / "Deleted table"

**PAGEREF field resolution to live target page numbers**
- Resolve PAGEREF instructions to display number, format (numeric picture, general format), and relative position (`\p` → "on page N", "above", "below")
- Preserve instruction tokens and CHARFORMAT properties for export round-trip
- Render in document body with live re-resolution on pagination change

**SEQ sequence field support**
- Import and export SEQ field instructions with numbering, restart, and hidden-field state
- Resolve in layout using shared evaluator: counter tracking, heading-level serials, explicit restart precedence
- Field discovery reads resolvedNumber; export emits current evaluated results
- Preserve raw instructions for lossless round-trip

**NUMPAGES field formatting with numeric pictures and ordinal**
- Parse `\# "0"` (numeric picture) and `\* Ordinal` format switches on insert and import
- Format total page count as "1", "I", "1st" per field switch
- Applied to both inserted and refreshed fields
- Preserve instructions on import for export parity

**PAGE field formatting with numeric pictures**
- Parse `\# "0"` (numeric pictures) and `\*` general-format switches (Arabic, Roman, Alphabetic, ArabicDash)
- Format page numbers per field — "1", "i", "A", "1-" — independent of section numbering format
- Preserve instructions on import; export round-trips back to same field code

**SECTIONPAGES field support**
- Resolve SECTIONPAGES field to section-aware page count with section-level numbering awareness

**Document background**
- Import and render document-level background image positioned behind all body content

**Nested content controls**
- Import nested block SDTs (SDT inside paragraph) and nested inline SDTs (SDT inside run)
- Preserve structure through export

**Templates can be applied to documents**
- `doc.template.apply(templateDoc)` applies formatting, styles, and structure from template to active document

**Author colors for tracked changes**
- Per-author tracked change highlights in track-changes UI

### Improvements

**Multi-column sections now render with explicit per-column widths and gaps**
- Authored column widths preserved — no scaling to fit content area
- Per-column gaps (w:col/@w:space) drive separator positions, not uniform section gap
- Footnote assignment and positioning use per-column geometry
- Column hit-testing honors per-column boundaries, not uniform stride
- Separators position at resolved geometry

**Footnote pagination matches Word's behavior**
- Ordered-cluster rule — first N-1 anchors on a page render fully, only Nth may split across pages
- Body paginator reserves minStart (first-line height) for each new anchor, allowing tight packing
- Continuation pages reserve only remaining space after cluster obligation
- One-line widows absorbed by bumping reserve when eliminates a split
- Footnote numbering honors per-section w:footnotePr/@numFmt, @numStart, and eachSect restart
- Custom-mark footnotes (customMarkFollows) no longer consume an ordinal
- Marker is plain superscript with space gap before body text

**Tracked change rendering**
- Inserted table rows paint green, deleted rows paint struck-through red
- Right-rail bubble for structural table changes
- Inline changes inside tracked table suppressed (structural bubble only)

**Table border and shading fidelity**
- Collapsed shared edges resolve per ECMA-376 §17.4.66 — greater weight wins, style precedence breaks ties
- Explicit nil borders suppress dividers (both adjacent cells nil = no line)
- Cell shading — pct/auto patterns resolve to Word-matching blend colors
- Row-level tblPrEx borders merged and rendered once per edge
- rowspan continuation rows no longer double interior lines
- Keep-next paragraphs before tables reserve only first row height (table splits normally)

**Header/footer refs inherit across multiple sections**
- Walk back to nearest prior section defining first/even/default variant (not just immediately prior)
- first and even no longer fall back to default per OOXML §17.10.1
- Preserve section fallback refs in section resolver

**Field-annotation pills resolve fonts per document**
- Pills measure and paint through document's font resolver
- Custom substitutes and bundled clones apply to field content
- Underline rendering matches adjacent text

**Anchored tables reposition with per-wrap padding**
- Apply correct offset based on wrap value (square, tight, through)

### Fixes

- Image resize in suggesting mode no longer duplicates (uses AttrStep, preserves track-changes state)
- Tracked deletion coalescing in suggesting mode — adjacent Backspace batches into single change
- Multi-column sections with explicit equal widths now balance correctly (only skip genuinely-unequal widths)
- Per-column w:space honored for unequal columns per ECMA-376 §17.6.4
- Hit-testing resolves mid-page column region — column boundaries correct when section breaks change columns mid-page
- Explicit column layout idempotence — per-column gaps preserved through resolveColumnLayout
- Table hit-testing uses region column count, not page-start count
### What's New

**Fonts API for custom registration and per-document mapping** — Register custom fonts via `superdoc.fonts.add()`, map logical families per document with `fonts.map()`, preload families with `fonts.preload()`, and surface diagnostics via `fonts.getReport()`, `fonts.getMissingFonts()`, and `fonts.getDocumentFonts()`.

**Bundled font substitutes for common Word families** — Calibri, Cambria, Arial, Times New Roman, and Courier New now load metric-compatible open substitutes (Carlito, Liberation Sans, Liberation Serif, Liberation Mono) before measurement, with no configuration required. Helvetica aliases to bundled Liberation Sans. All substitutes load before render — no late-load reflow.

**Structural tracked changes for tables** — Import and render whole-table insert/delete as a single tracked-change item, not per-row. Inserted tables render green; deleted tables render struck-through with red rows. Accepting an inserted table accepts contained cell text; rejecting removes the whole table. Right-rail bubble labels "Added table" or "Deleted table."

**PAGEREF field resolution to live target page numbers** — Resolve PAGEREF instructions to display number, format (numeric picture, general format), and relative position (`\p` → "on page N", "above", "below"). Preserve instruction tokens and CHARFORMAT properties for export round-trip. Render in document body with live re-resolution on pagination change.

**SEQ sequence field support** — Import and export SEQ field instructions with numbering, restart, and hidden-field state. Resolve in layout using shared evaluator with counter tracking and heading-level serials. Field discovery reads resolvedNumber; export emits current evaluated results. Preserve raw instructions for lossless round-trip.

**NUMPAGES field formatting with numeric pictures and ordinal** — Parse `\# "0"` (numeric picture) and `\* Ordinal` format switches. Format total page count as "1", "I", "1st" per field switch. Applied to both inserted and refreshed fields. Preserve instructions on import for export parity.

**PAGE field formatting with numeric pictures** — Parse `\# "0"` (numeric pictures) and `\*` general-format switches (Arabic, Roman, Alphabetic, ArabicDash). Format page numbers per field — "1", "i", "A", "1-" — independent of section numbering format. Preserve instructions on import; export round-trips back to same field code.

**SECTIONPAGES field support** — Resolve SECTIONPAGES field to section-aware page count with section-level numbering awareness.

**Document background** — Import and render document-level background image positioned behind all body content.

**Nested content controls** — Import nested block SDTs (SDT inside paragraph) and nested inline SDTs (SDT inside run). Preserve structure through export.

**Templates can be applied to documents** — `doc.template.apply(templateDoc)` applies formatting, styles, and structure from template to active document.

**Author colors for tracked changes** — Per-author tracked change highlights in track-changes UI.

### Improvements

**Multi-column sections now render with explicit per-column widths and gaps** — Authored column widths preserved (no scaling to fit). Per-column gaps (w:col/@w:space) drive separator positions. Footnote assignment and positioning use per-column geometry. Column hit-testing honors per-column boundaries. Separators position at resolved geometry.

**Footnote pagination matches Word's behavior** — Ordered-cluster rule: first N-1 anchors on a page render fully; only Nth may split. Body paginator reserves minStart (first-line height) for each anchor, allowing tight packing. Continuation pages reserve remaining space. One-line widows absorbed by bumping reserve when it eliminates a split. Footnote numbering honors per-section w:footnotePr/@numFmt, @numStart, and eachSect restart. Custom-mark footnotes no longer consume an ordinal. Marker is plain superscript with space gap before body text.

**Tracked change rendering** — Inserted table rows paint green; deleted rows paint struck-through red. Right-rail bubble for structural table changes. Inline changes inside tracked table suppressed (structural bubble only).

**Table border and shading fidelity** — Collapsed shared edges resolve per ECMA-376 §17.4.66 (greater weight wins; style precedence breaks ties). Explicit nil borders suppress dividers. Cell shading — pct/auto patterns resolve to Word-matching blend colors. Row-level tblPrEx borders merged and rendered once per edge. Rowspan continuation rows no longer double interior lines. Keep-next paragraphs before tables reserve only first row height.

**Header/footer refs inherit across multiple sections** — Walk back to nearest prior section defining first/even/default variant (not just immediately prior). First and even no longer fall back to default per OOXML §17.10.1. Preserve section fallback refs in section resolver.

**Field-annotation pills resolve fonts per document** — Pills measure and paint through document's font resolver. Custom substitutes and bundled clones apply to field content. Underline rendering matches adjacent text.

**Anchored tables reposition with per-wrap padding** — Apply correct offset based on wrap value (square, tight, through).

### Fixes

- Image resize in suggesting mode no longer duplicates; uses AttrStep, preserves track-changes state.
- Tracked deletion coalescing in suggesting mode — adjacent Backspace batches into single change.
- Multi-column sections with explicit equal widths now balance correctly.
- Per-column w:space honored for unequal columns per ECMA-376 §17.6.4.
- Hit-testing resolves mid-page column region — column boundaries correct when section breaks change columns mid-page.
- Explicit column layout idempotence — per-column gaps preserved through resolveColumnLayout.
- Table hit-testing uses region column count, not page-start count.
- Fonts: policy-gate legacy embedded @font-face; skip malformed embeds instead of aborting.
- Fonts: strip quotes from structured physical family so load probe matches registered face.
- Fonts: reject conflicting duplicate font-face registrations; identical source is idempotent.
- Fonts: invalidate measure caches on font registration so cached fallback widths refresh.
- Fonts: clear fonts-changed report state on document swap to prevent cache poisoning.
- Fonts: paint underlined tabs flush with text underline; align to baseline offset from resolved line metrics.
- Fonts: measure tab-only lines from tab font, not 12px default.
- Fonts: tab paint-version now keys on font properties so style changes repaints.
- Fonts: harden public font contract — classify six new root exports; remove never-emitted 'config-change' source; fix stale comments.
- Fonts: hasFace now consults register-only set; awaited as_requested family no longer masquerades as registered_face.
- Fonts: registered face whose asset fails drops out of hasFace; gate replans to demote to bundled clone.
- Fonts: explicit map to bundled clone now stored as custom_mapping override; pin beats registered real face.
- Fonts: FontFace built with bucketed weight/style; 500 face no longer answers 400 query.
- Footnote marker: no longer inherits bold/italic/letterSpacing from first body run; appends NBSP for visible gap.
- Footnote band: anchors at bodyMaxY (immediately under body) instead of fixed page-bottom position.
- Footnote range-awareness: demand queried by range so slicing charges only what candidate slice anchors.
- Footnote minimum-start model: body slicer reserves minimum (separator + first line) for each anchor; rest splits to continuation.
- Footnote ordered-cluster: first N-1 anchors on page render fully; only Nth may split.
- Footnote continuation deferral: carryover to next page respects source order; failed continuation plus all later entries deferred together.
- Footnote post-reserve relayouts: separatorSpacingBefore plumbed from planner so body slicer matches planned band height.
- Footnote column advance: per-column footnote counters reset on column change.
- Footnote one-line widows: absorbed by bumping reserve when it eliminates the split.
- Footnote preferred-reserve: scorer allows +1 page growth when trial eliminates doc-level cluster split.
- Footnote split candidates: flagged when last anchor appears in continuationOut AND preferred > actual by meaningful margin.
- Footnote continuation reserve: includes inbound carryover demand as floor so continuation + cluster + overhead fit on next page.
- Footnote separator widths: flipped to match ECMA-376 (standard spans ~50% width; continuation spans ~100%).
- Footnote customMark: no longer consumes an ordinal per §17.11.14; preserves pmStart/pmEnd on empty marker run.
- Footnote section-level numFmt: honored via per-ref override; numRestart=eachSect resets counter at section boundaries.
- Footnote endnote parity: endnoteNumberFormat plumbed through EndnotesBuilder like footnoteNumberFormat.
- Footnote position attribute: w:pos (pageBottom / beneathText / sectEnd / docEnd) read and stored; rendering falls back to pageBottom for non-default.
- Footnote separator content: imported separator/continuationSeparator/continuationNotice content preserved via classifyNoteSeparatorContent.
- Tracked changes: deletion guard on tracked rewrite; empty text nodes rejected, replaced with delete instead.
- Tracked changes: nested replacement tracked-change decisions now resolved correctly.
- Tracked changes: content handling in replaceStep prevents data loss.
- Content controls: nested block/inline SDTs imported with structure preserved and exported correctly.
- Table rendering: keepNext heading reserves table first row (not full table), allowing split.
- Table cells: shading pct/auto patterns resolved to Word-matching blend colors.
- Table cell borders: collapsed shared-edge resolution per ECMA-376 §17.4.66; explicit nil suppresses divider on both sides.
- Table cell borders: style-bordered row above tblPrEx-suppressed shared edge no longer loses closing border.
- Table rowspan: continuation rows no longer double interior lines via occupancy-inclusive rightmost/nextRowMaxCol.
- Page numbering: section-aware displayNumber used for odd/even header parity, not physical page index.
- PAGE field: case-insensitive dispatch; lowercase page/numpages fields no longer resolve to cached static text.
- Header/footer: first/even refs no longer fall back to default when intermediate sections omit them; walk back to nearest defining section.
- Header/footer: converters' legacy refs no longer leaked into per-section resolution.
- Header/footer: span-page-relative behindDoc media no longer shift normal footer/header fragments.
- List markers: caps (FIRST/SECOND/THIRD) now render via allCaps/smallCaps on marker.run.
- RTL tabs: underline overlay gated on !isRtl; RTL keeps native text-decoration.
- Deletion coalescing: excluded structured changes so cell-level changes inside a structural row deletion don't batch.
- Template apply: imports styles, formatting, and structure from template to active document.
- Content control chrome='none': custom hover wins on locked SDTs; background re-asserted after base lock-hover rules.
- LLM tools executor: empty text nodes on tracked rewrite now handled via delete instead of schema.text("").
- LLM tools operation-args: oneOf validation errors now discriminator-aware; surface specific failure instead of generic "must match one of" message.
### What's New

**Fonts API for custom registration and per-document mapping**
- `superdoc.fonts.add({ family, weight, style }, url)` registers custom fonts and awaits them before layout
- `superdoc.fonts.map({ 'Calibri': 'Carlito', ... })` maps logical families per editor — custom substitutes override bundled defaults
- `superdoc.fonts.preload(['Georgia', ...])` loads families before measurement
- `superdoc.fonts.getReport()`, `getMissingFonts()`, `getDocumentFonts()` surface font diagnostics
- Font changes emit `fonts-changed` events with load status and resolution reasons

**Bundled font substitutes for common Word families**
- Calibri, Cambria, Arial, Times New Roman, Courier New load metric-compatible open substitutes (Carlito, Liberation Sans, Liberation Serif, Liberation Mono) — no configuration required
- Helvetica aliases to bundled Liberation Sans
- All substitutes load before measurement — rendering is deterministic, no late-load reflow

**Structural tracked changes for tables**
- Import whole-table insert/delete as single tracked-change item (not per-row)
- Render visually — inserted tables green, deleted tables struck-through with red rows
- Cell text inside tracked table subsumed into structural change — one review bubble, one decision
- Accept cascades: accepting inserted table accepts contained cell text; rejecting removes whole table
- Right-rail bubble labels "Added table" / "Deleted table"

**PAGEREF field resolution to live target page numbers**
- Resolve PAGEREF instructions to display number, format (numeric picture, general format), and relative position (`\p` → "on page N", "above", "below")
- Preserve instruction tokens and CHARFORMAT properties for export round-trip
- Render in document body with live re-resolution on pagination change

**SEQ sequence field support**
- Import and export SEQ field instructions with numbering, restart, and hidden-field state
- Resolve in layout using shared evaluator: counter tracking, heading-level serials, explicit restart precedence
- Field discovery reads resolvedNumber; export emits current evaluated results
- Preserve raw instructions for lossless round-trip

**NUMPAGES field formatting with numeric pictures and ordinal**
- Parse `\# "0"` (numeric picture) and `\* Ordinal` format switches on insert and import
- Format total page count as "1", "I", "1st" per field switch
- Applied to both inserted and refreshed fields
- Preserve instructions on import for export parity

**PAGE field formatting with numeric pictures**
- Parse `\# "0"` (numeric pictures) and `\*` general-format switches (Arabic, Roman, Alphabetic, ArabicDash)
- Format page numbers per field — "1", "i", "A", "1-" — independent of section numbering format
- Preserve instructions on import; export round-trips back to same field code

**SECTIONPAGES field support**
- Resolve SECTIONPAGES field to section-aware page count with section-level numbering awareness

**Document background**
- Import and render document-level background image positioned behind all body content

**Nested content controls**
- Import nested block SDTs (SDT inside paragraph) and nested inline SDTs (SDT inside run)
- Preserve structure through export

**Templates can be applied to documents**
- `doc.template.apply(templateDoc)` applies formatting, styles, and structure from template to active document

**Author colors for tracked changes**
- Per-author tracked change highlights in track-changes UI

### Improvements

**Multi-column sections now render with explicit per-column widths and gaps**
- Authored column widths preserved — no scaling to fit content area
- Per-column gaps (w:col/@w:space) drive separator positions, not uniform section gap
- Footnote assignment and positioning use per-column geometry
- Column hit-testing honors per-column boundaries, not uniform stride
- Separators position at resolved geometry

**Footnote pagination matches Word's behavior**
- Ordered-cluster rule — first N-1 anchors on a page render fully, only Nth may split across pages
- Body paginator reserves minStart (first-line height) for each new anchor, allowing tight packing
- Continuation pages reserve only remaining space after cluster obligation
- One-line widows absorbed by bumping reserve when eliminates a split
- Footnote numbering honors per-section w:footnotePr/@numFmt, @numStart, and eachSect restart
- Custom-mark footnotes (customMarkFollows) no longer consume an ordinal
- Marker is plain superscript with space gap before body text

**Tracked change rendering**
- Inserted table rows paint green, deleted rows paint struck-through red
- Right-rail bubble for structural table changes
- Inline changes inside tracked table suppressed (structural bubble only)

**Table border and shading fidelity**
- Collapsed shared edges resolve per ECMA-376 §17.4.66 — greater weight wins, style precedence breaks ties
- Explicit nil borders suppress dividers (both adjacent cells nil = no line)
- Cell shading — pct/auto patterns resolve to Word-matching blend colors
- Row-level tblPrEx borders merged and rendered once per edge
- Rowspan continuation rows no longer double interior lines
- Keep-next paragraphs before tables reserve only first row height (table splits normally)

**Header/footer refs inherit across multiple sections**
- Walk back to nearest prior section defining first/even/default variant (not just immediately prior)
- first and even no longer fall back to default per OOXML §17.10.1
- Preserve section fallback refs in section resolver

**Field-annotation pills resolve fonts per document**
- Pills measure and paint through document's font resolver
- Custom substitutes and bundled clones apply to field content
- Underline rendering matches adjacent text

**Anchored tables reposition with per-wrap padding**
- Apply correct offset based on wrap value (square, tight, through)

**Underlined tabs render flush with text underline**
- Tab underline anchor aligns to line-box top and ends at baseline offset
- Measured ascent/descent from tab font drives line height
- One merged overlay for text and tab underlines, consistent weight throughout

### Fixes

- Image resize in suggesting mode no longer duplicates (uses AttrStep, preserves track-changes state)
- Tracked deletion coalescing in suggesting mode — adjacent Backspace batches into single change
- Multi-column sections with explicit equal widths now balance correctly (only skip genuinely-unequal widths)
- Per-column w:space honored for unequal columns per ECMA-376 §17.6.4
- Hit-testing resolves mid-page column region — column boundaries correct when section breaks change columns mid-page
- Explicit column layout idempotence — per-column gaps preserved through resolveColumnLayout
- Table hit-testing uses region column count, not page-start count
- Cell border conflict resolution respects explicit nil borders on both sides of shared edges
- Rowspan continuation rows include spanning cells in measure, preventing doubled interior lines
- Nested tracked-change decisions resolve structural changes on shared revision ids
- Nested content controls import and export without data loss
- Empty text runs skip flow accounting, preventing phantom spaces and height inflation
- Field keyword dispatch case-insensitive (lowercase PAGE/NUMPAGES now work in headers/footers)
- SEQ field result caching preserves lowercase seq fields and cached numbers nested inside runs
- Continuation separators and dividers correctly sized per ECMA-376 (standard ≈ 0.5× width, continuation ≈ full width)
- Font embedding policy gated on embedding check; malformed embeds skip instead of aborting
- Provider precedence: registered real face > bundled substitute > as-requested
- Quoted primary family normalized through resolution, collapsing distinct signatures
- Header/footer display numbers for odd/even parity respect per-section numbering restarts and offsets
- Section inheritance walks back across all prior sections, not just immediately preceding
- Legacy header/footer refs preserved through adapter, not exposed through resolution
- Page field format switches case-insensitive and preserve internal whitespace in quoted numeric pictures
- TOC updates followed by F9 preserve SEQ position shifts
- Comment resolution on whole-thread level with batch processing (dangling comments after resolve fixed)
- Nested replacement tracked-change decisions handle replacement boundaries correctly
@superdoc-bot superdoc-bot Bot added the patch-sync Patch sync from release branch label Jun 6, 2026
@superdoc-bot superdoc-bot Bot requested a review from a team as a code owner June 6, 2026 00:05
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 197d2dc3a3

ℹ️ 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".

"name": "superdoc",
"type": "module",
"version": "1.38.0",
"version": "1.39.0",
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve stable ancestry for stable sync

This version bump is only safe when it lands as the real stable→main merge, but the reviewed commit (9ea829bd0868d46398b974a960fff91e33352024) has a single parent rather than the stable parent. The sync workflow explicitly requires git merge --no-ff origin/stable so stable release tags are reachable for semantic-release's @next version floor (.github/workflows/sync-patches.yml:143-146); merging these version-only lines as a normal/squashed commit leaves those tags unreachable and the next prerelease calculation still broken. Please replace this with the actual merge commit that includes stable as the second parent.

Useful? React with 👍 / 👎.

@caio-pizzol caio-pizzol merged commit 4ce95e9 into main Jun 6, 2026
38 of 39 checks passed
@caio-pizzol caio-pizzol deleted the sync/stable-to-main-20260606-000549 branch June 6, 2026 00:09
@codecov-commenter
Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

patch-sync Patch sync from release branch review: quick

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants