.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.
This commit is contained in:
dotCooCoo
2026-11-12 21:25:12 -08:00
parent ec892d4218
commit a1f6eaed77
2 changed files with 25 additions and 6 deletions
+15 -5
View File
@@ -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,
+10 -1
View File
@@ -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}");