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.
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).
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.
| 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 |
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.
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-dxfand 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:Classesdeclares - 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.
- 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.
# From git (currently the only distribution):
git clone https://github.com/DrunkOnJava/dwg-rs
cd dwg-rs
cargo build --releaseThe 0.1.0-alpha.1 crate has not been published to crates.io yet; publishing
is tracked in #109 and
RELEASE.md.
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/:
basic_open.rswalk_entities.rsextract_preview.rsdump_metadata.rscoverage_report.rs: run this against your own files to see how much of your data dwg-rs can parse today
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.dwgSee 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.
$ 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.
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.
Rust 1.85 (for edition = "2024"). MSRV bumps are minor-version events announced in
the CHANGELOG. CI verifies MSRV on every PR.
| 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 |
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-rsis 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.
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:
- 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.
- Values the oracle cannot score yet. Types listed as unscored in the oracle summary need a scorer before their values count as proven.
- Python bindings, planned in
docs/python.md. - 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 warningsandtools/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.mdlists 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.
"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.
Apache-2.0. See LICENSE. Contributions land under the same terms per the standard inbound = outbound convention.