feat(engine): INSERT of a drawing file keeps an attribute definition's field (#2619)

* feat(engine): INSERT of a drawing file keeps an attribute definition's field

The attribute definitions of a drawing file read by INSERT name=file lost their
extension dictionary, so the ACAD_FIELD dictionary and the FIELD objects of an attribute
whose value is a field (the file name in a title block) were gone from the block.
adopt() now copies that object tree to the drawing under new handles, with the FIELD
class and a field list if the drawing has none.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* foreign: index the kept objects once when walking and copying a dictionary tree

A file can keep many objects; a linear lookup per pointer grew with their product.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: andrewbrake <6925387+andrewbrake@users.noreply.github.com>
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
This commit is contained in:
andrewbrake
2026-11-02 05:31:18 -05:00
committed by GitHub
co-authored by Claude Sonnet 5.5 andrewbrake Brandon Thomas
parent b006110dc5
commit fa03bf2a29
3 changed files with 208 additions and 4 deletions
+14 -3
View File
@@ -109,7 +109,7 @@ fn define_from(s: &mut Session, src: &Drawing, name: &str) -> Result<String> {
nb.name = to.clone();
nb.entities = Default::default();
for e in b.entities.iter() {
let e = adopt(&mut d, e, &renames);
let e = adopt(&mut d, src, e, &renames);
nb.entities.push(e);
}
d.blocks.insert(to.clone(), Arc::new(nb));
@@ -118,7 +118,7 @@ fn define_from(s: &mut Session, src: &Drawing, name: &str) -> Result<String> {
b.base = src.header.point("INSBASE").filter(|p| p.x.is_finite() && p.y.is_finite() && p.z.is_finite()).unwrap_or_default();
let mut ents = Vec::new();
for e in src.model.iter() {
let e = adopt(&mut d, e, &renames);
let e = adopt(&mut d, src, e, &renames);
d.ensure_layer(&e.common.layer);
ents.push(e);
}
@@ -183,9 +183,20 @@ fn block_names(d: &Drawing, src: &Drawing, defining: &str) -> BTreeMap<String, S
/// A copy of a source entity for this drawing: a fresh handle, block references renamed, and
/// links to other source objects (associative dimension points) dropped.
fn adopt(d: &mut Drawing, e: &Entity, renames: &BTreeMap<String, String>) -> Entity {
fn adopt(d: &mut Drawing, src: &Drawing, e: &Entity, renames: &BTreeMap<String, String>) -> Entity {
let mut e = e.clone();
let old = e.handle;
e.handle = d.new_handle();
// An attribute definition keeps its field: its extension dictionary and the objects in it.
if matches!(e.kind, EntityKind::AttDef(_))
&& let Some(root) = src.foreign.entities.get(&old).and_then(|x| x.xdict)
{
let tree = src.foreign.tree(root);
let fresh: Vec<Handle> = (0..tree.len() + 2).map(|_| d.new_handle()).collect();
let foreign = Arc::make_mut(&mut d.foreign);
foreign.adopt_tree(&src.foreign, &tree, &fresh, e.handle);
foreign.entities.entry(e.handle).or_default().xdict = fresh.first().copied();
}
match &mut e.kind {
EntityKind::Insert(i) => {
if let Some(n) = renames.get(&i.block) {