From a1f6eaed7776ac301a49ab31d2d1c4f818d96f07 Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Thu, 12 Nov 2026 21:25:12 -0800 Subject: [PATCH] .ai import: a one-paint style reads the last fill and stroke paint A one-paint style took a gradient (`Bg`) after a solid stroke color as the solid stroke, and left out a style whose solid fill color came after a pattern or a gradient. Each later paint operator now replaces the one before it on its own side, and a gradient marks both sides as not read, since its operands do not say which one it paints. --- crates/eps/src/import/ai/style.rs | 20 +++++++++++++++----- crates/eps/src/import/ai/style/tests.rs | 11 ++++++++++- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/crates/eps/src/import/ai/style.rs b/crates/eps/src/import/ai/style.rs index 43e457100f..27657f570b 100644 --- a/crates/eps/src/import/ai/style.rs +++ b/crates/eps/src/import/ai/style.rs @@ -776,15 +776,25 @@ struct OnePaint { paint: Filter, /// Its `stroke fill Xd` was read. xd: bool, - /// It strokes with a pattern (`P`). - stroke_pattern: bool, + /// Its last fill paint is a pattern (`p`) or may be a gradient (`Bg`). + fill_unread: bool, + /// Its last stroke paint is a pattern (`P`) or may be a gradient (`Bg`). + stroke_unread: bool, } impl OnePaint { fn op(&mut self, op: &str, vals: &[V], names: &mut Names) { match op { "Xd" => self.xd |= vals.iter().filter_map(V::num).count() >= 2, - "P" => self.stroke_pattern = true, + "p" => self.fill_unread = true, + "P" => self.stroke_unread = true, + // Read as either one's, since its operands don't say which it paints. + "Bg" => (self.fill_unread, self.stroke_unread) = (true, true), + // A color replaces what came before it; one that isn't read leaves none. + "Xa" | "k" | "g" | "x" | "Xx" | "Xk" | "Xs" | "Xz" | "xa" | "xx" | "xk" | "xs" | "xz" => { + (self.fill_unread, self.paint.fill) = (false, None); + } + "XA" | "K" | "G" | "X" | "XX" | "XK" | "XS" | "XZ" => (self.stroke_unread, self.paint.stroke) = (false, None), _ => {} } paint_op(&mut self.paint, op, vals, names); @@ -800,13 +810,13 @@ impl OnePaint { let mut items = vec![]; if filled { match &f.fill { - Some(p) if !f.gradient && f.pattern.is_none() => items.push(StyleItem::Fill(p.clone())), + Some(p) if !self.fill_unread => items.push(StyleItem::Fill(p.clone())), _ => return Err(()), } } if stroked { match &f.stroke { - Some(p) if !self.stroke_pattern => items.push(StyleItem::Stroke { + Some(p) if !self.stroke_unread => items.push(StyleItem::Stroke { paint: p.clone(), width: f.width, cap: f.cap, diff --git a/crates/eps/src/import/ai/style/tests.rs b/crates/eps/src/import/ai/style/tests.rs index 2ada369b28..29a6e996bb 100644 --- a/crates/eps/src/import/ai/style/tests.rs +++ b/crates/eps/src/import/ai/style/tests.rs @@ -1534,12 +1534,21 @@ fn a_style_of_one_paint_reads_as_the_fill_and_stroke_its_xd_turns_on() { // Only what `Xd` turns on; nothing when it turns on neither. assert!(matches!(one(&format!("0 0 1 0 k\n{stroke}1 0 Xd\n")), Some(Ok(i)) if matches!(i.as_slice(), [StyleItem::Stroke { .. }]))); assert_eq!(one(&format!("0 0 1 0 k\n{stroke}0 0 Xd\n")), Some(Ok(vec![]))); - // Not read: no `Xd`, a gradient or pattern fill, a pattern stroke, a paint turned on with no color. + // The last paint of each one counts: a solid color after a pattern or a gradient replaces it. + let solid_fill = |before: &str| one(&format!("{before}\n0 0 1 0 k\n0 1 Xd\n")); + for before in ["(Dots) 0 0 1 1 0 0 0 0 0 [1 0 0 1 0 0] p", "0 Bb\n2 (Sky) 0 0 0 100 1 0 0 1 0 0 Bg\n0 BB"] { + assert!(matches!(solid_fill(before), Some(Ok(i)) if matches!(i.as_slice(), [StyleItem::Fill(_)])), "{before}"); + } + let solid_stroke = one("(Dots) 0 0 1 1 0 0 0 0 0 [1 0 0 1 0 0] P\n0 1 1 0 K\n1 0 Xd\n"); + assert!(matches!(solid_stroke, Some(Ok(i)) if matches!(i.as_slice(), [StyleItem::Stroke { .. }]))); + // Not read: no `Xd`, a gradient or pattern fill, a pattern stroke, a gradient stroke after a solid one, a paint + // turned on with no color. for paint in [ format!("0 0 1 0 k\n{stroke}"), "0 Bb\n2 (Sky) 0 0 0 100 1 0 0 1 0 0 Bg\n0 BB\n0 1 Xd\n".to_string(), "(Dots) 0 0 1 1 0 0 0 0 0 [1 0 0 1 0 0] p\n0 1 Xd\n".to_string(), "(Dots) 0 0 1 1 0 0 0 0 0 [1 0 0 1 0 0] P\n1 0 Xd\n".to_string(), + "0 R\n0 1 1 0 K\nBb\n0 0 0 0 Bh\n2 (Ramp) 0 0 90 1 1 0 0 1 0 0 1 Bg\n0 BB\n2 w\n1 0 Xd\n".to_string(), "0 0 1 0 k\n1 1 Xd\n".to_string(), ] { assert_eq!(one(&paint), Some(Err(())), "{paint}");