From 1ad74d1ddffa0aafae21b794acabee0fda634897 Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Tue, 3 Nov 2026 16:52:40 -0800 Subject: [PATCH] Type: reject doubly signed OpenType feature tags --- crates/engine/src/cmd/textstyles/tests.rs | 15 +++++++++++++++ crates/text/src/features.rs | 5 ++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/crates/engine/src/cmd/textstyles/tests.rs b/crates/engine/src/cmd/textstyles/tests.rs index 90bb32bbb..19da4c8ab 100644 --- a/crates/engine/src/cmd/textstyles/tests.rs +++ b/crates/engine/src/cmd/textstyles/tests.rs @@ -335,6 +335,21 @@ fn character_style_color_and_features_are_checked() { assert_eq!(s.doc().unwrap().history.undo.len(), undo); } +#[test] +fn text_style_rejects_doubly_signed_feature_tags() { + let (mut s, id) = session_with_text("Text"); + let before = runs(&s, id); + let undo = s.doc().unwrap().history.undo.len(); + for cmd in ["text.setStyle", "text.setRangeStyle"] { + for tag in ["--liga", "+-dnom", "-+ss01", "++liga"] { + let error = s.execute(cmd, &json!({"id": id, "start": 0, "end": 4, "features": [tag]})).unwrap_err().to_string(); + assert!(error.contains(&format!("`{tag}`")), "{cmd}: {error}"); + } + } + assert_eq!(runs(&s, id), before); + assert_eq!(s.doc().unwrap().history.undo.len(), undo); +} + #[test] fn style_paints_count_as_swatch_uses_and_gradient_stops_are_checked() { let (mut s, _) = session_with_text("Text"); diff --git a/crates/text/src/features.rs b/crates/text/src/features.rs index 3a75f2579..eac3b3c3f 100644 --- a/crates/text/src/features.rs +++ b/crates/text/src/features.rs @@ -146,7 +146,7 @@ impl OtFeatures { /// Is `tag` (optionally prefixed with `-` or `+`) one this set understands? pub fn known_tag(tag: &str) -> bool { - let t = tag.trim_start_matches(['-', '+']); + let t = tag.strip_prefix(['-', '+']).unwrap_or(tag); matches!(t, "liga" | "calt" | "dlig" | "smcp" | "frac" | "onum" | "tnum" | "ordn" | "swsh") || EXTRA_FEATURES.contains(&t) } @@ -306,6 +306,9 @@ mod tests { assert!(OtFeatures::known_tag(t), "{t}"); } assert!(!OtFeatures::known_tag("ss21")); + for tag in ["--liga", "+-dnom", "-+ss01", "++liga"] { + assert!(!OtFeatures::known_tag(tag), "{tag}"); + } let o = OtFeatures::from_tags(["ss03", "pnum", "sups", "-liga"]); assert_eq!(o.to_tags(), vec!["-liga", "ss03", "pnum", "sups"]); assert_eq!(OtFeatures::from_tags(["ss03", "-ss03"]).to_tags(), Vec::::new(), "turned off again");