- document.save / Save As / Save a Copy to a native or .ai file take separateArtboards + range (also write <name>-<artboard>.<ext> per artboard beside the master file, holding that artboard and the art touching it; the result lists every file), includeLinked (linked images keep their files' own pixels), embedProfiles (ICC profiles loaded from files that the document is tagged with; opening installs them where missing) and pdfCompatible (native: also carry a PDF of every artboard; .ai: false writes blank pages around the native document). .ai files also take compress (compression.compressText). - File > Save As to a native or .ai file shows the options dialog (VectorCraft Options) after the save panel: Save each artboard to a separate file (All or a range), Include Linked Files, Embed ICC Profiles, Create PDF-Compatible File, Use Compression, Preview; the web's Save As dialog shows them too. Save, Save a Copy and Save as Template reuse the options as last saved. - Format: optional "profiles" and "pdf" members (load_file, pdf_content), SaveOptions.include_linked/profiles/pdf; files without them are unchanged. - Changed test: tests_aisave::save_as_ai_writes_a_pdf_that_reopens_editable now confirms the options dialog Save As .ai shows before writing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
103 lines
4.8 KiB
Rust
103 lines
4.8 KiB
Rust
//! VectorCraft Options after Save As: each artboard to a separate file (All or a range), Include
|
|
//! Linked Files, Embed ICC Profiles, Create PDF-Compatible File, Use Compression and Preview; the
|
|
//! web's Save As dialog shows them too.
|
|
|
|
use serde_json::{Value, json};
|
|
|
|
use crate::tests_svg::app;
|
|
use crate::{dialogs, theme};
|
|
|
|
/// One headless frame of the dialog layer.
|
|
fn frame(app: &mut crate::VectorcraftApp) {
|
|
let ctx = egui::Context::default();
|
|
theme::install_fonts(&ctx);
|
|
let mut out = ctx.run_ui(egui::RawInput::default(), |ui| dialogs::show(app, ui.ctx()));
|
|
out.textures_delta.clear();
|
|
}
|
|
|
|
/// Add artboards B and C, a rectangle on each.
|
|
fn three_boards(app: &mut crate::VectorcraftApp) {
|
|
for (x, name) in [(200, "B"), (400, "C")] {
|
|
app.run("artboard.new", json!({"x": x, "y": 0, "width": 100, "height": 80, "name": name})).unwrap();
|
|
app.run("shape.rectangle", json!({"x": x + 10, "y": 10, "width": 20, "height": 20})).unwrap();
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn save_as_native_asks_for_its_options_and_writes_every_artboard() {
|
|
let (mut app, written) = app("/tmp/doc.vectorcraft");
|
|
three_boards(&mut app);
|
|
let r = app.run("file.saveAs", Value::Null).unwrap();
|
|
assert_eq!(r["pending"], "saveOptions");
|
|
let d = app.ui.dialog.as_ref().unwrap();
|
|
assert_eq!(
|
|
(d.str("format").as_str(), d.bool("separateArtboards"), d.bool("embedProfiles"), d.bool("pdfCompatible")),
|
|
("vectorcraft", false, true, false)
|
|
);
|
|
assert!(written.borrow().is_empty(), "nothing is written before OK");
|
|
frame(&mut app);
|
|
assert!(app.ui.dialog.is_some(), "drawing keeps it open");
|
|
let d = app.ui.dialog.as_mut().unwrap();
|
|
d.fields.insert("separateArtboards".into(), json!(true));
|
|
d.fields.insert("all".into(), json!(false));
|
|
d.fields.insert("range".into(), json!("9"));
|
|
frame(&mut app);
|
|
assert!(dialogs::confirm(&mut app).is_err(), "a bad range keeps the dialog open");
|
|
assert!(app.ui.dialog.is_some());
|
|
app.ui.dialog.as_mut().unwrap().fields.insert("range".into(), json!("2-3"));
|
|
let r = dialogs::confirm(&mut app).unwrap();
|
|
let names: Vec<String> = written.borrow().iter().map(|(p, _)| p.clone()).collect();
|
|
assert_eq!(names, ["/tmp/doc.vectorcraft", "/tmp/doc-B.vectorcraft", "/tmp/doc-C.vectorcraft"], "{r}");
|
|
let st = app.session.active().unwrap();
|
|
assert_eq!((st.path.as_deref(), st.is_dirty()), (Some("/tmp/doc.vectorcraft"), false));
|
|
// Save writes them again with the same options, without asking.
|
|
app.run("shape.ellipse", json!({"x": 50, "y": 40, "width": 10, "height": 10})).unwrap();
|
|
app.run("file.save", Value::Null).unwrap();
|
|
assert!(app.ui.dialog.is_none());
|
|
assert_eq!(written.borrow().len(), 6);
|
|
// All: every artboard.
|
|
app.run("file.saveAs", Value::Null).unwrap();
|
|
let d = app.ui.dialog.as_mut().unwrap();
|
|
assert!(d.bool("separateArtboards"), "the options as last saved");
|
|
d.fields.insert("all".into(), json!(true));
|
|
dialogs::confirm(&mut app).unwrap();
|
|
assert_eq!(written.borrow().len(), 10);
|
|
}
|
|
|
|
#[test]
|
|
fn save_a_copy_and_save_dont_ask_and_options_reach_the_file() {
|
|
let (mut app, written) = app("/tmp/plain.vectorcraft");
|
|
// Save of an untitled document goes straight to the file, as before.
|
|
app.run("file.save", Value::Null).unwrap();
|
|
assert!(app.ui.dialog.is_none());
|
|
assert_eq!(written.borrow().len(), 1);
|
|
app.run("file.saveAs", Value::Null).unwrap();
|
|
let d = app.ui.dialog.as_mut().unwrap();
|
|
d.fields.insert("pdfCompatible".into(), json!(true));
|
|
d.fields.insert("compress".into(), json!(false));
|
|
dialogs::confirm(&mut app).unwrap();
|
|
let bytes = written.borrow()[1].1.clone();
|
|
assert!(vectorcraft_format::pdf_content(&bytes).is_some_and(|p| p.starts_with(b"%PDF")));
|
|
assert!(!vectorcraft_format::is_compressed(&bytes));
|
|
}
|
|
|
|
#[test]
|
|
fn the_web_save_as_dialog_shows_the_native_options() {
|
|
let written = crate::tests_svg::Written::default();
|
|
let w = written.clone();
|
|
let services = crate::Services {
|
|
download: Some(Box::new(move |name: &str, b: &[u8]| w.borrow_mut().push((name.to_string(), b.to_vec())))),
|
|
..Default::default()
|
|
};
|
|
let mut app = crate::VectorcraftApp::new(vectorcraft_engine::Session::new(), services);
|
|
app.run("file.new", json!({"width": 100, "height": 80})).unwrap();
|
|
three_boards(&mut app);
|
|
app.run("file.saveAs", Value::Null).unwrap();
|
|
frame(&mut app);
|
|
let d = app.ui.dialog.as_mut().unwrap();
|
|
assert!(d.bool("__pick") && d.fields.contains_key("separateArtboards"));
|
|
d.fields.insert("separateArtboards".into(), json!(true));
|
|
dialogs::confirm(&mut app).unwrap();
|
|
let names: Vec<String> = written.borrow().iter().map(|(p, _)| p.clone()).collect();
|
|
assert_eq!(names, ["Untitled-1.vectorcraft", "Untitled-1-Artboard-1.vectorcraft", "Untitled-1-B.vectorcraft", "Untitled-1-C.vectorcraft"]);
|
|
}
|