Save: warn when SVG keeps only one of several artboards

A document with more than one artboard saved as SVG/SVGZ without
Preserve Editing Capabilities is written as one artboard and reopens
with one, so the others were lost without a word. The save now says so
(first, since the status bar shows only the first warning) and names the
two ways to keep them all.

Refs storytold/vectorcraft#2091

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Higgs32584
2026-10-20 02:05:01 +07:00
committed by bflatastic
co-authored by Claude Opus 5.5
parent f106f32af4
commit c2a605ce8e
2 changed files with 33 additions and 1 deletions
+16 -1
View File
@@ -322,6 +322,19 @@ fn fidelity_warning(f: &Format) -> Option<String> {
})
}
/// What an SVG save loses of a document with several artboards: the file is one artboard and
/// reopens as one, unless it carries the native document (Preserve Editing Capabilities).
fn artboards_warning(f: &Format, doc: &Document, params: &Value) -> Option<String> {
let n = doc.artboards.len();
let preserved = super::svg::options_map(params).ok().and_then(|m| m.get("preserveEditing").and_then(Value::as_bool)).unwrap_or(false);
(is_svg(f) && n > 1 && !preserved).then(|| {
format!(
"{} keeps 1 of the document's {n} artboards (the first, unless one is chosen) and reopens with only that one: turn on Preserve Editing Capabilities or save as VectorCraft to keep them all",
f.label
)
})
}
/// A save of the active document, snapshotted and ready to encode and write anywhere (Background
/// Save does both on a worker thread): [`save_job`], then [`SaveJob::write`], then
/// [`SaveJob::finish`]. Cheap to clone (the documents are shared).
@@ -549,7 +562,9 @@ impl SaveJob {
let one = self.encode_one(&artboard_doc(&self.doc, b))?;
enc.files.extend(one.files.into_iter().map(|(_, bytes)| (Some(b), bytes)));
}
enc.warnings.splice(0..0, fidelity_warning(self.plan.format));
// Lost artboards first: the status bar shows only the first warning.
let f = self.plan.format;
enc.warnings.splice(0..0, artboards_warning(f, &self.doc, &self.params).into_iter().chain(fidelity_warning(f)));
Ok(enc)
}
+17
View File
@@ -145,6 +145,23 @@ fn preserve_editing_reopens_the_native_document() {
assert_eq!(s.doc().unwrap().doc.artboards.len(), 1);
}
#[test]
fn saving_several_artboards_as_plain_svg_warns_they_are_lost() {
let artboards = |r: &Value| r["warnings"].as_array().unwrap().iter().filter(|w| w.as_str().unwrap().contains("artboards")).count();
let mut s = session(3);
let r = s.execute("document.save", &json!({"format": "svg"})).unwrap();
assert_eq!(artboards(&r), 1, "{r}");
// First: the status bar shows only the first warning.
assert!(r["warnings"][0].as_str().unwrap().contains("1 of the document's 3 artboards"), "{r}");
assert_eq!(artboards(&s.execute("file.saveAs", &json!({"format": "svgz"})).unwrap()), 1);
// Preserve Editing keeps them all (they reopen); a single artboard loses none.
for p in [json!({"format": "svg", "preserveEditing": true}), json!({"format": "svg", "svg": {"preserveEditing": true}})] {
let r = s.execute("document.save", &p).unwrap();
assert_eq!(artboards(&r), 0, "{p}: {r}");
}
assert_eq!(artboards(&session(1).execute("document.save", &json!({"format": "svg"})).unwrap()), 0);
}
#[test]
fn save_writes_svg_and_remembers_its_options() {
let mut s = session(1);