From c23b956861d6e9040ff546dc9441aaab3c48f9cf Mon Sep 17 00:00:00 2001 From: Hakan Seven Date: Sun, 28 Jun 2026 20:31:30 +0300 Subject: [PATCH] feat(io): default DWG save to 2018; bump acadrust handle-counter fix - Save As now defaults the version dropdown to DWG/DXF 2018 (latest) instead of the loaded file's version; the selected version is written as-is (removed the source-version fallback for version-locked data). - Pin acadrust 55a2caa: allocate_handle respects header.handle_seed as a floor, fixing the corruption where opening a file and saving it silently overwrote existing XRECORD/DICTIONARY objects (handle reuse) and AutoCAD refused the result. Co-Authored-By: Claude Opus 4.8 --- Cargo.lock | 2 +- src/app/update/dialog.rs | 17 +++++-------- src/app/update/file.rs | 54 ++-------------------------------------- 3 files changed, 9 insertions(+), 64 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f757e6419..3907b2580 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -71,7 +71,7 @@ checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618" [[package]] name = "acadrust" version = "0.4.0" -source = "git+https://github.com/HakanSeven12/acadrust?branch=main#0831995f4b219b8ff7e7b1b52bd147760880ff13" +source = "git+https://github.com/HakanSeven12/acadrust?branch=main#55a2caa3c9318876fe61a8836221d0b4d17a2f62" dependencies = [ "ahash 0.8.12", "anyhow", diff --git a/src/app/update/dialog.rs b/src/app/update/dialog.rs index 89df077c8..b17cda167 100644 --- a/src/app/update/dialog.rs +++ b/src/app/update/dialog.rs @@ -23,10 +23,9 @@ use iced::{mouse, Point, Task}; impl OpenCADStudio { pub(in crate::app) fn open_save_dialog_window(&mut self, tab_idx: usize) -> Task { - // Default the format dropdown to the loaded file's version so a - // round-trip preserves it (e.g. an R2004 drawing offers "DWG 2004", - // not "DWG 2018"). The save still falls back to the source version if - // the user picks an incompatible one (dwg_save_version). + // Default the format dropdown to the latest version (DWG/DXF 2018), + // regardless of the loaded file's version. The user can pick any older + // version explicitly; the selected version is written as-is. let is_dxf = self.tabs[tab_idx] .current_path .as_ref() @@ -34,8 +33,8 @@ impl OpenCADStudio { .and_then(|e| e.to_str()) .map(|e| e.eq_ignore_ascii_case("dxf")) .unwrap_or(false); - let doc_version = self.tabs[tab_idx].scene.document.version; - self.save_dialog_format = crate::io::format_for_version(doc_version, is_dxf); + self.save_dialog_format = + crate::io::format_for_version(acadrust::DxfVersion::AC1032, is_dxf); // Pre-fill filename and folder from current path or defaults. if let Some(p) = &self.tabs[tab_idx].current_path.clone() { @@ -258,11 +257,9 @@ pub(super) fn on_ribbon_tool_click(&mut self, tool_id: String, event: ModuleEven } pub(super) fn on_unsaved_picked_save_path_some(&mut self, path: std::path::PathBuf) -> Task { - let (ext, version) = crate::io::parse_save_format(&self.save_dialog_format); + let (_ext, version) = crate::io::parse_save_format(&self.save_dialog_format); match self.pending_close.take() { Some(crate::app::PendingClose::Tab(idx)) => { - // Fall back to source version for version-locked data. - let version = self.dwg_save_version(idx, ext, version); match crate::io::save_as_version( &self.tabs[idx].scene.document, &path, @@ -284,8 +281,6 @@ pub(super) fn on_ribbon_tool_click(&mut self, tool_id: String, event: ModuleEven } Some(crate::app::PendingClose::Quit) => { let i = self.active_tab; - // Fall back to source version for version-locked data. - let version = self.dwg_save_version(i, ext, version); match crate::io::save_as_version( &self.tabs[i].scene.document, &path, diff --git a/src/app/update/file.rs b/src/app/update/file.rs index 84ff81689..048fefde8 100644 --- a/src/app/update/file.rs +++ b/src/app/update/file.rs @@ -518,43 +518,6 @@ pub(super) fn on_open_file(&mut self) -> Task { Task::none() } - /// Resolve the effective DWG save version for `tab`. - /// - /// A drawing that carries data locked to its source DWG version (e.g. - /// AEC/Civil3D objects with no published spec, raw MLEADER/Surface records, - /// EED) can only be written losslessly in that version. When the requested - /// version is in a different encoding family, fall back to the source - /// version so nothing is dropped and the file opens cleanly elsewhere, and - /// note it on the command line. Non-DWG / convertible saves are unchanged. - /// Shared by every save path (Save, Save As, save-before-close). - pub(crate) fn dwg_save_version( - &mut self, - tab: usize, - ext: &str, - requested: acadrust::DxfVersion, - ) -> acadrust::DxfVersion { - if ext != "dwg" { - return requested; - } - let fallback = { - let doc = &self.tabs[tab].scene.document; - match doc.dwg_source_version { - Some(src) if src != requested && doc.has_version_locked_data(requested) => Some(src), - _ => None, - } - }; - match fallback { - Some(src) => { - self.command_line.push_output(&format!( - "Saved in source DWG version ({src:?}) to preserve objects that cannot be \ - converted to the selected version." - )); - src - } - None => requested, - } - } - pub(super) fn on_save_file(&mut self) -> Task { if self.read_only { self.command_line @@ -572,17 +535,8 @@ pub(super) fn on_open_file(&mut self) -> Task { if let Some(path) = self.tabs[i].current_path.clone() { self.tabs[i].scene.document.header.user_real1 = self.tabs[i].scene.annotation_scale as f64; - // Preserve the document's version, but fall back to the - // source version if it carries version-locked data (so a - // direct Save can't silently drop AEC/Civil3D objects). - let ext = path - .extension() - .and_then(|e| e.to_str()) - .unwrap_or("") - .to_ascii_lowercase(); - let requested = self.tabs[i].scene.document.version; - let version = self.dwg_save_version(i, &ext, requested); - match crate::io::save_as_version(&self.tabs[i].scene.document, &path, version) { + // A direct Save preserves the document's current version. + match crate::io::save(&self.tabs[i].scene.document, &path) { Ok(()) => { self.command_line .push_output(&format!("Saved: {}", path.display())); @@ -614,10 +568,6 @@ pub(super) fn on_open_file(&mut self) -> Task { let i = self.active_tab; sync_annotation_scale_header(&mut self.tabs[i].scene); - // Fall back to the source version for files with version-locked - // data (e.g. AEC/Civil3D objects) so nothing is dropped. - let version = self.dwg_save_version(i, ext, version); - // Native: write to the chosen path. Web: download the bytes // under the chosen name (no filesystem). #[cfg(not(target_arch = "wasm32"))]