DrunkOnJava/dwg-rs

GitHub: DrunkOnJava/dwg-rs

一个开源的 DWG 文件解析器,填补 Apache-2.0 下干净室实现的格式读取空白。

Stars: 0 | Forks: 0

# dwg-rs [![CI](https://static.pigsec.cn/wp-content/uploads/repos/2026/04/4c8ce1bbd7100609.svg)](https://github.com/DrunkOnJava/dwg-rs/actions/workflows/ci.yml) [![Crates.io](https://img.shields.io/crates/v/dwg.svg)](https://crates.io/crates/dwg) [![Documentation](https://docs.rs/dwg/badge.svg)](https://docs.rs/dwg) [![License: Apache-2.0](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](./LICENSE) [![Rust MSRV](https://img.shields.io/badge/rust-1.85%2B-orange.svg)](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, 矢量图形, 网络流量审计, 逆向工程, 通知系统