DWG: keep the current layer (CLAYER) through a save and reopen (#2252) (#2407)

* 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)
This commit is contained in:
Books
2026-10-29 21:21:15 -04:00
committed by GitHub
parent c2695f6ab0
commit bcf0dbc5e8
3 changed files with 42 additions and 2 deletions
+12
View File
@@ -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<acadrust::DxfVersion>) -> Result<Vec<u8>, 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}"))
+28
View File
@@ -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}");
}
+2 -2
View File
@@ -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 | % |
|---|---|---|---|