AI import: a compound path whose subpaths' group is marked like a look keeps its holes (fixes #980)

This commit is contained in:
Marco Arnulfo
2026-10-17 14:46:20 +07:00
committed by bflatastic
parent 77f7f02acc
commit 9739eeb333
2 changed files with 31 additions and 1 deletions
+16 -1
View File
@@ -870,7 +870,9 @@ impl<'a> Reader<'a> {
let mut painted = None;
let node = match kind {
// A compound path holding a group (its drawn look) is that look: its paths paint apart.
Kind::Compound if kids.iter().any(|k| self.looks.contains(&k.id)) => {
// Some files mark every group so, the plain one holding the subpaths too (#980): one
// paint for all its paths keeps it one compound path, its holes cut.
Kind::Compound if kids.iter().any(|k| self.looks.contains(&k.id) && paints_apart(k)) => {
let (children, clip) = self.clip_first(kids, &clips);
self.new_node(NodeKind::Group { children, clip }, &gs)
}
@@ -2203,6 +2205,19 @@ fn ungrouped(kids: Vec<Node>) -> Vec<Node> {
out
}
/// Whether `look`'s paths don't all paint the same.
fn paints_apart(look: &Node) -> bool {
fn leaves<'a>(n: &'a Node, out: &mut Vec<&'a Node>) {
match &n.kind {
NodeKind::Group { children, .. } => children.iter().for_each(|c| leaves(c, out)),
_ => out.push(n),
}
}
let mut paths = vec![];
leaves(look, &mut paths);
paths.windows(2).any(|w| matches!(w, [a, b] if a.appearance != b.appearance))
}
fn path_rule(n: &Node) -> FillRule {
match &n.kind {
NodeKind::Path { rule, .. } | NodeKind::Compound { rule, .. } => *rule,
+15
View File
@@ -446,6 +446,21 @@ fn a_compound_path_holding_its_drawn_look_paints_its_paths_apart() {
assert_eq!(fills, [Some(Color::rgb(1.0, 0.0, 0.0)), Some(Color::rgb(0.0, 0.0, 1.0))], "{a:#?}");
}
#[test]
fn a_compound_path_of_a_plain_group_stays_one_compound_path() {
// GUARDS: #980. Files write `9 () XW` after every group, the one a compound path keeps its
// subpaths in too: read as a drawn look, the compound became a group of filled paths, its
// holes filled in. Here a ring (outer path, inner one reversed, one paint) and a path beside it.
let ring = format!("{RED}1 D\n{}f\n0 D\n{}f\n", rect_path(0.0, 0.0, 20.0, 20.0), "5 5 m\n15 5 L\n15 15 L\n5 15 L\n5 5 L\n");
let art_ = format!("0 Ae\nu\n0 Ae\n*u\n0 Ae\nu\n{ring}U\n9 () XW\n*U\n{}f\nU\n9 () XW\n", rect_path(8.0, 8.0, 12.0, 12.0));
let a = art(&read_ok(&layer(&art_)).doc);
let NodeKind::Group { children, .. } = &a[0].kind else { panic!("{a:#?}") };
let NodeKind::Compound { children: subpaths, .. } = &children[0].kind else { panic!("a compound path: {:#?}", children[0]) };
assert_eq!(subpaths.len(), 2, "{subpaths:#?}");
assert!(subpaths.iter().all(|p| matches!(p.kind, NodeKind::Path { .. })));
assert_eq!(fill(&children[0]).color(), Some(Color::rgb(1.0, 0.0, 0.0)));
}
#[test]
fn gradients_through_a_skewed_matrix() {
// GUARDS: a sheared `Xm` leaves the matrix's columns oblique. A linear ramp runs along the