DrunkOnJava/dwg-rs
GitHub: DrunkOnJava/dwg-rs
一个开源的 DWG 文件解析器,填补 Apache-2.0 下干净室实现的格式读取空白。
Stars: 0 | Forks: 0
# dwg-rs
[](https://github.com/DrunkOnJava/dwg-rs/actions/workflows/ci.yml)
[](https://crates.io/crates/dwg)
[](https://docs.rs/dwg)
[](./LICENSE)
[](https://www.rust-lang.org/)
## ⚠ 预发布状态 — 请先阅读
这是 **0.1.0-alpha.1**。不要在生产环境中使用。不要将其与 ODA SDK 进行基准对比。不要告诉你的 CAD 团队 dwg-rs 今天已经解决了他们的互操作问题。
经验性实体解码覆盖率(依据
[`examples/coverage_report.rs`](./examples/coverage_report.rs) 对比
19 个文件的 `nextgis/dwg_samples` 语料库 + 一个 1 MB 的 AC1032 文件,在
尺寸子类型修复(任务 #71)之后:
| 版本 | 测试文件数 | 尝试解码实体数 | 解码成功数 | 错误数 | 成功率 |
|---------|--------------|--------------------|---------|---------|--------------|
| R14 / R2000 / R2007 | 7 | 0 | 0 | 0 | 不支持(尚未为该布局实现句柄映射) |
| R2004 (AC1018) | 3 | 每文件 7 个 | 0 | 7 | **0 %** |
| R2010 (AC1024) | 3 | 每文件 7 个 | 3 | 4 | **43 %** |
| R2013 (AC1027) | 3 | 每文件 7 个 | 6 | 1 | **86 %** |
| R2018 (AC1032) | 1 | 尝试 306 个(745 个对象中,其中 439 个是非实体控制/字典) | 66 | 240 | **22 %** |
| **总计** | **19** | **尝试 369 个实体** | **93** | **276** | **25 %** |
在 R2018 样本(大多数实际数据所在)中,按实体类型错误的集中度:
| 类型代码 | DXF 名称 | 作为错误出现的次数 |
|-----------|----------|-----------------------|
| 0x13 (19) | `LINE` | 82 |
| 0x2C (44) | `MTEXT` | 33 |
| 0x1B (27) | `POINT` | 26 |
| ... | (长尾) | 99 |
**解读:** [`src/entities/*.rs`](./src/entities/) 中的 27 个实体解码器
已通过手工构造的合成输入进行验证(193 个单元测试、proptest 和样本测试通过)
但在真实 DWG 文件上失败,因为生产 DWG 文件中的公共实体前导、扩展数据循环和
句柄流布局存在版本特定的偏差,而该 crate 尚未完全建模这些偏差。这是“解码函数存在”与
“解码器端到端可用”之间的差距。关闭这一差距是 0.2.0 版本的目标。
## 目前可用的功能
容器层非常稳固,并通过了 193 个测试:
- 跨 AC1014 (R14, 1997) → AC1032 (2018, 2024+) 的版本识别
- R13–R15 简单文件头 + R2004+ XOR 加密头
- 段页映射(Section Page Map)与段信息(Section Info)解析
- LZ77 解压缩(通过 ACadSharp 验证 +1 偏移方言)
- 每个 R2004 系列版本的 Sec_Mask 层 1 去屏蔽
- `DwgFile::read_section(name)` — 任意命名段的解压字节
- CRC-8 + CRC-32 校验
- Reed-Solomon(255,239) GF(256) FEC 解码器(防御性路径)
- 元数据解析器:`SummaryInfo`、`AppInfo`(R18 ANSI + R21+ UTF-16 自动检测)、
`Preview`(BMP、WMF、现代 PNG code-6 回退)、`FileDepList`
- 句柄映射解析器、类映射解析器、位流提取器
- 对象流遍历器:`all_objects()` 返回 `Vec`,包含类型代码、
句柄和原始负载字节 — 该部分在 R2018 上运行良好(清晰枚举了 745 个对象),
并为你提供了足够的基础来构建适用于任意版本的实体调度器(若你比 0.2.0 更早需要)
- 部分写入路径:位写入器 + 仅字面量的 LZ77 编码器 + 每段帧(Sec_Mask 与 CRC);文件级
`WriterScaffold` 脚手架处于阶段 1,共 5 个阶段
### 硬不支持的列表 — dwg-rs 目前不做什么
- 大多数真实 R2004 系列文件的端到端实体解码(见上方覆盖率表)。
- R14 / R2000 对象流遍历(布局与 R2004 系列不同;尚未实现)。
- R2007 段负载(Sec_Mask 层 1 已提供,层 2 位旋转脚手架已搭建)。
- 完整的 HATCH 边界路径树、完整的 MLEADER 引导线列表、完整的 75 字段 DIMSTYLE。
- `DwgFile::to_bytes()` — 脚手架已搭建,阶段 2–5(页映射 + 段信息 + 系统页 + 文件打开头重建)为未来版本。
## 安装
```
# 从 git(目前唯一的发行版):
git clone https://github.com/DrunkOnJava/dwg-rs
cd dwg-rs
cargo build --release
```
0.1.0-alpha.1 的 crate 尚未发布到 crates.io,直到实体解码覆盖率达到负责任的基线。
## 使用可用的组件
```
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 — this is fully reliable.
if let Some(Ok(bytes)) = file.read_section("AcDb:Preview") {
println!("preview section: {} bytes", bytes.len());
}
// Structured metadata — works on every corpus file we've tested.
if let Some(Ok(summary)) = file.summary_info() {
println!("title: {}", summary.title);
println!("author: {}", summary.author);
}
// Handle-indexed object walk — works on R2004+ but skips R14/R2000/R2007.
if let Some(Ok(objects)) = file.all_objects() {
println!("raw objects: {}", objects.len());
}
// End-to-end entity decode — alpha quality; check the returned
// DispatchSummary's decoded_ratio() for honest per-file coverage
// 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(())
}
```
其他示例位于 [`examples/`](./examples/):
- [`basic_open.rs`](./examples/basic_open.rs)
- [`walk_entities.rs`](./examples/walk_entities.rs)
- [`extract_preview.rs`](./examples/extract_preview.rs)
- [`dump_metadata.rs`](./examples/dump_metadata.rs)
- [`coverage_report.rs`](./examples/coverage_report.rs) — 对你自己的文件运行此命令,查看 dwg-rs 目前能实际解析多少数据
## CLI 工具
```
dwg-info drawing.dwg # version + 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 # decompressed section
dwg-convert --verify drawing.dwg # all-sections decompress check
```
## 架构
请参阅 [`ARCHITECTURE.md`](./ARCHITECTURE.md) 获取深入的设计说明 — 格式入门、
模块职责、四阶段读取管线、Sec_Mask 解释,以及 LZ77 规范误差修正。
快速层概览:
```
DwgFile::open ─────────────────────────────────────────┐
│ │
▼ ▼
header + section_map (R2004+ only) handle_map
│ │
▼ ▼
read_section("AcDb:*") all_objects() ──► [shipping]
│ │
▼ ▼
metadata::* (SummaryInfo, decoded_entities() ──► [alpha]
AppInfo, Preview, FileDepList) │
▼
dispatch on type_code → entities::*
│
▼
per-entity struct
```
## 验证
```
$ cargo test --release
156 unit tests + 5 corpus + 9 proptest + 22 samples + 1 doctest = 193 tests, 0 failures
$ 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 # no advisories, no disallowed licenses
```
测试对容器层进行端到端遍历,涵盖全部 19 个语料库文件,并验证每个原语的位级往返属性。
它们**不**验证每个实体解码器在所有真实图纸上是否成功 — 这正是上述 22% / 43% /
85% 覆盖率数字所衡量的内容。这两类测试都是必需的。
## 安全性
整个 crate 均为 `#![deny(unsafe_code)]`。Reed-Solomon、LZ77、GF(256)、位光标、
位写入器均为安全的 Rust。每个解析器返回 `Result`。防御性上限限制了
失控的分配(1 M 字典项、16 MB XRECORD、1 M 样条控制点)。请参阅
[`SECURITY.md`](./SECURITY.md) 了解威胁模型 + 私密报告。
## MSRV 策略
Rust 1.85(针对 `edition = "2024"`)。MSRV 的升级是小版本事件,会在
[变更日志](./CHANGELOG.md) 中宣布。CI 在每次 PR 上验证 MSRV。
## 相关项目
| 项目 | 语言 | 许可证 | 说明 |
|---------|----------|---------|-------|
| [ACadSharp](https://github.com/DomCR/ACadSharp) | C# | MIT | 宽容参考 — `dwg-rs` 与其交叉验证 LZ77 偏移错误(仅比对注释中的算法,而非其源码) |
| [LibreDWG](https://www.gnu.org/software/libredwg/) | C | **GPL-3** | 成熟但为 copyleft;未在构建本 crate 时咨询 |
| [Teigha / ODA SDK](https://www.opendesign.com/) | C++ | 商业 | 专有;需付费会员 |
| [dxf-rs](https://github.com/ixmilia/dxf-rs) | Rust | MIT | 仅 DXF(文本配套格式) |
## 存在的原因
DWG 格式已有约 28 年历史,一直是护城河。Autodesk 从未公开规范。ODA 的 SDK 需要会员资格并引入许可限制。LibreDWG 采用 GPL-3,不适合大多数商业堆栈。
`dwg-rs` 是第一个开源、Apache-2.0、干净室实现的 — 为 CAD 互操作性工具提供宽容许可的基础。它**不是**成品。它是一个其他人在不支付通行费或将其依赖图引入 GPL-3 的情况下可以构建的基础。
## 贡献
该项目需要帮助,按影响大致排序:
1. **每个版本的实体前导修复** — 查明为何 LINE 和 MTEXT 在 R2004/R2010/R2018 的真实文件中失败。这是“4% 总计”与“可发布”之间的最大差距。
2. **R14 / R2000 对象流遍历** — 与 R2004 系列完全不同的布局。
3. **R2007 Sec_Mask 层 2 记账** — 规范 §5.2。
4. **模糊测试目标** — 用于 LZ77 解压缩、位光标和对象遍历器的 cargo-fuzz 框架。
5. **写入路径阶段 2–5** — 页映射 / 段信息 / 系统页 / 文件打开头重建。
提交 PR 前:
- 运行 `cargo fmt --all`、`cargo clippy --all-targets -- -D warnings`、`cargo test`。
- 为任何新的解码器行为引用 ODA 规范章节。
- **干净室声明**:在 PR 正文确认你未查阅 Autodesk SDK 源码、ODA SDK 源码或 LibreDWG (GPL-3) 源码。PR 模板中有此复选框。
- 遵循 [Contributor Covenant 2.1](https://www.contributor-covenant.org/version/2/1/code_of_conduct/) 行为准则。
安全漏洞请私下通过 [GitHub Security Advisories](https://github.com/DrunkOnJava/dwg-rs/security/advisories/new) 报告 — 请参阅
[SECURITY.md](./SECURITY.md)。
## 法律立场
DWG 是 Autodesk, Inc. 的商标。本 crate 与 Autodesk 未经关联、未经授权或未经背书。它是一个干净室第三方实现,受:
- **17 U.S.C. § 1201(f)** — DMCA 反向工程用于互操作性的例外。
- **Autodesk, Inc. v. Open Design Alliance**, N.D. Cal. 2006(已和解)— 明确允许第三方 DWG 实现。
权威参考是 ODA 的可自由分发 *Open Design Specification for .dwg files* (v5.4.1) — 与 ODA SDK 许可不同的文档。
未咨询任何 Autodesk SDK、ODA SDK 或 LibreDWG 源码。
## 许可证
Apache-2.0。参见 [LICENSE](./LICENSE)。贡献在相同条款下生效,遵循标准“输入 = 输出”约定。
标签:AC1032, Apache 2.0, AutoCAD, CAD, clean-room, DWG, R13, R2018, Rust, 二维绘图, 云资产清单, 内存安全, 可视化界面, 图形格式, 威胁情报, 实体解码, 工程软件, 底层解析, 开发者工具, 开源, 开源库, 技术博客, 搜索引擎爬虫, 数据转换, 文件解析, 无SDK, 矢量图形, 网络流量审计, 逆向工程, 通知系统