fix(io): sort each space once when a DXF has several SORTENTSTABLEs for it

Every SORTENTSTABLE rebuilt its whole entity store, so many small tables naming one large space made import quadratic. Each space is now sorted once, by the last table naming it, which already decided its order.
This commit is contained in:
Dylan M. Taylor
2026-11-03 11:45:50 +00:00
parent 5ed192555c
commit e29c2f80a4
2 changed files with 33 additions and 1 deletions
+11 -1
View File
@@ -314,7 +314,17 @@ pub(super) fn apply_sortents(
paper_for: &HashMap<String, String>,
first_layout: &str,
) {
for (owner, map) in tables {
// Each sort rebuilds its whole space, so a space named by many tables is sorted once, by the
// last of them (which decided its order anyway); repeated tables made import quadratic.
let target = |owner: &String| {
let up = brs.get(owner)?.to_ascii_uppercase();
Some(if up.starts_with("*PAPER_SPACE") { paper_for.get(&up).cloned().unwrap_or_else(|| first_layout.to_string()) + "\0paper" } else { up })
};
let last: HashMap<String, usize> = tables.iter().enumerate().filter_map(|(i, (owner, _))| Some((target(owner)?, i))).collect();
for (i, (owner, map)) in tables.iter().enumerate() {
if target(owner).and_then(|t| last.get(&t)) != Some(&i) {
continue;
}
let Some(name) = brs.get(owner) else { continue };
let up = name.to_ascii_uppercase();
// A sort handle of 0 draws last (ezdxf `reorder`, which notes AutoCAD defines it, undocumented).
+22
View File
@@ -337,6 +337,28 @@ fn entity_transparency_roundtrips_as_group_440() {
assert_eq!(d.model.iter().map(|e| e.common.transparency).collect::<Vec<_>>(), [Transparency::ByLayer, Transparency::Percent(90)]);
}
/// Several SORTENTSTABLEs for one space sort it once, in the last table's order.
#[test]
fn repeated_sortents_tables_use_the_last() {
let table =
|a: u32, b: u32| format!("0\nSORTENTSTABLE\n5\n{:X}\n100\nAcDbSortentsTable\n330\n1F\n331\nA\n5\n{a:X}\n331\nB\n5\n{b:X}\n", 0x100 + a);
let mut t = String::from(
"0\nSECTION\n2\nTABLES\n0\nTABLE\n2\nBLOCK_RECORD\n0\nBLOCK_RECORD\n5\n1F\n2\n*Model_Space\n0\nENDTAB\n0\nENDSEC\n\
0\nSECTION\n2\nENTITIES\n0\nLINE\n5\nA\n330\n1F\n10\n0\n20\n0\n11\n1\n21\n1\n0\nLINE\n5\nB\n330\n1F\n10\n0\n20\n0\n11\n2\n21\n2\n0\nENDSEC\n\
0\nSECTION\n2\nOBJECTS\n0\nDICTIONARY\n5\nC\n",
);
for _ in 0..1000 {
t.push_str(&table(2, 1));
}
t.push_str(&table(1, 2));
t.push_str("0\nENDSEC\n0\nEOF\n");
let d = read(t.as_bytes(), "s.dxf").unwrap();
assert_eq!(d.model.iter().map(|e| e.handle.0).collect::<Vec<_>>(), [0xA, 0xB]);
let reversed = t.replacen(&table(1, 2), &table(2, 1), 1);
let d = read(reversed.as_bytes(), "s.dxf").unwrap();
assert_eq!(d.model.iter().map(|e| e.handle.0).collect::<Vec<_>>(), [0xB, 0xA]);
}
#[test]
fn hostile_dxf_does_not_panic() {
for t in [