From 949d6c2830344162fb302baa558dff906b821c8b Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Sat, 24 Oct 2026 13:51:32 -0700 Subject: [PATCH] M4: text on a path keeps its effects when the path's effect style has none A path linked to an effect style with no effects gave the text an empty effect list and the style link. The text now takes the path's effects only when there are any. --- crates/engine/src/cmd/text.rs | 2 +- crates/engine/src/draw_tools_tests.rs | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/crates/engine/src/cmd/text.rs b/crates/engine/src/cmd/text.rs index 2998885f..0334a72a 100644 --- a/crates/engine/src/cmd/text.rs +++ b/crates/engine/src/cmd/text.rs @@ -147,7 +147,7 @@ fn on_path(s: &mut Session, p: &Value) -> Result { let (parent, index) = (pn.parent.ok_or_else(|| bad(CMD, "pages have no path"))?, doc.index_in_parent(path)); let (transform, w, h) = (pn.transform, pn.width, pn.height); let fills = pn.fills.iter().any(|f| f.visible).then(|| (pn.fills.clone(), pn.styles.fill.clone())); - let effects = (!pn.effects.is_empty() || pn.styles.effect.is_some()).then(|| (pn.effects.clone(), pn.styles.effect.clone())); + let effects = (!pn.effects.is_empty()).then(|| (pn.effects.clone(), pn.styles.effect.clone())); let moved = |k: &str| (fills.is_some() && (k == "fill" || k.starts_with("fills/"))) || (effects.is_some() && k.starts_with("effects/")); let bound: Vec<(String, VariableId)> = pn.bound.iter().filter(|(k, _)| moved(k)).map(|(k, v)| (k.clone(), v.clone())).collect(); let mut tp = TextPath::new(&geom); diff --git a/crates/engine/src/draw_tools_tests.rs b/crates/engine/src/draw_tools_tests.rs index c989a7b7..d33a48ff 100644 --- a/crates/engine/src/draw_tools_tests.rs +++ b/crates/engine/src/draw_tools_tests.rs @@ -128,6 +128,20 @@ fn text_put_on_a_stroke_only_path_keeps_its_own_fill_and_effects() { assert_eq!((&n.fills, &n.effects, &n.bound), (&text.fills, &text.effects, &text.bound), "a path with no fill or effects to give"); } +#[test] +fn text_put_on_a_path_with_an_empty_effect_style_keeps_its_own_effects() { + let mut s = Session::new(); + let (t, v) = text_and_curve(&mut s, json!({"fills": []})); + let none = s.execute("style.create", &json!({"type": "effect", "name": "None", "value": []})).unwrap()["id"].clone(); + s.execute("style.apply", &json!({"ids": [v], "style": none})).unwrap(); + let path = s.doc().unwrap().node(v).unwrap().clone(); + assert!(path.effects.is_empty() && path.styles.effect.is_some()); + let text = s.doc().unwrap().node(t).unwrap().clone(); + s.execute("text.onPath", &json!({"ids": [t, v]})).unwrap(); + let n = s.doc().unwrap().node(t).unwrap(); + assert_eq!((&n.effects, &n.styles.effect), (&text.effects, &text.styles.effect), "a path whose effect style draws no effects"); +} + #[test] fn pattern_fills_tile_their_source_live() { let mut s = Session::new();