Skip to content
DrunkOnJavaPublic

About

Apache-2.0 Rust reader for Autodesk DWG files (R13 → R2018 / AC1032), built from the Open Design Alliance's published specification. Pre-alpha.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

9 stars

Watchers

0 watching

Forks

Latest commit

 

History

355 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

dwg-rs

Apache-2.0 Rust reader (and early writer) for Autodesk DWG files, R14 to R2018 (AC1014 to AC1032), built only from the Open Design Alliance's published specification. Pre-alpha. Every pinned public drawing walks completely, every decoded record is checked against its own data-stream boundary, and the DXF output is compared field by field with AutoCAD's own exports.

CI Perf Doc build License: Apache-2.0 Rust MSRV


Pre-alpha status: read this first

This is 0.1.0-alpha.1. Do not use it in production. Do not benchmark it against the ODA SDK. Do not tell your CAD team dwg-rs solves their interop problem today.

Entity and object decode coverage, measured in CI by the Corpus (public DWG) job on main (run 40739158933 on 2026-10-28) with examples/coverage_report.rs, on the public drawings fetched by pinned sha256 (tools/corpus/pins.tsv; none is committed):

Version Files tested Decoded Skipped Errored Success rate Median drawing
R14 (AC1014) 12 4737 19 0 99.6 % 100.0 %
R2000 (AC1015) 9 2785 0 0 100.0 % 100.0 %
R2004 (AC1018) 12 106994 0 0 100.0 % 100.0 %
R2007 (AC1021) 8 2237 0 0 100.0 % 100.0 %
R2010 (AC1024) 8 2188 0 0 100.0 % 100.0 %
R2013 (AC1027) 8 1787 0 0 100.0 % 100.0 %
R2018 (AC1032) 4 1615 0 0 100.0 % 100.0 %
Aggregate 61 122343 19 0 > 99.9 % 100.0 %

A success rate counts records, and the sources differ in size by orders of magnitude (four R2004 drawings can hold more records than every other source together), so pinning a source of large drawings moves it without any decoder changing. The median drawing weighs every drawing the same, and each source's own row below stays put when another source is pinned, so these show whether a change came from decoding or from the corpus:

Source Files tested Decoded Skipped Errored Success rate Median drawing
nextgis 42 8443 0 0 100.0 % 100.0 %
acadsharp 10 7631 0 0 100.0 % 100.0 %
bookviewerapp3 4 1440 19 0 98.7 % 99.4 %
oshw-jetson 4 104577 0 0 100.0 % 100.0 %
planar-algorithm 1 252 0 0 100.0 % 100.0 %
All sources 61 122343 19 0 > 99.9 % 100.0 %

Totals: 122,343 decoded / 19 skipped / 0 errored / > 99.9 % of all records on 61 drawings; the median drawing decodes 100.0 %. Per source (drawings): nextgis 100.0 % (42), acadsharp 100.0 % (10), bookviewerapp3 98.7 % (4), oshw-jetson 100.0 % (4), planar-algorithm 100.0 % (1). Per version: R14 99.6 %, R2000 100.0 %, R2004 100.0 %, R2007 100.0 %, R2010 100.0 %, R2013 100.0 %, R2018 100.0 %.

tools/corpus/baseline.tsv pins each drawing's decoded, skipped and errored counts, its class-census shortfalls and its reference closure. The job fails on any difference, in either direction, so a regression is red and an improvement is recorded in the pull request that makes it. The tables above are rendered from that file by tools/maintainer/readme_status.py, never typed by hand.

The drawings come from nextgis/dwg_samples (seven drawings, each saved by AutoCAD 2016 as R14 to R2013; no licence is stated, so they are fetched for the run and never shared) and from the MIT-licensed samples/ of DomCR/ACadSharp (one drawing saved by AutoCAD 2025 as R14 to R2018, and three more R2018 drawings). Every save of those drawings except R14 has AutoCAD's own DXF export of the same drawing beside it; of the three extra R2018 drawings only geoloc.dwg has one. CLEANROOM.md records the provenance.

What "errored" and "skipped" mean. Every decoder in this crate is self-validating: its field list must end exactly on the record's data-stream boundary (the string-stream trailer on R2010+, the RL object-data size on R2000 to R2007, the size inside the common entity data on R13/R14), or it returns an error instead of plausible-looking geometry. An errored record is a field list that does not add up; there are none left on the pinned corpus. A skipped record is a type with no decoder on main yet. Two of the skipped types have a decoder in review, SHAPE (#310) and the VIEWPORT entity header (#311). The rest are custom classes in the four drawings pinned from kurema/BookViewerApp3: the render settings (RAPIDRTRENDERSETTINGS, MENTALRAYRENDERSETTINGS and RENDERGLOBAL) and five associative-network classes (ACDBASSOCNETWORK, ACDBASSOCACTION, ACDBASSOCDEPENDENCY, ACDBASSOCVERTEXACTIONPARAM and ACDBASSOCARRAYACTIONBODY). Their few records do not settle a field list, and those drawings have no DXF export to derive or score them against yet (#152). The Corpus job's corpus-measurement artifact lists the skipped records of each drawing by type (corpus-logs/).

Most of the classes in that list are not printed in the spec at all; their field lists are learned from AutoCAD's own DWG and DXF output under the method recorded in CLEANROOM.md, and each stays provisional until a controlled AutoCAD drawing confirms it (#348).

Values, not just widths. A boundary that closes proves the field widths add up, not that the values are right. The DXF value oracle (examples/dxf_oracle/) pairs every decoded record with the same handle in AutoCAD's DXF export and scores each field. On main it scores 173,688 field comparisons on 73 types: 153,787 equal, 11,916 equal to the DXF default, 288 different and 7,697 present only in the DWG (tools/oracle/baseline.tsv pins the result, and CI fails when it moves). Decoded types with no scorer yet are listed as unscored in the oracle summary the Corpus job uploads.

Not yet in the table: R13 (AC1012). The reader for it is in review (#323), pinned on 22 public R13 drawings, none of them written by AutoCAD R13 itself (#345).

Roadmap

The plan to 1.0 is the milestones and their epics, described in ROADMAP.md. docs/spec-coverage.md gives every section of ODA spec v5.4.1 its state on main, and docs/release-1.0-criteria.md is the 1.0 release decision: no release tagged 1.0 until every requirement there has passed.

Capability matrix at a glance

Layer Status Notes
File identification (AC1014 to AC1032) Shipped R13 (AC1012) in review, #323
R13 to R15 file header Shipped Plain and XOR-encrypted variants
R2004 family and R2007 file headers Shipped Every release from R2004 to R2018
LZ77 decompression with output caps Shipped §4.7 and R2007's §5.10 dialect; 256 MiB default, configurable
Page map and section map Shipped With a fallback path via SectionMapStatus
Sec_Mask (R2004 family) Shipped R2007 does not use it; see src/r2007.rs
CRC-8, CRC-32 and record CRCs Shipped
Reed-Solomon (255,239) Shipped §5.13's codes, as AutoCAD writes them; read-side error correction is not wired in
Metadata (SummaryInfo, AppInfo, Preview, FileDepList) Shipped UTF-16 auto-detect, PNG thumbnail carve
Handle map and class map Shipped
Header variables Shipped Table-driven by release range (#126)
Object-stream walker Shipped Every release from R14 to R2018; every handle-map entry resolves to a record
Entity and object decoders Alpha 0 errored records on the pinned corpus; the skipped types are listed above, two with a decoder in review
Symbol tables Alpha Every table record in the pinned corpus decodes, on every release; values scored by the DXF oracle
Entity graph (owners, reactors, blocks, layers) Partial Resolver APIs exist; reference closure is measured per drawing in CI
Streaming reader Alpha Byte-demand state machines up to the object records; see below
DXF writer Alpha HEADER, CLASSES, the symbol tables, BLOCKS (#371), ENTITIES (15 kinds) and OBJECTS (the dictionary family, XRECORD, SCALE and placeholders, #372; LAYOUT, GROUP and MLINESTYLE, #373; VISUALSTYLE, #413) written from decoded records and paired with AutoCAD's exports, in the drawing's code page (#382)
DXF reader Alpha ASCII DXF keyed by handle, used by the value oracle
DWG writer Alpha Assembles an R2004-family file this crate reads back; no CAD application has been checked against it
SVG and PDF export Alpha SVG writer and a paged-SVG PDF path; output quality follows the decoded geometry
glTF 3D export Alpha glTF and GLB writer; surface tessellation is approximate
WASM viewer Partial Core APIs and 2D viewer pieces exist; selection, progressive and 3D APIs are stubs
Python bindings Pending A stub module only; see docs/python.md

Streaming reader

The streaming core reads DWG through explicit byte demands, fixed storage, pausable sources and portable checkpoints. Its grammar is no_std with no allocator, compiled natively and for wasm32-unknown-unknown, and CI enforces that seal (tools/streaming/check_invariants.py). On main it covers:

  • R13 to R2000: the file header and the whole container walk to every mapped record's framing, type and handle;
  • the R2004 family: the file header, a bounded resumable LZ77 decoder, the page and section maps, the data pages, the whole-file walk and the object records, each record's CRC checked (the legacy walker never checked it);
  • R2007: its own whole-file walk with every checksum checked before bytes are handed out, and its object records.

It does not decode object fields yet; the legacy DwgFile reader still loads whole files and sections. That work, and large-model evidence, remain open in #205. Comparisons of success rate, coverage or memory against other DWG readers are NOT MEASURED; no claim of superiority to another reader is made.

What works today

The container layer is the most mature part of the crate. It is measured on real drawings, not by a test count: the Corpus (public DWG) job opens every pinned drawing and fails on any change from the committed baselines.

  • Version identification from AC1014 (R14, 1997) to AC1032 (2018 and later)
  • R13 to R15 file header and the R2004-family XOR-encrypted header
  • Page map and section map parsing, LZ77 decompression, Sec_Mask un-masking
  • R2007's container: Reed-Solomon de-interleave, its LZ dialect, page and section maps
  • DwgFile::read_section(name): decompressed bytes for any named section
  • CRC-8, CRC-32 and Reed-Solomon (255,239)
  • Metadata parsers: SummaryInfo, AppInfo (R18 ANSI and R21+ UTF-16 auto-detected), Preview (BMP, WMF, PNG), FileDepList
  • Handle map, class map and header variables
  • R14 to R2004 text in the code page the file header names, through one shared decoder (#374): the ten pages real drawings prove, 1250 to 1254, 1257 and the double-byte 932, 936, 949 and 950 (#384), in entity and object text, XRECORD values and extended data (#380); dwg-to-dxf and the DWG writer write the same page back (#382)
  • The object walk on every release from R14 to R2018, and the class census CI gate that holds it to what AcDb:Classes declares
  • Entity and object decoding, boundary-checked on every release, with the values scored against AutoCAD's DXF exports
  • dwg-to-dxf: HEADER, CLASSES, the symbol tables, BLOCKS, ENTITIES and OBJECTS from the decoded records
  • An experimental R2004-family write path: bit writer, LZ77 encoder, section framer, Sec_Mask, the CRCs and checksums real files carry, and assembly via DwgFile::to_bytes()

The LZ77 offset encoding is ambiguous in one place in the spec; this crate's implementation was cross-checked against the algorithm-description comments in the MIT-licensed ACadSharp source (comments only, no code). CLEANROOM.md records that and every later disclosure.

What dwg-rs does not do today

  • Read R13 (AC1012) files; the reader is in review (#323).
  • Read R14 to R2004 text in a code page no measured drawing names: a header short outside the ten proven pages still decodes as lossy UTF-8 (#375).
  • Decode password-protected R2007 files: a section whose descriptor declares encryption is refused, not decoded.
  • Decode the render-settings and five associative-network classes listed above; the pinned corpus holds a few such records and no DXF export of them yet (#152).
  • Write R13, R14 or R2007 files; the writer covers the R2004 family only.
  • Byte-identical DWG round trips, or output checked by any CAD application.
  • Python bindings, a non-technical end-user application, an installer or a drag-and-drop GUI.

Install

# From git (currently the only distribution):
git clone https://github.com/DrunkOnJava/dwg-rs
cd dwg-rs
cargo build --release

The 0.1.0-alpha.1 crate has not been published to crates.io yet; publishing is tracked in #109 and RELEASE.md.

Use the parts that work

use dwg::DwgFile;

fn main() -> dwg::Result<()> {
    let file = DwgFile::open("drawing.dwg")?;
    println!("version: {}", file.version());
    println!("sections: {}", file.sections().len());

    // Decompressed bytes for any named section.
    if let Some(Ok(bytes)) = file.read_section("AcDb:Preview") {
        println!("preview section: {} bytes", bytes.len());
    }

    // Structured metadata.
    if let Some(Ok(summary)) = file.summary_info() {
        println!("title:  {}", summary.title);
        println!("author: {}", summary.author);
    }

    // Handle-indexed object walk, on every release from R14 on.
    if let Some(Ok(objects)) = file.all_objects() {
        println!("raw objects: {}", objects.len());
    }

    // Entity decode: alpha quality. The DispatchSummary reports per-file
    // coverage; check it before relying on the output.
    if let Some(Ok((entities, summary))) = file.decoded_entities() {
        println!(
            "entities: {} decoded / {} skipped / {} errored ({:.1}% decoded)",
            summary.decoded,
            summary.unhandled,
            summary.errored,
            summary.decoded_ratio() * 100.0
        );
    }

    Ok(())
}

Other examples live in examples/:

CLI tools

Seven binaries ship behind the cli feature flag. Inspection tools work on any file the container layer can parse; export tools (dwg-to-*) work as far as the decoders for your file's release do (see the compatibility matrix).

cargo run --release --features cli --bin dwg-info -- drawing.dwg

# Inspection
dwg-info drawing.dwg                                        # version and section list
dwg-corpus /path/to/corpus/                                 # sweep a directory
dwg-dump drawing.dwg                                        # hierarchical dump
dwg-convert --extract AcDb:Preview -o preview.bmp x.dwg     # one decompressed section
dwg-convert --verify drawing.dwg                            # decompress every section

# Export (alpha; checked against AutoCAD's DXF, not against a CAD application)
dwg-to-dxf drawing.dwg out.dxf --version R2018              # ASCII DXF (R12 to R2018)
dwg-to-gltf drawing.dwg out.glb                             # glTF 2.0 binary (.glb)
dwg-to-gltf drawing.dwg out.gltf                            # glTF JSON and a sidecar .bin

# Write (experimental; --dwg assembles an R2004-family file)
dwg-write --version R2018 \
  --section AcDb:Header=header.bin \
  --section AcDb:SummaryInfo=summary.bin \
  --report report.json --dwg out.dwg

Architecture

See ARCHITECTURE.md for the design deep-dive: format primer, module responsibilities, the read pipeline, Sec_Mask, the LZ77 spec errata, and a dated section with its evidence for every format finding.

Quick layer overview:

  DwgFile::open ─────────────────────────────────────────┐
         │                                               │
         ▼                                               ▼
   header + section map                       handle map + class map
         │                                               │
         ▼                                               ▼
   read_section("AcDb:*")                       all_objects()  ──► [shipping]
         │                                               │
         ▼                                               ▼
   metadata::* (SummaryInfo,                   decoded_entities() ──► [alpha]
   AppInfo, Preview, FileDepList)                       │
                                                        ▼
                                         dispatch on type code → entities::*,
                                         tables::*, objects::*
                                                        │
                                                        ▼
                                        dxf / svg / gltf exporters, writer

R13 to R15 files are the object stream, addressed by a flat locator list; the R2004 family adds an encrypted header, a page map, Sec_Mask and LZ77; R2007 has its own Reed-Solomon pages, LZ dialect and maps. The three share no mechanism.

Verification

$ tools/ci/run_test_targets.sh --release --all-features          # the real-file, contract and hardening targets, then the doc tests
$ cargo clippy --all-targets --all-features -- -D warnings       # clean
$ cargo fmt --all -- --check                                      # clean
$ RUSTDOCFLAGS="-D warnings" cargo doc --no-deps --all-features   # clean
$ cargo deny check advisories bans licenses sources               # clean
$ python3 tools/ci/spec_coverage.py --check                       # spec matrix agrees with its data
$ python3 tools/maintainer/readme_status.py --check               # this README agrees with the baseline

main requires twelve CI checks, including Corpus (public DWG), which fetches the 52 pinned drawings, measures them, runs the probes and the value oracle, and fails on any change from the committed baselines. The older real-file suites (samples, block_names, corpus_roundtrip, r2013_entity_values) read a 19-file subset and skip silently when it is absent, so a green run of them says nothing about real drawings unless python3 -I tools/corpus/fetch.py --legacy-samples ../../samples has run.

Safety

The whole crate is #![forbid(unsafe_code)]. Reed-Solomon, LZ77, GF(256), the bit cursor and the bit writer are all safe Rust. Every parser returns Result<T, Error>; malformed input returns an error and never panics. Claimed counts and allocations are capped (ParseLimits, WalkerLimits). See SECURITY.md and THREAT_MODEL.md.

MSRV policy

Rust 1.85 (for edition = "2024"). MSRV bumps are minor-version events announced in the CHANGELOG. CI verifies MSRV on every PR.

Related projects

Project Language License Notes
ACadSharp C# MIT Permissive reference; dwg-rs cross-checked the LZ77 offset errata against its algorithm comments (not its code). Its MIT samples/ are part of the CI corpus
LibreDWG C GPL-3 The most complete open-source DWG reader; preferable to dwg-rs today for any stack that can take GPL-3. Its source was not consulted during this crate's implementation
Teigha / ODA SDK C++ Commercial Proprietary; paid membership required
dxf-rs Rust MIT DXF (the text companion format) only
rvt-rs Rust Apache-2.0 Sibling project: an Autodesk Revit (.rvt / .rfa) reader by the same author, with the same source-provenance policy

Why this exists

DWG is Autodesk's proprietary format. Autodesk does not publish a specification. What's available to open-source implementers is:

  • The Open Design Alliance's Open Design Specification, the result of the ODA's own long-standing reverse-engineering effort, made available publicly. dwg-rs is built from version 5.4.1 of that spec.
  • LibreDWG (GPL-3), the most complete open-source DWG reader today. If GPL-3 fits your project, it is almost certainly the better tool.
  • ACadSharp (MIT, C#), a mature .NET DWG reader for stacks that can take a C# dependency.
  • Teigha / the ODA SDK, a commercial C++ SDK appropriate for production workloads that can afford the membership.

dwg-rs occupies a narrow niche: a permissively licensed (Apache-2.0) Rust crate for reading DWG, implemented from the ODA specification without linking against the ODA SDK and without reusing GPL-licensed source code. It is useful when your stack can't take GPL-3, can't justify an ODA membership, and needs a Rust dependency rather than an FFI binding.

It is pre-alpha and not a finished DWG reader. See CLEANROOM.md for the implementation discipline this project follows, including the honest scope of what "clean-room" means for a solo-developer project: a spec-only, no-reference-source posture, not a formal two-team protocol.

Contributing

New here? CONTRIBUTING.md opens with a ten-minute quickstart: clone, run the exact gate CI runs, pick a good first issue (each names the spec section, the file and the check that proves it), open a PR. Coverage numbers in this README come from the Corpus (public DWG) CI job and are never estimated; tools/corpus/baseline.tsv holds every drawing's row.

The project needs help, in rough order of impact:

  1. Drawings. Public pre-R2007 drawings with non-ASCII text in code pages other than 1251 and 1252 (#375), an AutoCAD-written R13 drawing (#345), and controlled AutoCAD drawings for the classes the spec never prints (#348), each with AutoCAD's own DXF export.
  2. Values the oracle cannot score yet. Types listed as unscored in the oracle summary need a scorer before their values count as proven.
  3. Python bindings, planned in docs/python.md.
  4. Fuzzing. cargo-fuzz harnesses live in fuzz/; more targets and longer runs are welcome.

Before submitting a PR:

  • Run cargo fmt --all, cargo clippy --all-targets --all-features -- -D warnings and tools/ci/run_test_targets.sh --release --all-features. Unit tests are banned: do not add one (CONTRIBUTING.md).
  • Cite the ODA spec section for any new decoder behavior, numbered as in v5.4.1 (docs/spec-coverage.md lists them).
  • Source-provenance declaration: confirm in the PR body that your contribution does not incorporate executable code (not just comments, not just API shapes) from any source whose license is incompatible with Apache-2.0: no Autodesk SDK source, no ODA SDK / Teigha source, and no GPL-licensed DWG implementation source (LibreDWG). Reading algorithm-description comments in permissively licensed projects (MIT / Apache / BSD) to resolve a spec ambiguity is allowed and must be disclosed in the PR body so it can be recorded in CLEANROOM.md. The PR template has a checkbox for this.
  • Follow the Contributor Covenant 2.1 code of conduct.

Security vulnerabilities: report privately via GitHub Security Advisories; see SECURITY.md.

Legal posture

"Autodesk", "AutoCAD", and "DWG" are trademarks of Autodesk, Inc. This crate is not affiliated with, authorized by, or endorsed by Autodesk.

dwg-rs is a spec-based interoperability implementation. The authoritative reference is the Open Design Alliance's freely redistributable Open Design Specification for .dwg files (v5.4.1), a document distinct from the ODA's Drawings SDK (Teigha) license. Executable code from the Autodesk SDK, the ODA SDK, and GPL-licensed DWG implementations (LibreDWG) was not consulted or imported at any point. The scoped exceptions, algorithm-description comments (never executable code) in permissively licensed projects, are each recorded with a date in CLEANROOM.md.

Independent reverse engineering for interoperability is generally supported across jurisdictions by authorities such as Sega v. Accolade (9th Cir. 1992) and Sony v. Connectix (9th Cir. 2000) in the United States, Article 6 of the EU Software Directive (2009/24/EC), and comparable provisions elsewhere. See NOTICE for a fuller reference set. Nothing in this repository is offered as legal advice; users with specific legal constraints should consult their own counsel.

License

Apache-2.0. See LICENSE. Contributions land under the same terms per the standard inbound = outbound convention.

About

Apache-2.0 Rust reader for Autodesk DWG files (R13 → R2018 / AC1032), built from the Open Design Alliance's published specification. Pre-alpha.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

9 stars

Watchers

0 watching

Forks

Releases

Sponsor this project

Packages

Used by

Contributors

Languages