Save note: a DWG loses SAT-text solids from 2013 on, and the note says so (#2334)

A real DWG write and read-back keeps a SAT-text REGION's groups up to 2010 and
returns it without them at 2013 and 2018, so the SAT-text skip in not_saved_as
applies before 2013 only. The DWG note now says solids are kept in a DXF only
when saved as 2010 or earlier.
This commit is contained in:
SBaksa
2026-10-31 12:28:21 +11:00
parent 768d25bb7d
commit 282ad11e60
3 changed files with 35 additions and 11 deletions
+7 -4
View File
@@ -550,10 +550,10 @@ pub fn not_saved_as(d: &Drawing, version: Version) -> std::collections::BTreeMap
let stores = std::iter::once(&d.model).chain(d.layouts.iter().map(|l| &l.entities)).chain(d.blocks.values().map(|b| &b.entities));
for e in stores.flat_map(|s| s.iter()) {
if let cadcraft_doc::EntityKind::Unknown(u) = &e.kind {
// A DWG writer converts SAT text to its own form at any version, and from 2013 a data
// store record keeps the geometry too: neither loses the solid.
// A DWG before 2013 writes a solid's SAT text as it is; from 2013 the geometry has to be
// in the data store, and SAT text alone is lost (#2334).
if MODELER_TYPES.iter().any(|t| u.dxf_type.eq_ignore_ascii_case(t))
&& (stored.contains(&e.handle) || cadcraft_doc::foreign::has_sat_text(&u.tags))
&& (stored.contains(&e.handle) || (version < Version::R2013 && cadcraft_doc::foreign::has_sat_text(&u.tags)))
{
continue;
}
@@ -570,7 +570,10 @@ pub fn not_saved_message(lost: &std::collections::BTreeMap<String, usize>) -> Op
return None;
}
let list: Vec<String> = lost.iter().map(|(t, n)| format!("{n} {t}")).collect();
Some(format!("Not saved in the DWG file (its writer can't write these object types yet; DXF keeps them): {}", list.join(", ")))
Some(format!(
"Not saved in the DWG file (its writer can't write these object types yet; DXF keeps them, solids only when saved as 2010 or earlier): {}",
list.join(", ")
))
}
/// Whether saving to `path` leaves out the entities [`not_saved`] counts: DWG does (the DWG
@@ -29,7 +29,7 @@ fn save_lists_the_entities_the_file_does_not_keep() {
let r = s.execute("saveas", &json!({ "path": path.to_string_lossy() })).unwrap();
let _ = std::fs::remove_file(&path);
assert_eq!(r["notSaved"], json!({ "3DSOLID": 1, "MLINE": 2, "REGION": 1 }));
let msg = "Not saved in the DWG file (its writer can't write these object types yet; DXF keeps them): 1 3DSOLID, 2 MLINE, 1 REGION";
let msg = "Not saved in the DWG file (its writer can't write these object types yet; DXF keeps them, solids only when saved as 2010 or earlier): 1 3DSOLID, 2 MLINE, 1 REGION";
assert!(s.log.iter().any(|l| l == msg), "{:?}", s.log);
// DXF keeps them.
@@ -145,17 +145,17 @@ fn the_note_counts_the_host_drawing_while_a_block_is_edited() {
assert_eq!(r["notSaved"], json!({ "REGION": 1 }));
}
/// A DWG writer converts a solid's SAT text itself (at any version), so a DWG save doesn't list
/// solids that hold it as left out; ones with neither SAT text nor a data store record are (#2334).
/// A DWG before 2013 writes a solid's SAT text as it is, so a save there doesn't list a solid that
/// holds it; from 2013 the geometry must be in the data store and SAT text alone is lost, so the
/// save lists it. A solid with neither is always listed (#2334).
#[test]
fn a_dwg_keeps_solids_that_hold_sat_text() {
fn a_dwg_keeps_sat_text_solids_only_before_2013() {
use cadcraft_engine::cmd::file::{Version, not_saved_as};
let tag = |code: i32, value: &str| RawTag { code, value: value.into() };
let solid = |tags: Vec<RawTag>| EntityKind::Unknown(Unknown { dxf_type: "3DSOLID".into(), tags });
let mut d = Drawing::new_imperial();
d.add(&Space::Model, Common::default(), solid(vec![tag(100, "AcDbModelerGeometry"), tag(1, "400 1 1 0")])).unwrap();
d.add(&Space::Model, Common::default(), solid(vec![tag(100, "AcDbModelerGeometry"), tag(70, "1")])).unwrap();
for v in [Version::R2010, Version::R2018] {
assert_eq!(not_saved_as(&d, v), [("3DSOLID".to_string(), 1)].into_iter().collect(), "{v:?}");
}
assert_eq!(not_saved_as(&d, Version::R2010), [("3DSOLID".to_string(), 1)].into_iter().collect(), "2010 keeps the SAT-text solid");
assert_eq!(not_saved_as(&d, Version::R2018), [("3DSOLID".to_string(), 2)].into_iter().collect(), "2018 loses it");
}
+21
View File
@@ -196,3 +196,24 @@ fn a_data_store_record_stores_only_its_first_entity() {
let store = Foreign { data_store: vec![tag(0, "ACDSRECORD"), tag(320, "2B"), tag(320, "2C"), tag(2, "ASM_Data")], ..Foreign::default() };
assert_eq!(store.data_store_entities(), [Handle(0x2B)].into());
}
/// A real DWG round trip of a SAT-text REGION: the DWG writer keeps the SAT groups up to 2010, and
/// from 2013 they come back empty (the solid's geometry is not in the file), which the DWG save
/// note has to say (#2334).
#[test]
fn a_dwg_keeps_a_sat_text_region_only_before_2013() {
let sat = "0\nSECTION\n2\nHEADER\n9\n$ACADVER\n1\nAC1015\n0\nENDSEC\n0\nSECTION\n2\nENTITIES\n\
0\nREGION\n5\n2B\n100\nAcDbEntity\n8\n0\n100\nAcDbModelerGeometry\n70\n1\n1\n400 1 1 0\n3\nEnd-of-ACIS-data\n0\nENDSEC\n0\nEOF\n";
let d = cadcraft_io::read_dxf(sat.as_bytes()).unwrap();
let holds_sat = |v: cadcraft_io::Version| {
let dwg = cadcraft_io::write_with(&d, "x.dwg", &cadcraft_io::SaveOptions { version: Some(v), binary: false }).unwrap();
let back = cadcraft_io::read(&dwg, "x.dwg").unwrap();
let region = back.model.iter().find_map(|e| match &e.kind {
EntityKind::Unknown(u) if u.dxf_type == "REGION" => Some(u.clone()),
_ => None,
});
region.is_some_and(|u| cadcraft_doc::foreign::has_sat_text(&u.tags))
};
assert!(holds_sat(cadcraft_io::Version::R2010), "2010 keeps the SAT text");
assert!(!holds_sat(cadcraft_io::Version::R2018), "2018 comes back without it");
}