fix: write STYLEREF by outline level, not style name

Word resolves the quoted style name against the localized built-in
name, so 'STYLEREF "Heading 1"' shows a field error in any
non-English Word. The numeric argument means outline level in Word
and LibreOffice alike (probed), and the importer already reads it.
This commit is contained in:
Steffen Becker committed 2026-08-30 10:06:42 +00:00
1 parent 43903319d9
commit 3d305a9920
2 files changed
+8 -2

No files matched your search

+4 -2
View File
@@ -670,8 +670,10 @@ function inlineToRuns(content: TiptapNode[] = [], force: TextProps = {}): Inline
} else if (node.type === 'pageCount') {
out.push(new TextRun({ children: [PageNumber.TOTAL_PAGES], ...runPropsFromMarks(node.marks) }));
} else if (node.type === 'chapterField') {
// Word's running head: STYLEREF picks the heading of that level in force on the page.
out.push(new SimpleField(`STYLEREF "Heading ${Number(node.attrs?.level) || 1}" \\* MERGEFORMAT`, String(node.attrs?.text ?? '')));
// Word's running head: STYLEREF picks the heading of that level in force on the
// page. The numeric form means outline level; a style name would be looked up
// localized ("Heading 1" errors in a German Word, which wants "Überschrift 1").
out.push(new SimpleField(`STYLEREF ${Number(node.attrs?.level) || 1} \\* MERGEFORMAT`, String(node.attrs?.text ?? '')));
} else if (node.type === 'dateTimeField') {
out.push(dateTimeRun(node));
} else if (node.type === 'sequenceField') {
+4
View File
@@ -36,6 +36,10 @@ describe('chapter field', () => {
it('round-trips through DOCX as a STYLEREF field', async () => {
const bytes = await buildDocx(doc, margins, 'portrait', hf);
// The numeric form (outline level) — Word resolves a quoted style name against the
// localized name, so "Heading 1" errors in any non-English Word.
const ftr = strFromU8(unzipSync(bytes)['word/footer1.xml']);
expect(ftr).toContain('w:instr="STYLEREF 1 \\* MERGEFORMAT"');
const back = importDocx(bytes);
// Word caches the shown name in the field result, which the import drops — the
// level is what the live field needs.