feat(properties): editable per-object leader dimension overrides

A legacy leader's Lines & Arrows / Text / Fit rows are now editable
per-object overrides of its dimension style, stored in the standard
ACAD_DSTYLE XDATA record (new src/entities/dim_override.rs codec). Arrow
block, arrow size, dim-line lineweight, text offset, vertical text
position and overall scale each prefer an override over the style; the
renderer honours arrow size / block / overall scale and the dim-line
lineweight, so an edited leader redraws at its new arrow and weight.

XDATA edits go through a new dispatch::set_entity_xdata, which registers
the application in the APPID table (the DWG writer drops records for an
unregistered app) and drops that app's stale verbatim EED block (which
otherwise wins over the structured record on a DWG save), so edits and
clears round-trip. The hyperlink editor moves onto this path too.

Dim-line colour stays read-only: it would live in the leader's
override_color, which the file layer doesn't serialise, so making it
editable would silently lose the pick on save. Picking "Closed filled"
writes an explicit null-handle arrow override rather than clearing, so
it sticks even when the style's arrow differs; an unparseable numeric
entry is ignored instead of wiping the stored override.

Note: the DXF entity writer only emits XDATA for hatches, so these
overrides (and hyperlinks) currently persist on DWG save but not DXF —
an acadrust-side gap to close separately.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
Hakan SevenandClaude Opus 4.8 committed 2026-07-13 00:28:42 +03:00
1 parent 984fa79189
commit 7cf1bf014b
6 files changed
+442 -53

No files matched your search

+31 -7
View File
@@ -86,15 +86,33 @@ pub fn apply_common_prop(entity: &mut EntityType, field: &str, value: &str) {
}
/// Replace — or, when `values` is `None`, remove — the single XDATA record for
/// application `app` on an entity's common data, preserving every other record
/// and the verbatim DWG EED blob. `ExtendedData` only appends, so a plain
/// `add_record` would leave a stale duplicate that the reader picks first;
/// rebuilding the collection is the only way to edit or clear a record.
pub fn set_common_xdata(
entity: &mut EntityType,
/// application `app` on the entity `handle`, leaving every other record intact.
///
/// This is document-level rather than entity-level for two DWG-correctness
/// reasons the entity alone can't satisfy:
/// * the writer skips a record whose application is not registered in the
/// APPID table, so a brand-new hyperlink/override would silently vanish —
/// register the app (with a real handle) first;
/// * on a DWG read each application's XDATA is captured verbatim in
/// `raw_dwg_eed`, and that verbatim blob *wins* over a structured record on
/// save. Editing an existing record therefore also has to drop that app's
/// stale verbatim block (matched by APPID handle) so the new value wins.
/// Other applications' verbatim blocks are preserved for round-trip fidelity.
pub fn set_entity_xdata(
doc: &mut acadrust::CadDocument,
handle: acadrust::Handle,
app: &str,
values: Option<Vec<acadrust::xdata::XDataValue>>,
) {
if values.is_some() && !doc.app_ids.contains(app) {
let mut a = acadrust::tables::AppId::new(app);
a.handle = doc.allocate_handle();
let _ = doc.app_ids.add(a);
}
let app_handle = doc.app_ids.get(app).map(|a| a.handle.value());
let Some(entity) = doc.get_entity_mut(handle) else {
return;
};
let common = entity.common_mut();
let mut rebuilt = acadrust::xdata::ExtendedData::new();
for r in common.extended_data.records() {
@@ -109,7 +127,13 @@ pub fn set_common_xdata(
}
rebuilt.add_record(rec);
}
rebuilt.raw_dwg_eed = common.extended_data.raw_dwg_eed.clone();
rebuilt.raw_dwg_eed = common
.extended_data
.raw_dwg_eed
.iter()
.filter(|(h, _)| Some(*h) != app_handle)
.cloned()
.collect();
common.extended_data = rebuilt;
}