From bcf0dbc5e8bfba025fb46344b826d2b8da9c960d Mon Sep 17 00:00:00 2001 From: Books Date: Fri, 30 Oct 2026 12:21:15 +1100 Subject: [PATCH] DWG: keep the current layer (CLAYER) through a save and reopen (#2252) (#2407) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * DWG: keep the current layer (CLAYER) through a save and reopen (#2252) The DWG header stores the current layer as a handle, and the one read from the DXF needn't be the layer's in the document, so the drawing reopened on layer 0. The bridge now resolves CLAYER by name to the layer's handle before writing, like the current text and dimension styles. Test: the_current_layer_round_trips_through_dwg. * Parity checklist: regenerated after merging main * DWG: resolve $CLAYER through the layer table's Unicode-aware lookup (#2252) eq_ignore_ascii_case missed non-ASCII case differences such as Survey_Ä / Survey_ä, leaving layer 0 current. acadrust's Table::get normalises names with to_uppercase. * DWG: test a current layer named in other letter case (#2252) --- crates/dwg/src/lib.rs | 12 ++++++++++++ crates/io/tests/it/dwg_header_vars.rs | 28 +++++++++++++++++++++++++++ docs/parity-checklist.md | 4 ++-- 3 files changed, 42 insertions(+), 2 deletions(-) diff --git a/crates/dwg/src/lib.rs b/crates/dwg/src/lib.rs index b81bd00d1..8abb696b5 100644 --- a/crates/dwg/src/lib.rs +++ b/crates/dwg/src/lib.rs @@ -512,6 +512,17 @@ mod native { } } + /// The current layer (`$CLAYER`) by its name, the same way as [`current_dimstyle`]: a DWG + /// stores the handle of the layer, and the one read with the DXF needn't be the layer's in + /// the document, so the drawing reopened with layer "0" current (#2252). + fn current_layer(doc: &mut acadrust::CadDocument) { + let name = doc.header.current_layer_name.clone(); + // The table's own lookup compares Unicode case-insensitively ("Survey_Ä" = "Survey_ä"). + if let Some(layer) = doc.layers.get(&name) { + doc.header.current_layer_handle = layer.handle; + } + } + fn convert(dxf: &[u8], version: Option) -> Result, String> { // Reject a pathologically large DXF before handing it to the third-party decoder, which // allocates tables and section buffers from the input's declared sizes and cannot be @@ -543,6 +554,7 @@ mod native { current_dimstyle(&mut doc); add_variable_dictionary(&mut doc); current_text_style(&mut doc); + current_layer(&mut doc); std::panic::catch_unwind(AssertUnwindSafe(|| acadrust::DwgWriter::write_to_vec(&doc))) .map_err(|_| "the DWG writer failed on this drawing".to_string())? .map_err(|e| format!("DWG write: {e}")) diff --git a/crates/io/tests/it/dwg_header_vars.rs b/crates/io/tests/it/dwg_header_vars.rs index d1850278f..1bebee75e 100644 --- a/crates/io/tests/it/dwg_header_vars.rs +++ b/crates/io/tests/it/dwg_header_vars.rs @@ -59,3 +59,31 @@ fn header_settings_round_trip_through_dwg() { let r = cadcraft_io::read(&dwg, "x.dwg").unwrap(); check("DWG", &r); } + +/// The current layer (`$CLAYER`) survives a DWG save and reopen, ASCII or not. A DWG stores a +/// handle to the layer, and the writer used the null handle, so the drawing came back on "0" (#2252). +#[test] +fn the_current_layer_round_trips_through_dwg() { + for name in ["Survey_B", "Survey_\u{c4}"] { + let mut d = Drawing::new_metric(); + d.layers.push(cadcraft_doc::Layer::new(name)); + d.header.set_str("CLAYER", name); + let dxf = cadcraft_io::read_dxf(cadcraft_io::write_dxf(&d).as_bytes()).unwrap(); + assert_eq!(dxf.header.str("CLAYER", "?"), name, "DXF keeps it"); + let dwg = cadcraft_io::write(&d, "x.dwg").unwrap(); + let r = cadcraft_io::read(&dwg, "x.dwg").unwrap(); + assert_eq!(r.header.str("CLAYER", "?"), name, "DWG"); + } +} + +/// `$CLAYER` naming the layer in other letter case still finds it: the DWG comes back on the +/// layer, under the layer's own spelling, not on "0" (#2252). +#[test] +fn a_current_layer_in_other_case_resolves_to_the_layer() { + let mut d = Drawing::new_metric(); + d.layers.push(cadcraft_doc::Layer::new("Survey_\u{c4}")); + d.header.set_str("CLAYER", "Survey_\u{e4}"); + let dwg = cadcraft_io::write(&d, "x.dwg").unwrap(); + let r = cadcraft_io::read(&dwg, "x.dwg").unwrap(); + assert_eq!(r.header.str("CLAYER", "?"), "Survey_\u{c4}"); +} diff --git a/docs/parity-checklist.md b/docs/parity-checklist.md index a6250d5dc..ee0358f9e 100644 --- a/docs/parity-checklist.md +++ b/docs/parity-checklist.md @@ -1,10 +1,10 @@ # Parity checklist -> **Generated:** 2026-10-25 by `cargo xtask parity`. Do not edit by hand. +> **Generated:** 2026-10-28 by `cargo xtask parity`. Do not edit by hand. Menu-breadth parity: a reference menu item counts as covered when a CADCraft command is registered under the same menu label. It measures breadth, not depth; see [target-app-parity.md](target-app-parity.md) for the weighted assessment. -**Menu breadth: 297 / 491 items (60%)** · 442 registered commands. +**Menu breadth: 298 / 491 items (61%)** · 444 registered commands. | Menu | Items | Covered | % | |---|---|---|---|