fix(task-2): block_explode bursts multi-line attributes as MTEXT and refuses xrefs and MINSERTs on both engines

A multi-line ATTRIB keeps its content in an embedded MTEXT; dxf.text is only
the first line (ezdxf) or "Line1\PLine2" (AutoCAD), so the TEXT the explode
emitted silently dropped lines or rendered literal \P with a clean audit.
ezdxf: `_burst_attrib` adds `virtual_mtext_entity()` (graphic properties and
WCS placement already carried; the reference matrix applied for a constant
ATTDEF) and marks it invisible like the ATTRIB. COM: `_capture_attrib` reads
`MTextAttribute` / `MTextAttributeContent` / `MTextBoundaryWidth` and
`_add_mtext_from_capture` goes through `AddMText` (height, style, frame,
then Rotation, then the attachment mapped from the ATTRIB's Alignment).

Two INSERT kinds `virtual_entities()` cannot explode were destroyed with
ok: True: an xref (no virtual entities, reference deleted) and a MINSERT
(only the first cell resolved, the rest of the grid gone). Both are now
refused by name before any write, on both engines (`block_record.is_xref`
/ `mcount > 1` headless; `Blocks.Item(Name).IsXRef` / `AcDbMInsertBlock`
live, the latter previously failing the INSERT check with the misleading
"not a block reference").

Tests: nine new cases in tests/test_track_a_hardening.py (headless multi-line
ATTRIB and constant ATTDEF, xref and MINSERT refusals, a single-cell grid
that still explodes; COM multi-line capture, frame-before-angle, and both
named refusals). The fake ActiveX surface gains AddMText and IsXRef.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FKFStjEFzpGyLq5bMpWxTd
This commit is contained in:
Umut CAN
2026-09-17 10:16:26 +03:00
co-authored by Claude Opus 5
parent b12fa854c1
commit 6cb7a13c66
4 changed files with 527 additions and 27 deletions
+9 -8
View File
@@ -3031,16 +3031,17 @@ async def block_explode(
Every attached ATTRIB becomes a TEXT with the same value, placement,
height, rotation and layer (an invisible attribute becomes an invisible
TEXT), and a constant attribute (which has no ATTRIB) becomes a TEXT of
its value too; AutoCAD's plain EXPLODE would keep only the tag-name
placeholders and drop the values. Everything lands in the layout that owns the
TEXT; a multi-line attribute becomes an MTEXT carrying every line), and
a constant attribute (which has no ATTRIB) becomes a TEXT of its value
too; AutoCAD's plain EXPLODE would keep only the tag-name placeholders
and drop the values. Everything lands in the layout that owns the
reference (model space or its paper-space sheet), whichever tab is
current. Returns `inserted_handles` (the geometry), `attribute_texts`
(one TEXT handle per attribute), `exploded_handle` and `backend`.
Refused, with nothing written: a handle that is not a block reference,
and a reference nested inside a block definition (explode the outer
reference instead). Not undoable except through `drawing_undo` / a
transaction.
(one TEXT or MTEXT handle per attribute), `exploded_handle` and
`backend`. Refused, with nothing written: a handle that is not a block
reference, a reference nested inside a block definition (explode the
outer reference instead), an external reference (bind it first), and a
MINSERT grid. Not undoable except through `drawing_undo` / a transaction.
"""
await ctx.warning(f"Exploding block reference {handle}")
return await _backend(ctx).block_explode(handle)