diff --git a/crates/docx/src/read/props.rs b/crates/docx/src/read/props.rs index d6d97914e..0ba0132a5 100644 --- a/crates/docx/src/read/props.rs +++ b/crates/docx/src/read/props.rs @@ -42,7 +42,19 @@ impl PropCtx { .filter(|f| !f.is_empty()) .map(str::to_string) .or_else(|| k.attr("w:asciiTheme").or_else(|| k.attr("w:hAnsiTheme")).and_then(|t| self.theme_font(t))) - .or_else(|| k.attr("w:cs").or_else(|| k.attr("w:eastAsia")).filter(|f| !f.is_empty()).map(str::to_string)); + .or_else(|| { + // `w:eastAsia` and `w:cs` name fonts for other scripts; they stand in for the + // Latin font only when the run is hinted as that script. Otherwise the Latin + // font is inherited (a Normal style with only `w:eastAsia`/`w:cs` keeps the + // `docDefaults` font, as in Word). + match k.attr("w:hint") { + Some("eastAsia") => k.attr("w:eastAsia"), + Some("cs") => k.attr("w:cs"), + _ => None, + } + .filter(|f| !f.is_empty()) + .map(str::to_string) + }); } "w:b" => c.bold = Some(on_off(k)), "w:i" => c.italic = Some(on_off(k)), diff --git a/crates/docx/tests/fixtures.rs b/crates/docx/tests/fixtures.rs index 7f1d99b7f..d91d6a8b5 100644 --- a/crates/docx/tests/fixtures.rs +++ b/crates/docx/tests/fixtures.rs @@ -389,6 +389,25 @@ fn header_self_reference_and_escaping_targets() { wordcraft_docx::read(&out).unwrap(); } +#[test] +fn style_rfonts_without_ascii_inherits_doc_defaults_font() { + // Issue #93: Normal names only East Asian / complex-script fonts, so Latin text keeps the + // docDefaults font. A run hinted as East Asian still uses its East Asian font. + let styles = format!( + r#" + + +"# + ); + let body = r#"Plain漢字"#; + let d = wordcraft_docx::read(&docx(body, &[("rId1", "styles", "styles.xml")], &[("word/styles.xml", &styles)])).unwrap(); + assert_eq!(d.styles.get("Normal").unwrap().chr.font, None); + let ps = paras(&d); + let font = |p: &Paragraph| d.styles.resolve_char(p.props.style.as_deref(), &p.runs[0].props).font; + assert_eq!(font(ps[0]), "Calibri"); + assert_eq!(font(ps[1]), "SimSun"); +} + #[test] fn bare_break_directly_in_paragraph() { let body = r#"FirstSecondThird"#;