diff --git a/crates/engine/src/cmd/design.rs b/crates/engine/src/cmd/design.rs
index d1fedc160..eb39891d1 100644
--- a/crates/engine/src/cmd/design.rs
+++ b/crates/engine/src/cmd/design.rs
@@ -169,11 +169,19 @@ fn style_set(s: &mut Session, v: &Value) -> CmdResult {
_ => {}
}
}
- // Keep custom styles and fonts.
- let customs: Vec<_> = s.doc.styles.styles.iter().filter(|x| !x.builtin).cloned().collect();
- fresh.styles.extend(customs);
- fresh.default_chr.font = s.doc.styles.default_chr.font.clone();
- s.doc.styles = fresh;
+ // Overlay the set onto the document's sheet: every other style, the latent styles and the
+ // document defaults stay as they are (no set defines its own defaults).
+ for mut st in fresh.styles {
+ match s.doc.styles.styles.iter_mut().find(|x| x.id == st.id) {
+ Some(old) => {
+ st.default = old.default;
+ st.locked = old.locked;
+ st.builtin = old.builtin;
+ *old = st;
+ }
+ None => s.doc.styles.styles.push(st),
+ }
+ }
let (h, b) = (s.doc.settings.major_font.clone(), s.doc.settings.minor_font.clone());
set_fonts(s, &h, &b);
set_accent(s, accent);
diff --git a/crates/engine/src/tests.rs b/crates/engine/src/tests.rs
index f44e5c716..1f273d828 100644
--- a/crates/engine/src/tests.rs
+++ b/crates/engine/src/tests.rs
@@ -10577,3 +10577,38 @@ fn export_png_refuses_a_path_that_is_not_png() {
}
let _ = std::fs::remove_dir_all(&dir);
}
+
+#[test]
+fn style_set_keeps_imported_styles_latent_styles_and_defaults() {
+ use wordcraft_doc::props::{Rgb, TextColor};
+ use wordcraft_doc::styles::{Style, StyleKind};
+ for name in ["basic", "lines", "shaded", "casual", "centered", "minimalist", "title"] {
+ let mut s = s();
+ let mut body = s.doc.styles.get("Normal").unwrap().clone();
+ body.id = "BodyText".into();
+ body.name = "Body Text".into();
+ body.builtin = true;
+ body.based_on = Some("Normal".into());
+ body.chr.size = Some(20.0);
+ body.chr.color = Some(TextColor::Rgb(Rgb(0xFF, 0, 0)));
+ let grid = Style { id: "GridTable4".into(), name: "Grid Table 4".into(), kind: StyleKind::Table, builtin: true, ..Default::default() };
+ s.doc.styles.styles.push(body.clone());
+ s.doc.styles.styles.push(grid.clone());
+ s.doc.styles.latent_styles = Some("".into());
+ s.doc.styles.default_chr.lang = Some("de-DE".into());
+ s.doc.styles.default_chr.size = Some(11.0);
+ run(&mut s, "design.styleSet", json!({ "name": name }));
+ assert_eq!(s.doc.styles.get("BodyText"), Some(&body), "{name}: BodyText");
+ assert_eq!(s.doc.styles.get("GridTable4"), Some(&grid), "{name}: GridTable4");
+ assert_eq!(s.doc.styles.latent_styles.as_deref(), Some(""), "{name}");
+ assert_eq!(s.doc.styles.default_chr.lang.as_deref(), Some("de-DE"), "{name}");
+ assert_eq!(s.doc.styles.default_chr.size, Some(11.0), "{name}");
+ assert_eq!(s.doc.styles.styles.iter().filter(|x| x.id == "Heading1").count(), 1, "{name}");
+ let saved = crate::io::save_bytes("x.docx", &s.doc).unwrap();
+ let back = crate::io::open_bytes("x.docx", &saved).unwrap();
+ assert!(back.styles.get("BodyText").is_some(), "{name}: BodyText after round trip");
+ }
+ let mut s = s();
+ run(&mut s, "design.styleSet", json!({ "name": "title" }));
+ assert_eq!(s.doc.styles.get("Title").unwrap().chr.size, Some(36.0));
+}