From f31d91bf1a67de67cb6012c193151cde3b990490 Mon Sep 17 00:00:00 2001 From: Marco Arnulfo Date: Mon, 12 Oct 2026 23:17:35 +0200 Subject: [PATCH] Layers: a .ai another app wrote opens with its top-level layers closed, as its groups (fixes #1475) --- crates/engine/src/cmd/fileio/load.rs | 5 +++ .../engine/src/cmd/fileio/tests_aiimport.rs | 40 +++++++++++++++++++ crates/engine/src/lib.rs | 2 +- 3 files changed, 46 insertions(+), 1 deletion(-) diff --git a/crates/engine/src/cmd/fileio/load.rs b/crates/engine/src/cmd/fileio/load.rs index 58200162c..3834aa7e1 100644 --- a/crates/engine/src/cmd/fileio/load.rs +++ b/crates/engine/src/cmd/fileio/load.rs @@ -377,6 +377,11 @@ fn open_loaded(s: &mut Session, loaded: Loaded, links: Resolved, path: Option (Vec, Vec) { + let st = s.doc().unwrap(); + let (mut rows, mut open) = (vec![], vec![]); + st.doc.walk(|n| { + if n.children().is_some() { + rows.push(n.name.clone().unwrap_or_default()); + if st.layers_open.contains(n.id) { + open.push(n.name.clone().unwrap_or_default()); + } + } + }); + (rows, open) +} + +/// #1475: a `.ai` another app wrote keeps no open rows VectorCraft reads, so it opens with every +/// row closed, its top-level layers as its groups; a `.ai` VectorCraft wrote, and other formats, +/// open as before (with their top-level layers open unless the file says otherwise). +#[test] +fn an_illustrator_ai_file_opens_with_its_layers_closed() { + let mut s = Session::new(); + for file in [ai::ai(&compressed(&ai::sample_data()), ai::page_pdf()), ai::eps(&ai::sample_data(), ai::page_ps())] { + let r = open(&mut s, "art.ai", &file, json!({})).unwrap(); + assert_eq!((&r["format"], &r["restored"]), (&json!("ai"), &json!(false)), "{r}"); + let (rows, open) = open_rows(&s); + assert!(rows.contains(&"Art".into()) && rows.contains(&"Pair".into()) && rows.contains(&"Sub".into()), "{rows:?}"); + assert_eq!(open, Vec::::new(), "{r}"); + } + // The same art as an EPS: its top-level layers open. + open(&mut s, "art.eps", &ai::eps(&ai::sample_data(), ai::page_ps()), json!({})).unwrap(); + assert_eq!(open_rows(&s).1, ["Art", "Hidden"]); + // Saved as .ai by VectorCraft, it opens with the rows it was saved with. + let saved = s.execute("document.save", &json!({"format": "ai"})).unwrap(); + let bytes = vectorcraft_format::base64_decode(saved["dataBase64"].as_str().unwrap()).unwrap(); + let r = open(&mut s, "again.ai", &bytes, json!({})).unwrap(); + assert_eq!(r["restored"], json!(true), "{r}"); + assert_eq!(open_rows(&s).1, ["Art", "Hidden"]); +} diff --git a/crates/engine/src/lib.rs b/crates/engine/src/lib.rs index 21131638c..a16fc502e 100644 --- a/crates/engine/src/lib.rs +++ b/crates/engine/src/lib.rs @@ -345,7 +345,7 @@ static NEXT_DOC_UID: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64 /// The rows open in the Layers panel: the layers, sublayers and groups that show what they hold. /// A document opens with the ones it was saved with ([`Document::layers_open`]), else with only -/// its top-level layers open. +/// its top-level layers open (a `.ai` another app wrote opens with none open). #[derive(Clone, Debug, Default)] pub struct OpenRows { ids: std::collections::HashSet,