M4.35: native files compress, save atomically, drop unused images, keep newer keys and save down to v1

- Format v3: optional gzip (Preferences > File Handling > Use Compression, or
  document.save {compress}); compressed files open by their magic bytes, legacy
  ones too.
- Every file write goes to a temporary file renamed over the target (engine
  writes and the desktop app's writer), so a failed save keeps the old file.
- Only the images the document uses are written.
- Document keys from newer versions are kept (Document.extra, flattened).
- Assigned colour profiles are a real field (Document.color_profiles),
  migrated from unknown.colorProfiles on load.
- document.save {version: 1|2} writes files older versions open; {preview}
  embeds a PNG of the first artboard of at most 256 px.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
bflatastic
2026-10-05 12:27:02 +07:00
co-authored by Claude Opus 5.5
parent 7aee3bd487
commit 65cdc45ed9
22 changed files with 1103 additions and 85 deletions
+3 -3
View File
@@ -250,7 +250,7 @@ fn boards<T>(r: std::result::Result<T, String>) -> Result<T> {
r.map_err(|e| bad(C, e))
}
fn options<T: DeserializeOwned + Default>(f: &Format, p: &Value) -> Result<T> {
pub(super) fn options<T: DeserializeOwned + Default>(f: &Format, p: &Value) -> Result<T> {
if !p.is_object() {
return Ok(T::default());
}
@@ -303,12 +303,12 @@ pub fn encode_all(doc: &Document, format: &str, p: &Value) -> Result<Encoded> {
return encode_all(&single_artboard(doc, bounds, "Art"), f.id, &q);
}
let bytes = match f.id {
"vectorcraft" => vectorcraft_format::save_file(doc),
"vectorcraft" => super::native::encode(C, f, doc, p)?,
// A native file flagged so opening it starts a new untitled document.
"template" => {
let mut d = doc.clone();
d.template = true;
vectorcraft_format::save_file(&d)
super::native::encode(C, f, &d, p)?
}
"txt" => super::text::encode(doc, p)?,
"svg" | "svgz" => return super::svg::encode(doc, p, f.id == "svgz").map_err(|e| bad(C, e)),