Skip to main content

ezdgn

ezdgn is a native, read-only V8 and read/write V7 DGN toolkit for Python, implemented with an independently written Rust core and PyO3 bindings. It has no ODA SDK, binary, or runtime dependency.

Installation

ezdgn requires Python 3.10 or newer.

python -m pip install ezdgn

Install the optional Matplotlib renderer when preview images are needed:

python -m pip install "ezdgn[plot]"

Building from source requires Rust 1.83 or newer. The extension uses Python's stable ABI (abi3) with a Python 3.10 minimum.

Supported scope

Format and operation Support
V7/ISFF 2D read Native entities, hierarchy, metadata, linkages, and raw records
V7/ISFF 2D write Standalone or custom-seed creation of common primitives
V7/ISFF 3D Signature, raw record, and common-header inspection only
V8 DGN read Native 2D/3D models, common geometry, hierarchy, linkages, auxiliary data, and lossless raw objects
V8 DGN write/edit Not supported

The V7 reader decodes line, line string, shape, curve, ellipse, arc, text, cell, text node, complex chain/shape, and B-spline records as native entities. It restores parent/child relationships without flattening component records, decodes typed attribute linkages while retaining their exact bytes, applies high-precision sub-UOR coordinate corrections alongside the stored integers, and resolves outline/fill colors through the active color table. Every record, including unsupported control and application elements, retains its original bytes.

The writer creates standalone V7 2D files from a bundled empty seed. A custom seed can be supplied to preserve its units, origin, and design plane. It writes line, line string, shape, curve, ellipse, arc, circle-as-ellipse, and raw-byte text entities with basic symbology and shape fill linkage. Coordinates outside the selected seed's design plane are rejected instead of silently clipped.

The native V8 reader decodes model metadata, line, line string, shape, curve, point string, text, ellipse, arc, text node, complex chain/shape, cell, shared-cell instance, B-spline curve/poles, and a bounded dimension anchor. Unknown graphical, control, name-table, linkage, and auxiliary records remain addressable with exact bytes. Product-specific custom-object semantics, full dimension annotation semantics, V8 writing/editing, and 3D screen projection for plotting remain outside the supported scope.

V7 3D geometry is not supported. The V7 writer does not yet create cells, complex elements, B-splines, arbitrary linkages, or perform in-place editing. The raw V7 record framing is shared by 2D and 3D files, so scan_records() can inspect a 3D stream safely without implying V7 3D entity support.

2D entity API

import ezdgn

drawing = ezdgn.readfile("drawing.dgn")

# All records remain ordered and lossless. entities contains only top-level
# graphics; all_entities also includes drawable component records.
print(len(drawing.elements), len(drawing.entities), len(drawing.all_entities))

for entity in drawing:
    print(entity.dxftype(), entity.level, entity.style)

for line in drawing.query("LINE"):
    print(line.start_uor, line.end_uor)
    print(line.start_master, line.end_master)

for text in drawing.query("TEXT"):
    print(text.text_bytes)
    print(text.decode_text())  # ASCII with strict errors by default
    print(text.decode_text("cp932", errors="replace"))

for cell in drawing.query("CELL"):
    print(cell.name, cell.origin_master, cell.transform)
    for component in drawing.children(cell):
        print("  ", component.dxftype())

for element in drawing.elements:
    for linkage in element.linkages:
        print(linkage.kind, linkage.linkage_type_name, linkage.raw_bytes)

drawing.entities contains supported top-level drawable entities only. drawing.all_entities is a flat tuple in original drawing.elements order: it includes both Cell/ComplexChain/ComplexShape container headers and their supported drawable descendants at every depth. It excludes control records, UnsupportedElement instances, and non-drawable B-spline support records; those remain available through the lossless drawing.elements sequence. drawing.unsupported_elements provides a filtered diagnostic view for unknown kinds. Use parent(), children(), or descendants() when a tree view is needed.

Ellipse, arc, curve, and B-spline entities retain their native parameters or control records; they are not flattened to polylines. parent_index and child_indices refer to the lossless drawing.elements sequence, while drawing.parent(), children(), and descendants() resolve the objects. Stored integer UOR, sub-UOR-corrected floating UOR, and optional master-unit coordinates coexist. drawing.color_table is the last type-5, level-1 color table in file order. drawing.resolve_color(), entity.style.rgb, and fill_rgb use that table when present and otherwise fall back to the standard MicroStation V7 256-color palette.

V7 DGN does not record a text code page. Text.decode_text() therefore uses encoding="ascii", errors="strict" as a deterministic default; pass the project encoding explicitly for non-ASCII text.

Known DMRS/database, association ID, shape fill, and high-precision linkages have typed fields. Unknown user linkages and malformed trailing attribute bytes remain accessible through read-only raw views. Shared-cell definition/instance types 34/35 remain raw because the public ISFF chapter does not specify their layout.

The high-level read()/readfile() API deliberately rejects V7 3D files. scan_records() and inspect_headers() still support bounded inspection of their shared record framing and metadata.

Native V8 API

Use open_document() when a caller may receive either format. The existing read() and readfile() names remain V7-specific for compatibility.

import ezdgn

document = ezdgn.open_document("drawing.dgn")
if isinstance(document, ezdgn.V8Document):
    for model in document.models:
        print(
            model.metadata.name,
            model.metadata.dimension,
            model.metadata.master_unit,
        )
        for entity in model.entities:
            print(entity.dxftype(), entity.level, entity.common.color_index)

        for text in model.query("TEXT"):
            print(text.data.text, text.data.text_bytes)

        for cell in model.query("CELL"):
            for component in model.children(cell):
                print("  ", component.dxftype())

V8Model.elements is the complete graphical-object sequence. V8Model.entities is the feature-oriented view: complex/cell headers remain native aggregate entities, while TextNode headers yield their text children. parent(), children(), and descendants() navigate the original hierarchy. 2D and 3D coordinates coexist as (x, y, z) tuples; no Z coordinate is dropped. For complex headers, common.stored_dimension retains the header bit and common.dimension reports the effective dimension inherited from children. V8 text exposes its stored width_multiplier_raw and height_multiplier_raw alongside corrected width_uor, height_uor, width_master, and height_master distances.

The raw scanner is available independently of semantic decoding:

raw = ezdgn.scan_v8_objects("drawing.dgn")
for model in raw.models:
    for obj in model.graphical_objects:
        print(
            obj.stream_path,
            obj.inflated_offset,
            obj.element_type,
            obj.role,
            obj.raw_bytes,
        )

Every extraction and decode stage is bounded. Override the defaults as one immutable policy object when processing untrusted files:

limits = ezdgn.V8ScanLimits(
    max_file_size=256 * 1024 * 1024,
    max_objects=250_000,
    max_total_inflated_bytes=512 * 1024 * 1024,
)
document = ezdgn.read_v8("drawing-v8.dgn", limits=limits)

Malformed structure raises InvalidDgnError; configured resource ceilings raise DgnLimitError. The complete clean-room boundary, evidence ledger, and known limitations are recorded in docs/v8/SCOPE.md, docs/v8/FORMAT_NOTES.md, and docs/v8/PROVENANCE.md.

Plotting parsed drawings

The optional renderer can display a parsed V7 2D drawing or a V8 document and save it as an image without changing the native entity model:

import ezdgn

drawing = ezdgn.readfile("drawing.dgn")

figure, axes = ezdgn.plot(
    drawing,
    text_encoding="cp932",
    background="#111111",
)
figure.savefig("preview.png", dpi=150, bbox_inches="tight")

# Or render and save in one call.
ezdgn.save_plot(drawing, "preview.png", text_encoding="cp932")

# V8 uses already decoded native text and preserves Z in the object model;
# the preview is an explicit XY projection.
ezdgn.save_plot(ezdgn.read_v8("drawing-v8.dgn"), "preview-v8.png")

The equivalent CLI command is:

ezdgn plot drawing.dgn -o preview.png --encoding cp932

Omit -o to open an interactive Matplotlib window. Use --monochrome for a high-contrast preview, --hide-text to suppress text, or --coordinate-space uor when master-unit coordinates are unavailable. Run ezdgn plot --help for the complete option list.

Lines, line strings, shapes, ellipses, arcs, text, and drawable components of cells and complex elements are rendered. Ellipses and arcs are sampled only for display. Native type-11 curves and B-spline curves are previewed from their parsed control sequences; the source records and entity parameters are never flattened or modified. V8 3D geometry uses an XY preview projection, and orientation matrices are retained but not applied by the 2D renderer. V7 text does not store its code page, so the caller must select the correct encoding for non-ASCII text. Geometry and text with compatible display styles are batched to keep large previews practical.

V7 writer and optional custom seeds

import ezdgn

doc = ezdgn.new()  # uses the bundled empty V7 2D seed
msp = doc.modelspace()

msp.add_line(
    (0, 0),
    (10, 5),
    dgnattribs={"level": 2, "color": 3, "line_weight": 2},
)
msp.add_line_string([(0, 10), (5, 15), (10, 10)])
msp.add_shape(
    [(20, 0), (30, 0), (30, 10), (20, 10)],
    fill_color=6,
)
msp.add_ellipse((25, 25), primary_axis=5, secondary_axis=3, rotation=30)
msp.add_arc(
    (40, 5),
    primary_axis=5,
    secondary_axis=3,
    start_angle=30,
    sweep_angle=120,
)
msp.add_text("日本語", (0, 30), height=2, encoding="cp932")

doc.saveas("drawing.dgn")
roundtrip = doc.readback()

Pass a custom seed as ezdgn.new("project_seed.dgn") to use its units, origin, design plane, and active color table. new() copies the mandatory TCB, digitizer setup, level symbology, and last active color table from the selected seed. Set copy_seed_elements=True to retain every seed record, including any graphics. Text encoding is not recorded by V7 DGN, so add_text() accepts bytes directly or uses its encoding="ascii", errors="strict" defaults for str input.

Raw record API

import ezdgn

info = ezdgn.detect_format("drawing.dgn")
print(info.kind, info.dimension)

scan = ezdgn.scan_records("drawing.dgn")
print(len(scan.records), scan.termination)

for record in scan.records:
    print(record.offset, record.element_type, record.level, record.raw_bytes)

The V8_CFB result means that the input has the generic CFB signature used by V8 DGN files. It is intentionally described as a candidate because the outer signature alone does not prove that DGN-specific streams are present.

The bounded container inspector is the structural-only entry point. It verifies the known DGN root markers without decoding DGN stream contents:

container = ezdgn.inspect_v8_container("drawing-v8.dgn")
print(container.has_dgn_v8_markers)
print(container.model_storage_paths)
for entry in container.entries:
    print(entry.path, entry.kind, entry.size_bytes)

inspect_v8_container() alone is structural identification, not an entity or fidelity claim. Use scan_v8_objects() for bounded, exact raw objects and read_v8() or open_document() for native semantic decoding. The legacy read(), readfile(), and scan_records() contracts remain V7-specific and therefore reject V8 input. No V8 path silently converts to V7 or flattens native objects.

Design settings and common headers

import ezdgn

headers = ezdgn.inspect_headers("drawing.dgn")
settings = headers.design_settings

print(settings.master_unit_name, settings.uor_per_master)
print(settings.global_origin_master)

for element in headers.elements:
    common = element.common_header
    if common is not None:
        print(
            element.record.element_type,
            common.range.low_master,
            common.range.high_master,
            common.symbology.color,
        )

read_design_settings() decodes only the leading TCB. inspect_headers() pairs every raw record with its standard common header when that element type has one. Attribute bytes remain available as a read-only zero-copy ElementMetadata.attribute_view.

The same inspection is available from the CLI:

ezdgn inspect drawing.dgn
ezdgn inspect drawing.dgn --records --json
ezdgn inspect drawing.dgn --headers --json
ezdgn inspect drawing.dgn --entities --json
ezdgn inspect drawing-v8.dgn --json

Development

python -m venv .venv
. .venv/bin/activate
python -m pip install "maturin>=1.13,<2" "pytest>=8" "matplotlib>=3.8"
maturin develop
cargo fmt --check
cargo clippy --workspace --all-targets --all-features -- -D warnings
cargo test --workspace --all-features
python -m pytest
sha256sum -c tests/data/dgn/SHA256SUMS
uv lock --check

Run the bounded V8 fuzz target and parser microbenchmark separately:

cargo +nightly fuzz run v8_read -- -max_len=16777216
EZDGN_BENCH_ITERATIONS=100 cargo bench -p ezdgn-core --bench v8_read

Build a distributable wheel with:

maturin build --release --out dist

License

ezdgn is released under the MIT License. The bundled empty seed and test fixtures retain the separate upstream terms documented in src/ezdgn/_data/README.md and tests/data/dgn/README.md.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ezdgn-0.2.1.tar.gz (130.1 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

ezdgn-0.2.1-cp310-abi3-win_amd64.whl (418.3 kB view details)

Uploaded CPython 3.10+Windows x86-64

ezdgn-0.2.1-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (568.1 kB view details)

Uploaded CPython 3.10+manylinux: glibc 2.17+ x86-64

ezdgn-0.2.1-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (559.5 kB view details)

Uploaded CPython 3.10+manylinux: glibc 2.17+ ARM64

ezdgn-0.2.1-cp310-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl (981.6 kB view details)

Uploaded CPython 3.10+macOS 10.12+ universal2 (ARM64, x86-64)macOS 10.12+ x86-64macOS 11.0+ ARM64

File details

Details for the file ezdgn-0.2.1.tar.gz.

File metadata

  • Download URL: ezdgn-0.2.1.tar.gz
  • Upload date:
  • Size: 130.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for ezdgn-0.2.1.tar.gz
Algorithm Hash digest
SHA256 cbf5bdf80222cd8d3deeb47d4e691f0cb6679f7407e45f17974ee29d7ce2ff83
MD5 f14bc43158b09dc726e63c41ba8ecb1e
BLAKE2b-256 9620c9413c3f7e8b64a0fe989d8a82676c6187c6aa85154153ce8f3d849995f8

See more details on using hashes here.

Provenance

The following attestation bundles were made for ezdgn-0.2.1.tar.gz:

Publisher: release.yml on monozukuri-ai/ezdgn

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file ezdgn-0.2.1-cp310-abi3-win_amd64.whl.

File metadata

  • Download URL: ezdgn-0.2.1-cp310-abi3-win_amd64.whl
  • Upload date:
  • Size: 418.3 kB
  • Tags: CPython 3.10+, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for ezdgn-0.2.1-cp310-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 22f99fe02311631ff1a6e8ff3966081ae33f2e0919b3f78d51b6d3ac59a79a24
MD5 f95e3d13996e2ec801a29e633a8699a7
BLAKE2b-256 31a1296a1eb31d363d1e12b1a920c349210c278c6178fe3a0551aa631233a16e

See more details on using hashes here.

Provenance

The following attestation bundles were made for ezdgn-0.2.1-cp310-abi3-win_amd64.whl:

Publisher: release.yml on monozukuri-ai/ezdgn

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file ezdgn-0.2.1-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for ezdgn-0.2.1-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 36dca7765d5039a6a14fdbf7ac4981bef178a3b6a7476b22ec2a08d5eb3caae0
MD5 25ac6efac125bced7dcdca49ae54c02e
BLAKE2b-256 98c78ea07c85b3394ab2ec6f8167d5fbd4e04ce92403acfa8dea39be1ec56ac6

See more details on using hashes here.

Provenance

The following attestation bundles were made for ezdgn-0.2.1-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: release.yml on monozukuri-ai/ezdgn

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file ezdgn-0.2.1-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for ezdgn-0.2.1-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 3c424046358d9f14f38a6b56b74b26a71d8fb72828457eeef9c425e9f94f9e58
MD5 6988ade126d97cab68de020df3379333
BLAKE2b-256 09f7747d1c3fa788b595c2beb1d0d1a2ba13aee9bdf83aa74d0462f9e4ffb3f6

See more details on using hashes here.

Provenance

The following attestation bundles were made for ezdgn-0.2.1-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl:

Publisher: release.yml on monozukuri-ai/ezdgn

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file ezdgn-0.2.1-cp310-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl.

File metadata

File hashes

Hashes for ezdgn-0.2.1-cp310-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl
Algorithm Hash digest
SHA256 f79044221f20583582ef2acb003fac51e37ef9f2cb3772bb5fe065f9ebbc4471
MD5 a3ab58be91d909d9df786bff961f22e7
BLAKE2b-256 3cc3efb8c95671467f4e7ca1eac369be0052a42b45e61ce4ce3ccfb0468a69c5

See more details on using hashes here.

Provenance

The following attestation bundles were made for ezdgn-0.2.1-cp310-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl:

Publisher: release.yml on monozukuri-ai/ezdgn

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.2.4

5 files

0.2.3

5 files

0.2.2

5 files

This release

0.2.1 This release

5 files

0.2.0

5 files

0.1.2

5 files

0.1.1

5 files

0.1.0

5 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page