Skip to content

ezdxf write -> ezdxf.read -> saveas re-owns every SEQEND to the layout; libredwg dxf2dwg then segfaults

ezdxf 1.4.4: a scrub pipeline serializes a document with doc.write(stream), rewrites string tags, and reloads it with ezdxf.read(io.StringIO(text)) before saveas(). doc.audit() reports 0 errors and 0 fixes, and the drawing renders correctly. But libredwg 0.14 dxf2dwg crashes on the result after several minutes (exit 138 SIGBUS or 139 SIGSEGV; macOS crash report: dwg_ref_object <- dwg_fixup_BLOCKS_entities <- dwg_read_dxf). The source DXF converts fine, and so does a single recover.readfile() + saveas() round trip. Bisecting showed that a pure write -> ezdxf.read -> saveas cycle, with no content changes, is enough to reproduce the crash.

import io, ezdxf
from ezdxf import recover
doc, _ = recover.readfile('in.dxf')
buf = io.StringIO(); doc.write(buf)
ezdxf.read(io.StringIO(buf.getvalue())).saveas('out.dxf')  # dxf2dwg out.dxf -> SIGSEGV
1 solution
ranked by outcome — not votes
Accepted

After the reparse, every SEQEND is saved with the wrong owner (group code 330). In ezdxf 1.4.4, LinkedEntities.link_seqend() (ezdxf/entities/subentity.py) does seqend.dxf.owner = self.dxf.owner. That gives the SEQEND the POLYLINE/INSERT's owner (the layout's block record) instead of the POLYLINE/INSERT itself. When ezdxf.read() loads a file whose entities already have owners, all SEQENDs end up owned by the layout. In my file all 299 POLYLINE SEQENDs became 330 / 2. doc.audit() does not flag this. libredwg's DXF importer follows the owner while linking block entities and dereferences garbage.

Fix: after loading, re-assert ownership:

for e in doc.entitydb.values():
    if e.is_alive and e.dxftype() in {'POLYLINE', 'INSERT'}:
        e.take_ownership()  # sets VERTEX/ATTRIB/SEQEND owner = parent handle

Verify in the serialized text, not on a reloaded doc (the loader re-applies the bug). For each 0/SEQEND, its 330 must equal the preceding POLYLINE/INSERT's 5 handle. With the fix, dxf2dwg exits 0. Also prune SORTENTSTABLE.table entries of entities you deleted: ezdxf's delete_entity leaves them behind, and the audit does not flag those either.