mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-10 10:02:38 +00:00
Save As offers VectorCraft, VectorCraft Template, PDF, SVG and SVGZ and asks
for the chosen format's options before writing: SVG Options for SVG/SVGZ, the
Save PDF dialog for PDF (its OK finishes the save); on the web a Save As
dialog first names the file and picks the format. A document opened from or
saved as SVG/PDF remembers its format and options in one place
(DocState.format, save_options: the SVG options, or every Save PDF setting),
so Save writes it again; the result's warnings name what the format loses,
then the encoder's notes. Save a Copy writes "<name> copy.<ext>" and leaves
the path, title and modified state alone. Save is off for a clean saved
document, and Save as PDF... is in the File menu.
Engine: document.save, file.saveAs and file.saveCopy take {path?, format?,
options?, svg?} (SVG options also flat) and write linked images beside an
SVG; file.formatOptions reads a format's options. save_plan/save_with are the
one save path of every frontend, on the shared SVG encoder (one SVGZ path).
The template format is writable. Save panels get per-format filters, a folder
and a suggested name.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
58 lines
2.4 KiB
Rust
58 lines
2.4 KiB
Rust
//! SVGZ in the app: Place takes it, and a .svgz name (Export As SVG, Save As) writes it.
|
|
|
|
use std::cell::RefCell;
|
|
use std::rc::Rc;
|
|
|
|
use serde_json::{Value, json};
|
|
use vectorcraft_engine::Session;
|
|
|
|
use crate::{Services, VectorcraftApp, io};
|
|
|
|
pub(super) type Written = Rc<RefCell<Vec<(String, Vec<u8>)>>>;
|
|
|
|
/// An app with a rectangle whose save dialog answers `picked`; the writer records its files.
|
|
pub(super) fn app(picked: &str) -> (VectorcraftApp, Written) {
|
|
let written = Written::default();
|
|
let w = written.clone();
|
|
let picked = picked.to_string();
|
|
let services = Services {
|
|
write: Some(Box::new(move |p: &str, b: &[u8]| {
|
|
w.borrow_mut().push((p.to_string(), b.to_vec()));
|
|
Ok(())
|
|
})),
|
|
pick_save: Some(Box::new(move |_: &crate::FilePick| Some(picked.clone()))),
|
|
..Default::default()
|
|
};
|
|
let mut app = VectorcraftApp::new(Session::new(), services);
|
|
app.run("file.new", json!({"width": 100, "height": 80})).unwrap();
|
|
app.run("shape.rectangle", json!({"x": 10, "y": 10, "width": 40, "height": 30})).unwrap();
|
|
(app, written)
|
|
}
|
|
|
|
#[test]
|
|
fn svgz_places_like_svg() {
|
|
let (mut app, _) = app("/tmp/unused.svg");
|
|
let svg = app.session.execute("document.serialize", &json!({"format": "svg"})).unwrap()["text"].as_str().unwrap().to_string();
|
|
app.run("file.new", json!({"width": 100, "height": 80})).unwrap();
|
|
io::place_bytes(&mut app, "art.svgz", &vectorcraft_svg::compress(&svg)).unwrap();
|
|
assert_eq!(app.session.doc().unwrap().selection.objects.len(), 1, "the rectangle was placed");
|
|
}
|
|
|
|
#[test]
|
|
fn a_svgz_name_writes_compressed_svg() {
|
|
let (mut app, written) = app("/tmp/art.svgz");
|
|
// Export As SVG, named .svgz in the save dialog.
|
|
app.run("file.export.svg", Value::Null).unwrap();
|
|
crate::dialogs::confirm(&mut app).unwrap();
|
|
let (path, bytes) = written.borrow()[0].clone();
|
|
assert_eq!(path, "/tmp/art.svgz");
|
|
assert!(vectorcraft_svg::text_of(&bytes).unwrap().contains("<style>"), "the SVG Options apply");
|
|
assert!(vectorcraft_svg::is_svgz(&bytes));
|
|
// Save As .svgz asks for SVG Options, then saves compressed and keeps the path.
|
|
let r = app.run("file.saveAs", Value::Null).unwrap();
|
|
assert_eq!(r["path"], "/tmp/art.svgz");
|
|
crate::dialogs::confirm(&mut app).unwrap();
|
|
assert!(vectorcraft_svg::is_svgz(&written.borrow()[1].1));
|
|
assert_eq!(app.session.active().unwrap().path.as_deref(), Some("/tmp/art.svgz"));
|
|
}
|