From 77991abe6fec2b28f762d608cfb3f874799d9b83 Mon Sep 17 00:00:00 2001 From: "Dylan M. Taylor" Date: Sat, 14 Nov 2026 00:40:59 +0000 Subject: [PATCH] fix(file): preserve legacy JSON on INDD paths --- Cargo.lock | 2 +- Cargo.toml | 2 +- crates/engine/src/cmd/file/open.rs | 38 +++++++++++++++++++++++------- 3 files changed, 32 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 604139ec7..456f278ce 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1085,7 +1085,7 @@ source = "git+https://github.com/storytold/craft-libs?rev=ba24fe2435adb3c81ad605 [[package]] name = "craft-csv" version = "0.1.0" -source = "git+https://github.com/storytold/craft-libs?rev=69e457a8eb0fcdc824434ae12d67ab7d68a240ae#69e457a8eb0fcdc824434ae12d67ab7d68a240ae" +source = "git+https://github.com/storytold/craft-libs?rev=387c64892b30fdad491b263cc3d9eac660d88dcc#387c64892b30fdad491b263cc3d9eac660d88dcc" dependencies = [ "thiserror 2.0.21", ] diff --git a/Cargo.toml b/Cargo.toml index e763bd089..0e8173fc9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,7 +25,7 @@ craft-rtf = { git = "https://github.com/storytold/craft-libs", rev = "ba24fe2435 craft-xlsx = { git = "https://github.com/storytold/craft-libs", rev = "2b9a325f57624804ed423aded16695eddb815119", version = "=0.1.0" } craft-docx = { git = "https://github.com/storytold/craft-libs", rev = "ba24fe2435adb3c81ad6057faf36365fbba736cf", version = "=0.1.0" } craft-wordmodel = { git = "https://github.com/storytold/craft-libs", rev = "ba24fe2435adb3c81ad6057faf36365fbba736cf", version = "=0.1.0" } -craft-csv = { git = "https://github.com/storytold/craft-libs", rev = "69e457a8eb0fcdc824434ae12d67ab7d68a240ae", version = "=0.1.0" } +craft-csv = { git = "https://github.com/storytold/craft-libs", rev = "387c64892b30fdad491b263cc3d9eac660d88dcc", version = "=0.1.0" } craft-image-codecs = { git = "https://github.com/dylanmtaylor/craft-libs", rev = "2e670cf9377b523112f3fe4b409399772c745895", version = "=0.1.0" } craft-psd = { git = "https://github.com/dylanmtaylor/craft-libs", rev = "2e670cf9377b523112f3fe4b409399772c745895", version = "=0.1.0" } craft-file-formats = { git = "https://github.com/storytold/craft-libs", rev = "eed3c28cf016723d004d6f2c79c61424c15d4558", version = "=0.1.0" } diff --git a/crates/engine/src/cmd/file/open.rs b/crates/engine/src/cmd/file/open.rs index e41a44135..9cb94f5ab 100644 --- a/crates/engine/src/cmd/file/open.rs +++ b/crates/engine/src/cmd/file/open.rs @@ -85,14 +85,21 @@ fn open_path(s: &mut Session, p: &Value) -> Result { // The content decides first: a file's extension may be missing or wrong. The extension // then routes files whose content isn't recognised to the reader that explains why. let format = craft_file_formats::detect(path, &bytes); - let native = matches!(format, Some(craft_file_formats::Format::DesignCraft | craft_file_formats::Format::DesignCraftJson)); - if format == Some(craft_file_formats::Format::Idml) || (!native && format == Some(craft_file_formats::Format::Indd)) { + let native = matches!( + craft_file_formats::detect("", &bytes), + Some(craft_file_formats::Format::DesignCraft | craft_file_formats::Format::DesignCraftJson) + ); + let legacy = (format == Some(craft_file_formats::Format::Indd) && !native).then(|| load_bytes(&bytes).ok()).flatten(); + if format == Some(craft_file_formats::Format::Idml) || (!native && format == Some(craft_file_formats::Format::Indd) && legacy.is_none()) { return crate::cmd::interchange::open_idml(s, p); } - if !native { + if !native && legacy.is_none() { return Err(bad("file.open", format!("{path}: not a DesignCraft or IDML file"))); } - let (mut d, mut warnings) = load_bytes(&bytes)?; + let (mut d, mut warnings) = match legacy { + Some(loaded) => loaded, + None => load_bytes(&bytes)?, + }; crate::cmd::interchange::resolve_packaged_links(&mut d, Some(std::path::Path::new(path))); crate::cmd::datamerge::resolve_sources_on_open(&mut d, Some(std::path::Path::new(path))); let (fonts, faces, font_warnings) = load_document_fonts(&mut d, path); @@ -158,11 +165,16 @@ pub(super) fn file_open_bytes(s: &mut Session, p: &Value) -> Result { // signature (or their name). let name = str_param(p, "name").unwrap_or_default(); let format = craft_file_formats::detect(name, &b); - let native = matches!(format, Some(craft_file_formats::Format::DesignCraft | craft_file_formats::Format::DesignCraftJson)); - if format == Some(craft_file_formats::Format::Idml) || (!native && format == Some(craft_file_formats::Format::Indd)) { + let native = + matches!(craft_file_formats::detect("", &b), Some(craft_file_formats::Format::DesignCraft | craft_file_formats::Format::DesignCraftJson)); + let legacy = (format == Some(craft_file_formats::Format::Indd) && !native).then(|| load_bytes(&b).ok()).flatten(); + if format == Some(craft_file_formats::Format::Idml) || (!native && format == Some(craft_file_formats::Format::Indd) && legacy.is_none()) { return crate::cmd::interchange::open_idml(s, p); } - let (mut d, warnings) = load_bytes(&b)?; + let (mut d, warnings) = match legacy { + Some(loaded) => loaded, + None => load_bytes(&b)?, + }; crate::cmd::datamerge::resolve_sources_on_open(&mut d, None); if let Some(n) = str_param(p, "name") { // Titled as an IDML file opened from bytes is: without its extension. @@ -510,7 +522,17 @@ mod open_bytes_tests { maker.execute("file.new", &json!({})).unwrap(); let idml = designcraft_idml::export_idml(&maker.doc().unwrap().doc); let base64 = super::super::bytes::base64_encode(&idml); - let legacy = super::super::bytes::base64_encode(&serde_json::to_vec(&maker.doc().unwrap().doc).unwrap()); + let legacy_bytes = serde_json::to_vec(&maker.doc().unwrap().doc).unwrap(); + let legacy = super::super::bytes::base64_encode(&legacy_bytes); + #[cfg(not(target_arch = "wasm32"))] + { + let legacy_path = std::env::temp_dir().join(format!("dc-legacy-indd-{}.indd", std::process::id())); + std::fs::write(&legacy_path, &legacy_bytes).unwrap(); + let mut by_path = Session::new(); + by_path.execute("file.open", &json!({"path": legacy_path})).unwrap(); + assert_eq!(by_path.documents().len(), 1, "legacy JSON must win over an InDesign extension"); + let _ = std::fs::remove_file(&legacy_path); + } for name in ["Brochure.indd", "Brochure.INDT"] { let mut s = Session::new(); s.execute("file.openBytes", &json!({"name": name, "base64": legacy})).unwrap();