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.

This reference sits within SVG/DWG Parsing Workflows, part of the wider Automated Floor Plan Parsing & Vectorization section.