ezdxf vs. LibreDWG for Batch DWG Parsing
Choosing the CAD reader for an automated floor-plan pipeline is a decision you make once and live with across a whole portfolio, and it sits at the front of the SVG/DWG parsing workflows that feed everything downstream.
What the ezdxf vs. LibreDWG Choice Actually Decides
Both libraries turn a CAD drawing into iterable geometry, but they start from opposite ends of the format problem, and that difference propagates into every batch job you build.
ezdxf is a pure-Python library whose native format is DXF — the ASCII (or binary) interchange format. It gives you a rich, Pythonic object model: a document with a modelspace, layers, block definitions, and typed entities (LINE, LWPOLYLINE, ARC, INSERT) that you traverse with ordinary attribute access. It does not read the proprietary binary DWG format directly; to parse native DWG you first convert to DXF with an external tool such as the ODA File Converter or a bundled dwg2dxf. Within DXF, however, ezdxf is expressive: it explodes block references, resolves entity attributes, and edits geometry in place.
LibreDWG is a C library, part of the GNU project, built specifically to read (and increasingly write) the native binary DWG format across a broad span of AutoCAD versions. It exposes Python through SWIG bindings (libredwg) that surface a lower-level, C-shaped object graph — you walk sections, objects, and entity structs closer to the on-disk layout. That gets you native DWG with no conversion hop, at the cost of a more verbose, less ergonomic API and a heavier build/packaging story.
The one-line framing: ezdxf trades native-DWG support for a superb Python API on DXF; LibreDWG trades API ergonomics for reading native DWG directly. Most automated indoor-mapping pipelines standardize on DXF as the internal interchange format and reach for ezdxf, converting native DWG once at ingest — but a portfolio dominated by raw, un-convertible DWG deliverables is exactly where LibreDWG earns its keep.
Minimal Working Example
The following extracts wall-candidate geometry from a DXF modelspace with ezdxf, iterating both top-level entities and the contents of exploded block references. It is the shape of the inner loop most batch parsers run.
import logging
import ezdxf
from ezdxf.document import Drawing
from shapely.geometry import LineString
logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
logger = logging.getLogger(__name__)
WALL_LAYERS = {"A-WALL", "WALLS", "A-WALL-FULL"}
def extract_wall_lines(dxf_path: str) -> list[LineString]:
"""Read LINE and LWPOLYLINE geometry on wall layers, exploding block inserts."""
try:
doc: Drawing = ezdxf.readfile(dxf_path)
except (IOError, ezdxf.DXFStructureError) as exc:
logger.exception("could not read %s: %s", dxf_path, exc)
raise
msp = doc.modelspace()
# virtual_entities() explodes INSERT blocks into their world-coordinate geometry.
inserts = (e for ins in msp.query("INSERT") for e in ins.virtual_entities())
walls: list[LineString] = []
for entity in list(msp) + list(inserts):
if entity.dxf.layer not in WALL_LAYERS:
continue
if entity.dxftype() == "LINE":
walls.append(LineString([entity.dxf.start[:2], entity.dxf.end[:2]]))
elif entity.dxftype() == "LWPOLYLINE":
pts = [(x, y) for x, y, *_ in entity.get_points()]
if len(pts) >= 2:
walls.append(LineString(pts))
logger.info("extracted %d wall lines from %s", len(walls), dxf_path)
return walls
The virtual_entities() call is the load-bearing detail: a floor plan’s walls are almost always nested inside block references (a WALL block inserted per level), and iterating the modelspace alone would miss them entirely. LibreDWG’s equivalent walk exists but is spelled in terms of C structs and section pointers rather than a query() DSL.
Comparison Reference
| Dimension | ezdxf | LibreDWG |
|---|---|---|
| Native DWG support | No — needs a DXF conversion (ODA/dwg2dxf) first |
Yes — reads binary DWG directly |
| DXF support | Native, comprehensive, read + write | Reads DWG’s DXF-equivalent; DXF I/O less central |
| Implementation | Pure Python | C core with SWIG Python bindings |
| DWG version reach | Via converter (whatever the converter supports) | Broad, from R13 through recent releases |
| API ergonomics | High — modelspace, query(), typed entity attrs |
Low — C-shaped object graph, manual struct walks |
| Block / INSERT handling | virtual_entities() explodes to world coords |
Supported but manual dereferencing of block headers |
| Speed (per file) | Fast for DXF; conversion adds a step | Fast native parse, no conversion hop |
| Memory | Full document held in Python objects | Leaner C structs, thinner Python wrappers |
| Packaging | pip install ezdxf, no system deps |
System build of libredwg + bindings; heavier CI setup |
| Licensing | MIT | GPL-3.0 (affects redistribution) |
| Maintenance / docs | Active, extensively documented | Active GNU project, sparser Python-facing docs |
Common Errors & Fixes
DXFStructureError when reading a native DWG. ezdxf was handed a .dwg file, which it cannot parse — it only reads DXF. Convert first, then read:
# Wrong: ezdxf.readfile chokes on binary DWG.
# doc = ezdxf.readfile("level_02.dwg") # -> DXFStructureError
import subprocess
# Convert once at ingest (ODA File Converter or LibreDWG's dwg2dxf).
subprocess.run(["dwg2dxf", "level_02.dwg", "-o", "level_02.dxf"], check=True)
doc = ezdxf.readfile("level_02.dxf")
Walls come back empty even though the drawing clearly has them. The geometry lives inside unexploded block references, so a plain modelspace iteration sees only the INSERT placeholders, not their contents. Explode them into world coordinates before filtering:
msp = doc.modelspace()
lines = list(msp.query("LINE")) # top-level only — misses blocks
for ins in msp.query("INSERT"):
lines.extend(e for e in ins.virtual_entities()
if e.dxftype() == "LINE") # now includes block geometry
Version incompatibility on a mixed portfolio. A batch that hard-codes one AutoCAD version assumption fails when a vendor ships an older or newer DWG. With ezdxf, standardize by round-tripping every input through the converter to a single target DXF version at ingest; with LibreDWG, confirm the drawing’s version falls inside the supported range and route anything outside it to a review queue rather than letting a partial parse emit truncated geometry.
Integration Point
Whichever reader you pick, its job ends at the same handoff: a set of typed, world-coordinate line and polyline geometries that the wall and door detection algorithms classify into structural walls and openings. In practice most pipelines standardize on ezdxf and treat native-DWG conversion as an ingest concern, which keeps the parsing code in one language and leans on ezdxf’s block handling — the same pattern the dedicated parsing DWG files with Python and ezdxf reference walks through end to end. That parsed geometry is the first stage of the full DWG floor plan to routable GeoJSON pipeline, where the entities extracted here become walls, doors, a routing graph, and finally a validated GeoJSON FeatureCollection.
Frequently Asked Questions
Can ezdxf read native DWG files at all?
Not directly. ezdxf’s native format is DXF, so a raw binary .dwg must first be converted — most teams use the ODA File Converter or LibreDWG’s dwg2dxf utility as a one-time ingest step, then read the resulting DXF with ezdxf. This is usually a feature rather than a limitation: converting to a single canonical DXF version at ingest normalizes a mixed-vintage portfolio before any geometry code runs, so the parser only ever sees one well-understood format.
Which is faster for a large batch of floor plans?
It depends on where the format lives. If your inputs are already DXF, ezdxf parses them quickly with no external process. If they are native DWG, LibreDWG avoids the conversion hop and parses in one pass, which can win on very large batches of raw DWG. But the conversion step in an ezdxf pipeline parallelizes trivially across workers, so in an async batch system the wall-clock difference often disappears — the more decisive factors are API ergonomics and packaging.
Does the GPL licence of LibreDWG matter for my pipeline?
It matters if you redistribute software that links LibreDWG. LibreDWG is GPL-3.0, so bundling it into a distributed product carries copyleft obligations; ezdxf’s MIT licence does not. For an internal, server-side batch pipeline that never ships the library to third parties the practical difference is small, but it is worth confirming against your organization’s distribution model before standardizing on LibreDWG.
Related
- Parsing DWG Files with Python and ezdxf
- SVG/DWG Parsing Workflows
- DWG Floor Plan to Routable GeoJSON in One Pipeline
- Wall & Door Detection Algorithms
This reference sits within SVG/DWG Parsing Workflows, part of the wider Automated Floor Plan Parsing & Vectorization section.