From b1c515fd2f84ae0102d7cdb321adf7f73980605c Mon Sep 17 00:00:00 2001 From: Steffen Becker Date: Thu, 6 Aug 2026 19:18:58 +0000 Subject: [PATCH] fix: adopt the DOCX document default font into the Standard style Body paragraphs carry no w:pStyle, so the default style never reached usedStyles and Standard kept the editor's Liberation Serif 12pt. Run suppression meanwhile resolves against w:docDefaults, so the file's body font was dropped with no run mark left to render it: a Calibri 11pt Word document rendered as Liberation Serif 12pt. --- src/lib/import/docx.ts | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/lib/import/docx.ts b/src/lib/import/docx.ts index 8f32632..3af80f7 100644 --- a/src/lib/import/docx.ts +++ b/src/lib/import/docx.ts @@ -594,7 +594,10 @@ function stylePara(ctx: Ctx, id: string | null): ParaProps { // paragraph's formatting, so the document defaults are not its properties. function styleText(ctx: Ctx, id: string | null, own = false): TextProps { if (!id) return {}; - const run = own ? ctx.styles.styleOwn(id) : ctx.styles.paragraphRun(id); + return runTextProps(own ? ctx.styles.styleOwn(id) : ctx.styles.paragraphRun(id)); +} + +function runTextProps(run: RunProps): TextProps { const out: TextProps = {}; // Our own export declares the metric twin; keep the registry on the on-screen name. if (run.font) out.fontFamily = run.font === 'Times New Roman' ? 'Liberation Serif' @@ -615,7 +618,13 @@ function collectStyleSheet(ctx: Ctx): StyleSheet { const defs = ctx.styles.namedParagraphStyles(); const sheet = builtinStyleSheet(); const keep = new Set(); - for (const id of ctx.usedStyles) { + // Body paragraphs carry no w:pStyle, so the default style never reaches usedStyles — + // yet run suppression resolves against it, so Standard has to carry it or the file's + // body font is dropped without any run mark taking its place. + const roots = new Set(ctx.usedStyles); + const defaultId = ctx.styles.defaultParagraphStyle(); + if (defaultId) roots.add(defaultId); + for (const id of roots) { let cur: string | null = id; const seen = new Set(); while (cur && defs.has(cur) && !seen.has(cur)) { @@ -642,6 +651,10 @@ function collectStyleSheet(ctx: Ctx): StyleSheet { if (level) style.outlineLevel = Number(level[1]); sheet.paragraph[name] = style; } + // w:docDefaults alone can carry the body font (a file need not declare a default + // style), and it is what run suppression compares against — so Standard tracks it. + const standard = sheet.paragraph[DEFAULT_STYLE]; + if (standard) standard.text = { ...standard.text, ...runTextProps(ctx.styles.paragraphRun(null)) }; for (const id of ctx.usedCharStyles) { const name = ctx.charStyleNames.get(id) ?? id; const builtin = sheet.character[name];