Handling DWG Proxy Entities During Conversion

Proxy entities — ACAD_PROXY_ENTITY objects — are placeholders that AutoCAD writes for custom objects created by vertical products such as Civil 3D and Plant 3D, standing in for classes the reading application does not understand. During DWG-to-DXF conversion they either keep only a cached graphical snapshot or are dropped entirely, so ezdxf reports entity.dxftype() == "ACAD_PROXY_ENTITY" and cannot decode any parametric geometry beyond those cached graphics. The correct handling is detection, logging by handle, and mitigation at the source rather than pretending the proxy is real geometry. This page is part of the DWG Proprietary Limitations reference and assumes you already have a converted DXF to inspect.

How ezdxf Handles Proxy Entities

Autodesk vertical products define their own object classes — a Civil 3D corridor, a Plant 3D pipe, an alignment — through object enablers, runtime modules that teach AutoCAD how to draw and edit those objects. When a drawing containing such objects is opened by software that lacks the matching enabler, AutoCAD substitutes a proxy: an ACAD_PROXY_ENTITY that preserves the object’s handle and, optionally, a cached graphical representation captured at save time, but not the parametric definition. The custom object’s real geometry lives in code the reader does not have.

ezdxf is exactly such a reader. It never had the object enabler, so a proxy stays a proxy: entity.dxftype() returns "ACAD_PROXY_ENTITY", and there is no property that reconstructs the original corridor or pipe. At best, if the file was saved with cached proxy graphics, a downstream tool can recover a frozen visual snapshot of lines and arcs — an approximation with no editable topology and no attributes. Treating that snapshot as authoritative geometry is the central mistake this page exists to prevent. Because DWG is a closed format, this limitation compounds the general reasons ezdxf cannot read DWG directly, detailed in the pydwg Integration reference.

Classifying Native Geometry versus Proxy Entities After Conversion Decision diagram: each converted DXF entity is tested; native types such as LINE and LWPOLYLINE decode to real geometry, while ACAD_PROXY_ENTITY objects branch into a cached-graphics approximation when PROXYGRAPHICS was on, or total loss when it was off, and both proxy outcomes are logged by handle. DXF entity dxftype()? native proxy Real geometry LINE, LWPOLYLINE... ACAD_PROXY_ENTITY PROXYGRAPHICS? cached graphics = approximate only total loss Log by handle

Proxy handling belongs to the same family of closed-format problems as version incompatibility; when a converted drawing is missing objects you expected, checking DWG version compatibility and scanning for proxies are the two first diagnostics to run.

Production-Ready Script

This script scans a converted DXF, classifies every entity as native or proxy, records the handle and any cached-graphics extent of each proxy, and emits a report of unrenderable objects that a pipeline can gate on.

# ezdxf>=1.1.0 | python>=3.9
import ezdxf
import json
import sys
from collections import Counter
from pathlib import Path
from typing import Any

PROXY_TYPES = {"ACAD_PROXY_ENTITY", "ACAD_PROXY_OBJECT", "PROXY_ENTITY"}


def proxy_extent(entity) -> dict[str, Any] | None:
    """
    Try to read a cached bounding box for a proxy entity.

    Proxy graphics, when present, let ezdxf compute an approximate extent.
    Returns None when no cached graphics survived the conversion.
    """
    try:
        # bbox.extents works when cached proxy graphics are present.
        from ezdxf import bbox
        box = bbox.extents([entity])
        if box.has_data:
            return {
                "min": [float(c) for c in box.extmin],
                "max": [float(c) for c in box.extmax],
            }
    except Exception:
        pass
    return None


def scan_proxies(dxf_path: str, output_json: str) -> None:
    try:
        doc = ezdxf.readfile(dxf_path)
    except (IOError, ezdxf.DXFStructureError) as exc:
        sys.exit(f"Failed to load converted DXF: {exc}")

    msp = doc.modelspace()
    proxies: list[dict[str, Any]] = []
    native_count = 0
    type_histogram: Counter[str] = Counter()

    for entity in msp:
        dxftype = entity.dxftype()
        type_histogram[dxftype] += 1
        if dxftype in PROXY_TYPES:
            extent = proxy_extent(entity)
            proxies.append({
                "handle": entity.dxf.handle,
                "dxftype": dxftype,
                "layer": entity.dxf.get("layer", "0"),
                "cached_graphics": extent is not None,
                "approx_extent": extent,  # approximate only; never authoritative
            })
        else:
            native_count += 1

    total = native_count + len(proxies)
    report = {
        "source": dxf_path,
        "total_entities": total,
        "native_entities": native_count,
        "proxy_entities": len(proxies),
        "proxy_ratio": round(len(proxies) / total, 4) if total else 0.0,
        "type_histogram": dict(type_histogram.most_common()),
        "unrenderable": proxies,
    }

    Path(output_json).write_text(json.dumps(report, indent=2), encoding="utf-8")
    print(
        f"{len(proxies)} proxy / {native_count} native entities "
        f"({report['proxy_ratio']:.1%} proxy) -> {output_json}"
    )
    # Gate: fail loudly if a meaningful fraction is unrenderable.
    if report["proxy_ratio"] > 0.05:
        print("WARNING: >5% of entities are proxies; re-export from source.")


if __name__ == "__main__":
    scan_proxies("converted.dxf", "proxy_report.json")

Key implementation notes:

  • Match on entity.dxftype() against the proxy type names. Different AutoCAD releases and converters emit ACAD_PROXY_ENTITY, and older files may surface PROXY_ENTITY; check for the whole set.
  • proxy_extent() returns an approximate bounding box only when cached proxy graphics survived conversion. A None extent means the object came through with no recoverable geometry — a total loss that must be reported, not silently skipped.
  • The approx_extent field is labelled approximate deliberately. Cached graphics are a save-time snapshot; never feed them into spatial indexing or measurement as if they were true geometry.
  • The proxy_ratio gate turns a silent data-loss problem into a loud pipeline signal. A drawing that is 30% proxies is not usable geometry data regardless of how cleanly it converted.
  • The type_histogram helps triage which vertical product produced the file — a concentration of proxies on Civil 3D corridor layers points straight at the missing object enabler.

Compatibility Matrix

Component Supported range Notes
ezdxf >=1.1.0 dxftype(), dxf.handle, and ezdxf.bbox.extents are stable in 1.1.0+.
Python 3.9+ Uses pathlib, collections.Counter, and typing.
DXF format R2000 (AC1015) – R2018 (AC1032) Proxy entities are stored consistently across these revisions.
Proxy types ACAD_PROXY_ENTITY, PROXY_ENTITY Emitted for Civil 3D, Plant 3D, Map 3D custom objects.
Cached graphics PROXYGRAPHICS=1 Only recoverable when the source saved proxy graphics.
Object enablers Source-side The only path to true geometry is the originating application.

Fallback Strategies

1. Re-export with PROXYGRAPHICS=1

The PROXYGRAPHICS system variable governs whether AutoCAD stores cached graphics for custom objects. If it was 0 at save time, proxies may carry no recoverable geometry after conversion. Ask the source team to set PROXYGRAPHICS=1 and re-save, which at least preserves a visual approximation you can log and, where acceptable, use for footprint extraction.

2. Explode custom objects at the source

The most reliable fix is to convert custom objects to plain AutoCAD primitives before export. In Civil 3D or Plant 3D, exploding corridors, alignments, or pipe runs to lines, arcs, and polylines produces native entities ezdxf decodes fully. This trades editability for portability — acceptable when the downstream consumer only needs geometry, not the parametric model.

3. Install the object enabler where the workflow allows

Autodesk publishes free object enablers for many verticals. In a workflow that still runs through an AutoCAD-family reader before the Python stage, installing the matching enabler lets the true geometry survive into the exported DXF. This does not help pure-ezdxf pipelines, which have no enabler mechanism, but it is worth flagging to the source team.

4. Request a native geometry export (IFC, DGN, or LandXML)

For infrastructure objects, a format built for the data often beats DXF entirely: LandXML for alignments and surfaces, IFC for building objects. Requesting a purpose-built export sidesteps the proxy problem, because the custom object is serialized as first-class geometry rather than an opaque proxy.

5. Treat cached graphics as approximate only

When you must use recovered cached graphics, quarantine them. Tag every derived feature as approximate, exclude it from measurement and clash analysis, and record the proxy handle so the provenance is auditable. Never let a proxy-derived footprint enter a dataset that downstream users assume is survey-grade.

FAQ

What is an ACAD_PROXY_ENTITY in a DWG file?

An ACAD_PROXY_ENTITY is a stand-in that AutoCAD writes for a custom object created by a vertical product such as Civil 3D or Plant 3D when the reading application lacks the object enabler that defines that class. It may carry a cached graphical representation but not the parametric geometry, so ezdxf cannot decode its true shape.

Can ezdxf recover geometry from a proxy entity?

No. ezdxf sees entity.dxftype() == "ACAD_PROXY_ENTITY" and can read only whatever cached proxy graphics were stored at save time. It cannot reconstruct the original custom object’s parametric geometry. Treat any recovered cached graphics as an approximate visual snapshot, not authoritative geometry.

What does PROXYGRAPHICS control?

The PROXYGRAPHICS system variable controls whether AutoCAD saves cached graphics for custom objects into the file. With PROXYGRAPHICS=1 the DWG stores a graphical snapshot that survives conversion; with PROXYGRAPHICS=0 proxies may carry no recoverable geometry at all after conversion.

How much proxy content is acceptable in a converted drawing?

There is no universal threshold, but a pipeline should gate on the proxy ratio. A drawing that is a few percent proxies on annotation layers may be usable; one that is tens of percent proxies on the layers carrying your target geometry is not, and should be sent back for a native re-export before any downstream processing.