mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-13 08:29:14 +00:00
Save: include a PDF in new native files by default
Squashed from #5320 onto main.
This commit is contained in:
@@ -29,6 +29,8 @@ pub struct Loaded {
|
||||
/// A PDF, or a `.ai` or `.ait` that holds a PDF, read through the editor's own copy of its art
|
||||
/// that it carries (its layers), not from its PDF part alone.
|
||||
pub editing_data: bool,
|
||||
/// Whether the native source declared a PDF; other formats carry no native save preference.
|
||||
pub native_pdf_compatible: Option<bool>,
|
||||
}
|
||||
|
||||
impl Loaded {
|
||||
@@ -341,6 +343,7 @@ pub fn load_with(name: &str, bytes: &[u8], opts: &LoadOptions) -> Result<Loaded>
|
||||
let mut converted = false;
|
||||
let mut preview_only = false;
|
||||
let mut editing_data = false;
|
||||
let mut native_pdf_compatible = None;
|
||||
let (mut doc, mut warnings, restored) = match format.id {
|
||||
// EPS, .ps and PostScript .ai: the document our EPS files carry, else the PostScript read.
|
||||
_ if matches!(format.id, "eps" | "ps") || vectorcraft_pdf::is_postscript(bytes) => {
|
||||
@@ -353,6 +356,7 @@ pub fn load_with(name: &str, bytes: &[u8], opts: &LoadOptions) -> Result<Loaded>
|
||||
}
|
||||
"vectorcraft" | "template" => {
|
||||
let (d, info, warnings) = native_file(bytes)?;
|
||||
native_pdf_compatible = Some(info.pdf_compatible);
|
||||
converted = info.is_old() || super::extension(name) == vectorcraft_format::LEGACY_EXTENSION;
|
||||
(d, warnings, false)
|
||||
}
|
||||
@@ -500,7 +504,7 @@ pub fn load_with(name: &str, bytes: &[u8], opts: &LoadOptions) -> Result<Loaded>
|
||||
doc.title = title;
|
||||
}
|
||||
warnings.extend(doc.images.values().filter_map(|b| size_note(&b.bytes)));
|
||||
Ok(Loaded { doc, format, warnings, restored, converted, preview_only, editing_data })
|
||||
Ok(Loaded { doc, format, warnings, restored, converted, preview_only, editing_data, native_pdf_compatible })
|
||||
}
|
||||
|
||||
/// Import SVG text, reading the files its images link to (relative links from `folder`, the SVG's
|
||||
@@ -593,7 +597,7 @@ pub fn open_template(s: &mut Session, name: &str, bytes: &[u8], path: Option<&st
|
||||
/// Make a loaded file the new active document; `links` is what became of its linked files, and
|
||||
/// `opts` are the options it was read with.
|
||||
fn open_loaded(s: &mut Session, loaded: Loaded, links: Resolved, path: Option<String>, opts: &LoadOptions, as_template: bool) -> Result<Value> {
|
||||
let Loaded { mut doc, format, warnings, restored, converted, .. } = loaded.with_grid_prefs(&s.prefs);
|
||||
let Loaded { mut doc, format, warnings, restored, converted, native_pdf_compatible, .. } = loaded.with_grid_prefs(&s.prefs);
|
||||
// EPS now has an ordinary Save path. Its preview, partial-program and damaged-editing
|
||||
// fallbacks must ask before overwriting the richer source they could not fully read.
|
||||
let losses: Vec<String> = warnings
|
||||
@@ -630,6 +634,9 @@ fn open_loaded(s: &mut Session, loaded: Loaded, links: Resolved, path: Option<St
|
||||
let converted = saves_back && converted && s.prefs.append_converted;
|
||||
let index = s.add_document(doc, path);
|
||||
let st = s.doc_mut()?;
|
||||
if let Some(pdf) = native_pdf_compatible {
|
||||
st.save_options.insert("pdfCompatible".into(), json!(pdf));
|
||||
}
|
||||
if saves_back {
|
||||
st.format = format.id;
|
||||
// An SVG that reopened as the full document saves as one again: Save keeps Preserve
|
||||
|
||||
@@ -48,7 +48,7 @@ pub const OPTIONS: &[FormatOption] = &[
|
||||
FormatOption {
|
||||
name: "pdfCompatible",
|
||||
ty: "boolean",
|
||||
default: "false",
|
||||
default: "true",
|
||||
description: "also carry a PDF of every artboard, for apps that read PDF (a larger file)",
|
||||
},
|
||||
];
|
||||
@@ -146,7 +146,7 @@ struct NativeOptions {
|
||||
precision: Option<Value>,
|
||||
include_linked: bool,
|
||||
embed_profiles: Option<bool>,
|
||||
pdf_compatible: bool,
|
||||
pdf_compatible: Option<bool>,
|
||||
}
|
||||
|
||||
/// What [`save_options`] leaves to [`encode`] (rendering and encoding take time).
|
||||
@@ -171,7 +171,9 @@ pub(super) fn save_options(cmd: &str, f: &Format, doc: &Document, p: &Value) ->
|
||||
so.profiles = embedded_profiles(doc);
|
||||
}
|
||||
so.check().map_err(|e| bad(cmd, e.to_string()))?;
|
||||
Ok((so, Extras { preview: o.preview, pdf: o.pdf_compatible }))
|
||||
let pdf = o.pdf_compatible.unwrap_or(true);
|
||||
so.pdf_compatible = Some(pdf);
|
||||
Ok((so, Extras { preview: o.preview, pdf }))
|
||||
}
|
||||
|
||||
/// The `precision` option `v`: a whole number of decimal places, 0 to
|
||||
@@ -213,9 +215,11 @@ pub(super) fn encode_to(cmd: &str, f: &Format, doc: &Document, p: &Value, w: imp
|
||||
if extras.preview {
|
||||
so.preview = preview_png(&drawn)?;
|
||||
}
|
||||
if extras.pdf {
|
||||
// Every artboard as PDF pages (no editing data: the file is the native document).
|
||||
so.pdf = Some(super::pdf::encode(cmd, &drawn, &json!({ "range": "all", "preserveEditing": false }))?.0);
|
||||
if extras.pdf && !drawn.artboards.is_empty() {
|
||||
// Every artboard as PDF pages (no editing data: the file is the native document), dated by
|
||||
// the document's File Info, so the same document and dates give the same bytes.
|
||||
let at = drawn.metadata.modified.or(drawn.metadata.created);
|
||||
so.pdf = Some(super::pdf::encode_at(cmd, &drawn, &json!({ "range": "all", "preserveEditing": false }), at)?.0);
|
||||
}
|
||||
drop(drawn);
|
||||
let native = match full {
|
||||
|
||||
@@ -240,9 +240,13 @@ pub fn expand_preset<'a>(s: &Session, cmd: &str, p: &'a Value) -> Result<Cow<'a,
|
||||
/// time of writing. On the web, which has no clock, the creation date is the modification date too,
|
||||
/// and a document without one gets neither.
|
||||
pub fn options(cmd: &str, doc: &Document, p: &Value) -> Result<PdfOptions> {
|
||||
options_at(cmd, doc, p, vectorcraft_doc::metadata::output_time())
|
||||
}
|
||||
|
||||
/// [`options`] modified at `now` (Unix seconds) instead of the time of writing.
|
||||
fn options_at(cmd: &str, doc: &Document, p: &Value, now: Option<i64>) -> Result<PdfOptions> {
|
||||
let pick = if p.is_object() { ArtboardPick::deserialize(p).map_err(|e| bad(cmd, format!("PDF options: {e}")))? } else { ArtboardPick::default() };
|
||||
let artboards = pick.resolve(doc.artboards.len()).map_err(|e| bad(cmd, e))?;
|
||||
let now = vectorcraft_doc::metadata::output_time();
|
||||
Ok(PdfOptions {
|
||||
settings: settings(cmd, p)?,
|
||||
artboards,
|
||||
@@ -265,9 +269,14 @@ pub const BLEED_LEFT_OUT: &str = "the document sets a bleed that this PDF/X file
|
||||
/// to images at the document's raster effects resolution. Preserve Editing embeds `doc` itself
|
||||
/// when the PDF has every artboard (a PDF of some reopens as just those).
|
||||
pub fn encode(cmd: &str, doc: &Document, p: &Value) -> Result<(Vec<u8>, Vec<String>)> {
|
||||
encode_at(cmd, doc, p, None)
|
||||
}
|
||||
|
||||
/// [`encode`] modified at `modified` (Unix seconds) when given, instead of the time of writing.
|
||||
pub(super) fn encode_at(cmd: &str, doc: &Document, p: &Value, modified: Option<i64>) -> Result<(Vec<u8>, Vec<String>)> {
|
||||
// Keep the editable object tree independent of preview fallbacks used by the drawn pages.
|
||||
let (full, mut warnings) = crate::cmd::place::document::full_documents(doc);
|
||||
let (bytes, drawn_warnings) = encode_carrying(cmd, &full, p, || Ok(vectorcraft_format::save(doc, false)), || Ok(None))?;
|
||||
let (bytes, drawn_warnings) = encode_carrying_at(cmd, &full, p, modified, || Ok(vectorcraft_format::save(doc, false)), || Ok(None))?;
|
||||
warnings.extend(drawn_warnings);
|
||||
warnings.extend(overflow_warning(cmd, &full, p)?);
|
||||
Ok((bytes, warnings))
|
||||
@@ -294,7 +303,20 @@ pub(super) fn encode_carrying(
|
||||
native: impl FnOnce() -> Result<Vec<u8>>,
|
||||
private: impl FnOnce() -> Result<Option<(vectorcraft_pdf::PrivateData, Vec<String>)>>,
|
||||
) -> Result<(Vec<u8>, Vec<String>)> {
|
||||
let mut opts = options(cmd, doc, p)?;
|
||||
encode_carrying_at(cmd, doc, p, None, native, private)
|
||||
}
|
||||
|
||||
/// [`encode_carrying`] modified at `modified` (Unix seconds) when given, instead of the time of
|
||||
/// writing.
|
||||
fn encode_carrying_at(
|
||||
cmd: &str,
|
||||
doc: &Document,
|
||||
p: &Value,
|
||||
modified: Option<i64>,
|
||||
native: impl FnOnce() -> Result<Vec<u8>>,
|
||||
private: impl FnOnce() -> Result<Option<(vectorcraft_pdf::PrivateData, Vec<String>)>>,
|
||||
) -> Result<(Vec<u8>, Vec<String>)> {
|
||||
let mut opts = options_at(cmd, doc, p, modified.or_else(vectorcraft_doc::metadata::output_time))?;
|
||||
// The pages' background colors, behind the art (the editing data keeps them as colors).
|
||||
let pages = opts.artboards.clone().unwrap_or_else(|| (0..doc.artboards.len()).collect());
|
||||
let filled = super::export::with_artboard_fills(doc, p, pages).map_err(|e| bad(cmd, e))?;
|
||||
|
||||
@@ -270,7 +270,18 @@ pub fn save_plan(s: &Session, mode: SaveMode, p: &Value) -> Result<SavePlan> {
|
||||
}
|
||||
}
|
||||
// None given: the ones the document was last saved with in this format.
|
||||
let options = if given.is_empty() && format.id == st.format { remembered_save_options(st) } else { given };
|
||||
let mut options = if given.is_empty() && format.id == st.format { remembered_save_options(st) } else { given };
|
||||
// Remember the effective choice, including the encoder's default, so another document's
|
||||
// later save cannot change what this one writes. Invalid non-null values still reach validation.
|
||||
if matches!(format.id, "vectorcraft" | "template") {
|
||||
let pdf = match options.get("pdfCompatible") {
|
||||
None | Some(Value::Null) => Some(native_pdf_preference(s, Some(st)).unwrap_or(true)),
|
||||
_ => None,
|
||||
};
|
||||
if let Some(pdf) = pdf {
|
||||
options.insert("pdfCompatible".into(), json!(pdf));
|
||||
}
|
||||
}
|
||||
// Save writes the document's own file; the other modes only where they are told to.
|
||||
let path =
|
||||
path.or_else(|| (mode == SaveMode::Save && !st.converted && !st.recovered && format.id == st.format).then(|| st.path.clone()).flatten());
|
||||
@@ -336,6 +347,15 @@ fn same_value(a: &Value, b: &Value) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
/// Native documents and templates share only their PDF choice. Other source formats use the
|
||||
/// application's last native choice, keeping an AI file's separate PDF option out of it.
|
||||
fn native_pdf_preference(s: &Session, st: Option<&DocState>) -> Option<bool> {
|
||||
st.filter(|d| matches!(d.format, "vectorcraft" | "template"))
|
||||
.and_then(|d| d.save_options.get("pdfCompatible"))
|
||||
.and_then(Value::as_bool)
|
||||
.or_else(|| s.prefs.native_save_options.get("pdfCompatible").and_then(Value::as_bool))
|
||||
}
|
||||
|
||||
/// The options `st` was last saved with, their artboards (`artboard`, `artboards` and `range`)
|
||||
/// following the artboards they named ([`DocState::save_artboards`]): an artboard deleted since is
|
||||
/// left out, and one that moved is named by its new number. When none of the artboards the save
|
||||
@@ -1028,12 +1048,15 @@ fn can_revert(s: &Session) -> std::result::Result<(), String> {
|
||||
fn revert(s: &mut Session, _: &Value) -> Result<Value> {
|
||||
let index = s.active_index().ok_or(crate::EngineError::NoDocument)?;
|
||||
let path = s.doc()?.path.clone().ok_or_else(|| bad("file.revert", "the document has never been saved"))?;
|
||||
let Loaded { mut doc, .. } = load(&path, &read_file(&path)?)?.with_grid_prefs(&s.prefs);
|
||||
let Loaded { mut doc, native_pdf_compatible, .. } = load(&path, &read_file(&path)?)?.with_grid_prefs(&s.prefs);
|
||||
doc.template = false;
|
||||
// Back to the saved file: its recovery copy has nothing left to recover.
|
||||
let uid = s.doc()?.uid;
|
||||
crate::cmd::recovery::forget(s, uid);
|
||||
s.replace_document(index, doc);
|
||||
if let Some(pdf) = native_pdf_compatible {
|
||||
s.doc_mut()?.save_options.insert("pdfCompatible".into(), json!(pdf));
|
||||
}
|
||||
Ok(json!({ "path": path }))
|
||||
}
|
||||
|
||||
@@ -1053,11 +1076,17 @@ fn format_options(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
.or_else(|| (f.id == "vectorcraft" && !s.prefs.native_save_options.is_empty()).then(|| s.prefs.native_save_options.clone()));
|
||||
// A native save compresses as Use Compression says, unless told otherwise.
|
||||
let prefs = with_compression_pref(&s.prefs, &json!({}));
|
||||
let native_pdf = matches!(f.id, "vectorcraft" | "template").then(|| native_pdf_preference(s, st)).flatten();
|
||||
let mut v = f.to_json();
|
||||
if let Some(options) = v["options"].as_object_mut() {
|
||||
for (name, o) in options.iter_mut() {
|
||||
let default = prefs.get(name).filter(|_| matches!(f.id, "vectorcraft" | "template")).unwrap_or(&o["default"]);
|
||||
o["value"] = saved.as_ref().and_then(|m| m.get(name)).unwrap_or(default).clone();
|
||||
if name == "pdfCompatible"
|
||||
&& let Some(pdf) = native_pdf
|
||||
{
|
||||
o["value"] = json!(pdf);
|
||||
}
|
||||
}
|
||||
}
|
||||
v["saveFormats"] =
|
||||
@@ -1072,7 +1101,7 @@ pub(super) fn specs() -> Vec<CommandSpec> {
|
||||
"Save Document",
|
||||
[],
|
||||
None,
|
||||
"{path?, format?: vectorcraft|template|pdf|svg|svgz|ai|eps (default: the path's extension, else the document's own format), options?: {…the format's options, see file.formatOptions; default: as last saved}, svg?: {…SVG options} (an SVG save keeps hidden layers, display:none, unless hiddenLayers is false), …the SVG and PDF options given flat instead (an option given flat or in svg and in options with different values is an error), compress?: bool (native, also in options: gzip; default: the useCompression preference; warnings say when the format doesn't read compress, version, preview or precision given flat, or a key given in options), version?: 4 (3, 2 or 1: for older VectorCraft versions; 1 and 2 are never compressed), preview?: false (embed a PNG of the first artboard, at most 256 px), precision?: 0..9 (native: round path anchors and handles to this many decimal places, a smaller file; colors, transforms and type stay exact; default: exact, and a later save without it writes exactly again), separateArtboards?: false (native and .ai: also save each artboard of `range` to <name>-<artboard>.<ext> beside the file, holding that artboard and the art touching it; the result's files lists every file written), range?: \"1-3, 5\"|\"all\" (default; with separateArtboards), includeLinked?: false (keep linked files' own pixels, not just their previews; they stay linked), embedProfiles?: true (carry the ICC profiles loaded from files that the document is tagged with; opening the file installs them where missing), pdfCompatible?: false for native (also carry a PDF of every artboard) | true for .ai (false: blank pages, only the native document; a .ai file's compress, default true, is its compression.compressText), modified?: Unix seconds|null (the File Info modified date, and created date when there is none, a save to a file stamps; default now, recorded in the journal so a replay matches; null: leave the dates)} → {path, format, bytes, warnings, files?: [path…] (the file and its artboards' files), linked?: [path…] (images an SVG links to)}. Save writes one artboard, except a .ai file: a PDF-compatible file of every artboard carrying the native document (preserveEditing always on; PDF options flat or in options), which document.open restores exactly, on the active artboard (the app's, else the one the document opened on; with editingData: false, the first); separateArtboards writes more files, one per artboard. EPS saves the chosen artboard (first by default), retaining its page margins and the whole editable document; its PostScript appearance flattens transparency, as EPS requires. Without a path it writes the document's own file in its own format: a document opened from or saved as SVG/PDF/EPS saves as that again (warnings name what the format loses). No path known (never saved, converted from an older version, restored by Data Recovery, or another format) → {dataBase64, format, name, folder?, warnings} and the document stays modified",
|
||||
"{path?, format?: vectorcraft|template|pdf|svg|svgz|ai|eps (default: the path's extension, else the document's own format), options?: {…the format's options, see file.formatOptions; default: as last saved}, svg?: {…SVG options} (an SVG save keeps hidden layers, display:none, unless hiddenLayers is false), …the SVG and PDF options given flat instead (an option given flat or in svg and in options with different values is an error), compress?: bool (native, also in options: gzip; default: the useCompression preference; warnings say when the format doesn't read compress, version, preview or precision given flat, or a key given in options), version?: 4 (3, 2 or 1: for older VectorCraft versions; 1 and 2 are never compressed), preview?: false (embed a PNG of the first artboard, at most 256 px), precision?: 0..9 (native: round path anchors and handles to this many decimal places, a smaller file; colors, transforms and type stay exact; default: exact, and a later save without it writes exactly again), separateArtboards?: false (native and .ai: also save each artboard of `range` to <name>-<artboard>.<ext> beside the file, holding that artboard and the art touching it; the result's files lists every file written), range?: \"1-3, 5\"|\"all\" (default; with separateArtboards), includeLinked?: false (keep linked files' own pixels, not just their previews; they stay linked), embedProfiles?: true (carry the ICC profiles loaded from files that the document is tagged with; opening the file installs them where missing), pdfCompatible?: true (native: also carry a PDF of every artboard; false omits it) | true for .ai (false: blank pages, only the native document; a .ai file's compress, default true, is its compression.compressText), modified?: Unix seconds|null (the File Info modified date, and created date when there is none, a save to a file stamps; default now, recorded in the journal so a replay matches; null: leave the dates)} → {path, format, bytes, warnings, files?: [path…] (the file and its artboards' files), linked?: [path…] (images an SVG links to)}. Save writes one artboard, except a .ai file: a PDF-compatible file of every artboard carrying the native document (preserveEditing always on; PDF options flat or in options), which document.open restores exactly, on the active artboard (the app's, else the one the document opened on; with editingData: false, the first); separateArtboards writes more files, one per artboard. EPS saves the chosen artboard (first by default), retaining its page margins and the whole editable document; its PostScript appearance flattens transparency, as EPS requires. Without a path it writes the document's own file in its own format: a document opened from or saved as SVG/PDF/EPS saves as that again (warnings name what the format loses). No path known (never saved, converted from an older version, restored by Data Recovery, or another format) → {dataBase64, format, name, folder?, warnings} and the document stays modified",
|
||||
has_doc,
|
||||
|s, p| save(s, SaveMode::Save, p)
|
||||
),
|
||||
|
||||
@@ -680,7 +680,9 @@ fn a_native_save_streams_the_bytes_the_encoder_makes() {
|
||||
s.execute("shape.rectangle", &json!({"x": 10, "y": 10, "width": 50, "height": 20})).unwrap();
|
||||
for (name, compress) in [("a.vectorcraft", false), ("b.vectorcraft", true), ("c.vctemplate", true)] {
|
||||
let path = d.join(name).to_string_lossy().to_string();
|
||||
let p = json!({"path": path, "compress": compress});
|
||||
// A PDF-compatible file's PDF is dated to the second it is written, so the byte comparison
|
||||
// uses files without one.
|
||||
let p = json!({"path": path, "compress": compress, "pdfCompatible": false});
|
||||
let r = s.execute("file.saveCopy", &p).unwrap();
|
||||
let written = std::fs::read(&path).unwrap();
|
||||
assert_eq!(r["bytes"], written.len(), "{r}");
|
||||
@@ -690,6 +692,17 @@ fn a_native_save_streams_the_bytes_the_encoder_makes() {
|
||||
assert_eq!(written, job.encode().unwrap().files[0].1, "{name}");
|
||||
assert_eq!(vectorcraft_format::load(&written).unwrap().template, name.ends_with("vctemplate"));
|
||||
}
|
||||
// A save with the default options streams a file that carries its PDF.
|
||||
let path = d.join("d.vectorcraft").to_string_lossy().to_string();
|
||||
let p = json!({"path": path});
|
||||
let r = s.execute("file.saveCopy", &p).unwrap();
|
||||
let written = std::fs::read(&path).unwrap();
|
||||
assert_eq!(r["bytes"], written.len(), "{r}");
|
||||
assert!(vectorcraft_format::pdf_content(&written).is_some());
|
||||
let plan = save_plan(&s, SaveMode::Copy, &p).unwrap();
|
||||
let job = save_job(&mut s, plan).unwrap();
|
||||
assert!(job.streams());
|
||||
assert!(vectorcraft_format::pdf_content(&job.encode().unwrap().files[0].1).is_some());
|
||||
// A writer that fails: the save fails with its error.
|
||||
let plan = save_plan(&s, SaveMode::Copy, &json!({"path": "/x/y.vectorcraft"})).unwrap();
|
||||
let job = save_job(&mut s, plan).unwrap();
|
||||
|
||||
@@ -224,7 +224,11 @@ fn save_writes_svg_and_remembers_its_options() {
|
||||
let native = dir.join("doc.vectorcraft").to_string_lossy().to_string();
|
||||
s.execute("document.save", &json!({"path": native})).unwrap();
|
||||
assert!(vectorcraft_format::sniff(&std::fs::read(&native).unwrap()));
|
||||
assert!(s.doc().unwrap().save_options.is_empty(), "a native save remembers no options");
|
||||
assert_eq!(
|
||||
Value::Object(s.doc().unwrap().save_options.clone()),
|
||||
json!({"pdfCompatible":true}),
|
||||
"a native save retains its PDF choice without SVG options"
|
||||
);
|
||||
let _ = std::fs::remove_dir_all(dir);
|
||||
}
|
||||
|
||||
|
||||
@@ -565,6 +565,10 @@ struct Meta {
|
||||
path: Option<String>,
|
||||
/// The format Save writes for it ([`SAVE_FORMATS`]).
|
||||
format: String,
|
||||
/// A document's own native PDF choice before the recovery copy omits its PDF. None keeps
|
||||
/// inheritance from the current application preference. Older copies did not record a choice.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
native_pdf_compatible: Option<bool>,
|
||||
/// When the copy was written (Unix seconds; none without a clock).
|
||||
saved: Option<i64>,
|
||||
/// The file it was read from when that import left things out of the document, and what
|
||||
@@ -821,7 +825,7 @@ pub fn jobs(s: &mut Session, store: &Arc<dyn RecoveryStore>) -> Result<(Vec<Reco
|
||||
mode: SaveMode::Copy,
|
||||
path: st.path.clone(),
|
||||
format: native,
|
||||
options: Default::default(),
|
||||
options: serde_json::Map::from_iter([("pdfCompatible".into(), json!(false))]),
|
||||
name: format!("{file}{DOC_EXT}"),
|
||||
folder: None,
|
||||
modified: None,
|
||||
@@ -832,6 +836,11 @@ pub fn jobs(s: &mut Session, store: &Arc<dyn RecoveryStore>) -> Result<(Vec<Reco
|
||||
title: title.clone(),
|
||||
path: st.path.clone(),
|
||||
format: st.format.to_string(),
|
||||
native_pdf_compatible: if matches!(st.format, "vectorcraft" | "template") {
|
||||
st.save_options.get("pdfCompatible").and_then(Value::as_bool)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
saved,
|
||||
imported_from: st.imported_from.clone(),
|
||||
import_losses: st.import_losses.clone(),
|
||||
@@ -1060,6 +1069,9 @@ fn restore_one(s: &mut Session, store: &Arc<dyn RecoveryStore>, file: &str) -> R
|
||||
st.imported_from = meta.imported_from.clone();
|
||||
st.import_losses = meta.import_losses.clone();
|
||||
st.format = SAVE_FORMATS.iter().copied().find(|f| *f == meta.format).unwrap_or("vectorcraft");
|
||||
if let Some(pdf) = meta.native_pdf_compatible {
|
||||
st.save_options.insert("pdfCompatible".into(), json!(pdf));
|
||||
}
|
||||
st.mark_unsaved();
|
||||
// The copy stays until the document is saved or closed.
|
||||
st.recovery = Some(RecoveryCopy { file: mine.clone(), doc: st.doc.clone() });
|
||||
|
||||
Reference in New Issue
Block a user