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 | % | |---|---|---|---|