Skip to content

Zero-edit open→save injects w:rFonts into heading runs and drops 61KB from document.xml #2767

Description

@unhingedpanda

What happened?

A zero-edit round-trip (opensaveclose) through the SuperDoc CLI silently corrupts the DOCX by:

  1. Injecting 197 explicit w:rFonts elements into heading runs that previously had no run-level formatting (inheriting font from their w:pStyle definition)
  2. Dropping ~61KB from word/document.xml (741,386 → 680,141 bytes)
  3. Modifying word/styles.xml (62,027 → 65,997 bytes) and word/numbering.xml (24,639 → 23,011 bytes)

This causes headings to render with different fonts when opened in Microsoft Word, because the injected partial w:rFonts (e.g., w:ascii="Arial" w:hAnsi="Arial" without w:eastAsia or w:cs) breaks the style inheritance chain and causes Word to fall back to the theme font (Aptos Display) for some rendering paths.

Before (correct):

<!-- Paragraph style=Ttulo1, which defines Arial Bold 16pt -->
<w:r>
  <w:t>Article One — Heading Text</w:t>
  <!-- No rPr → inherits Arial from Ttulo1 style -->
</w:r>

After open→save (corrupted):

<w:r>
  <w:rPr>
    <w:rFonts w:ascii="Arial" w:hAnsi="Arial"/>
    <!-- Partial rFonts injected — breaks theme font inheritance in Word -->
  </w:rPr>
  <w:t>Article One — Heading Text</w:t>
</w:r>

The same corruption reproduces in the browser editor (exportDocx()) and the headless CLI — confirming it's in the core OOXML serializer, not the React wrapper or browser APIs.

Impact

Documents opened in Word after a SuperDoc round-trip show visibly different heading fonts, colors, and sizing. For legal document workflows (trusts, contracts), this level of formatting corruption is unacceptable.

Reproduction

The affected document uses localized Portuguese-named heading styles (e.g., Ttulo1 for heading 1, Ttulo2 for heading 2) generated by estate planning document software. These styles define explicit w:rFonts at the style level, and heading runs inherit via w:pStyle with no run-level w:rPr.

CLI reproduction (3 commands):

cp input.docx output.docx
superdoc open output.docx
superdoc save
superdoc close

Verification script (Python):

import zipfile, xml.etree.ElementTree as ET

ns = {'w': 'http://schemas.openxmlformats.org/wordprocessingml/2006/main'}

for label, path in [('BEFORE', 'input.docx'), ('AFTER', 'output.docx')]:
    with zipfile.ZipFile(path) as z:
        tree = ET.parse(z.open('word/document.xml'))
        root = tree.getroot()
        rfonts = sum(
            1 for p in root.iter(f'{{{ns["w"]}}}p')
            for r in p.findall('.//w:r', ns)
            if r.find('.//w:rPr/w:rFonts', ns) is not None
        )
        doc_size = len(z.read('word/document.xml'))
        print(f'{label}: document.xml={doc_size} bytes, w:rFonts in runs={rfonts}')

Expected output:

BEFORE: document.xml=741386 bytes, w:rFonts in runs=130
AFTER:  document.xml=680141 bytes, w:rFonts in runs=327

I will attach the sanitized reproduction DOCX file (all text replaced with lorem ipsum, no real content) in a comment below.

rFonts injection breakdown by paragraph style

Style ID Style Name Before After Injected
Ttulo3 heading 3 1 131 +130
Ttulo2 heading 2 0 124 +124
Sumrio1 toc 1 0 30 +30
Ttulo1 heading 1 0 16 +16
Ttulo4 heading 4 0 10 +10
TitleDocument Title Document 0 2 +2
DocumentTitle 0 1 +1
TitlePage 0 1 +1
DocumentTOC 0 1 +1

Document characteristics that trigger the bug

  • Styles use localized (Portuguese) w:styleId names (Ttulo1, Ttulo2, etc.) with English w:name values (heading 1, heading 2)
  • Heading styles define fonts via explicit w:rFonts at the style level: w:ascii="Arial" w:hAnsi="Arial" w:cs="Times New Roman"
  • Theme fonts are majorFont=Aptos Display, minorFont=Aptos
  • Heading runs have no w:rPr — they rely entirely on style inheritance
  • Document includes TOC styles (Sumrio1, Sumrio2), custom styles (TitleDocument, TextHeading2, TextHeading3), and complex numbering definitions

A simpler DOCX with standard English-named styles (Heading 1, Heading 2) does not trigger the rFonts injection — SuperDoc injects w:color instead but not w:rFonts. The localized style names appear to be a factor.

Environment

  • @superdoc-dev/cli v0.5.1
  • superdoc v1.24.2 (browser — same result)
  • @superdoc-dev/react v1.0.0-rc.2 (browser — same result)
  • Node.js v22.22.2
  • macOS (ARM64)

Related issues

SuperDoc version

@superdoc-dev/cli v0.5.1, superdoc v1.24.2

Browser

Not applicable (reproduced headlessly via CLI)

Additional context

The round-trip also loses ~61KB from document.xml even with zero edits. This suggests the serializer is dropping or restructuring OOXML elements it doesn't fully model internally. The word/theme/theme1.xml is the only part that survives completely unchanged.

The documentation states "Round-trip fidelity: import, edit, export — nothing lost" — this reproduction directly contradicts that claim for documents with localized heading styles.

Metadata

Metadata

Assignees

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions