Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
2e31043
Name libclang, and how to install it, when bindgen cannot find it
iujunhyung Sep 23, 2026
1918e0c
Read a drawing from memory, and convert its strings by code page, in …
iujunhyung Sep 23, 2026
b00adb7
Patch five defects in the vendored LibreDWG, and say so everywhere
iujunhyung Sep 23, 2026
53b77cd
Ship the GPLv3 text inside every crate
iujunhyung Sep 23, 2026
6c6d0e6
Refuse to build when a vendored LibreDWG patch has gone missing
iujunhyung Sep 23, 2026
d8a0f30
Pin the licence files and the vendored-patch notice a release has to …
iujunhyung Sep 23, 2026
3142ba3
Record the native-layer changes in the changelog
iujunhyung Sep 23, 2026
6138351
Say what the submodule has of the vendored patches, not whether they …
iujunhyung Sep 23, 2026
95be3d4
Build against the model's new fields and the renderer's PNG size
iujunhyung Sep 23, 2026
1e10f22
Bring the hand-written DXF fixtures, and read every one of them
iujunhyung Sep 23, 2026
2cf2e31
Bound the walks over an entity's owned subentities
iujunhyung Sep 23, 2026
f64350a
Read every vertex of a POLYLINE, and no VERTEX as an entity
iujunhyung Sep 23, 2026
29547b1
Read a layer's state, lineweight and linetype
iujunhyung Sep 23, 2026
a7ff344
Read POLYLINE_MESH as its grid's wireframe
iujunhyung Sep 23, 2026
07dbd78
Carry the object coordinate system of every OCS entity
iujunhyung Sep 23, 2026
3700221
Carry a polyline's bulges, widths and constant width
iujunhyung Sep 23, 2026
9120c1e
Read the file in Rust and decode it from memory, so a non-ASCII path …
iujunhyung Sep 23, 2026
5f2a904
Return the drawing's header variables beside the model, as uncad's ow…
iujunhyung Sep 23, 2026
ccabb07
Decode the DOS-era Big5, GB2312 and CP932 code pages by their bytes
iujunhyung Sep 23, 2026
f821647
Read an R2007+ DXF's strings in the width they were stored in, and st…
iujunhyung Sep 23, 2026
73743e2
Read how a text is justified, stretched, slanted and styled
iujunhyung Sep 23, 2026
b1aeacc
Read an attribute's flags
iujunhyung Sep 23, 2026
c01f1e3
Retire the shim's code-page conversion, now that TextDecoder does it
iujunhyung Sep 23, 2026
892721d
Say what a large DXF costs to read, measured on this branch
iujunhyung Sep 23, 2026
941b6b8
Read an entity's true colour where the file states one, and only there
iujunhyung Sep 23, 2026
2eea5dd
Read the dimension-style variables the displayed text depends on
iujunhyung Sep 23, 2026
925b461
Read an ordinate dimension's axis, and a DXF angular dimension's grou…
iujunhyung Sep 23, 2026
e2c30c1
Report a dimension measurement of -1 as not stated
iujunhyung Sep 23, 2026
f80db83
Read an MLINE's scale
iujunhyung Sep 23, 2026
f46a7be
Read what a viewport shows: its view, state, number and frozen layers
iujunhyung Sep 23, 2026
67bafad
Read the drawing's layouts and their plot settings
iujunhyung Sep 23, 2026
4fed21a
Pin what a dimension carries against its twins and the corpus
iujunhyung Sep 23, 2026
00ccb9f
Pin the hidden-layers and hatched-viewport fixtures from the parser's…
iujunhyung Sep 23, 2026
bb1570a
Say what the table conversion reads now
iujunhyung Sep 23, 2026
21f35c8
Merge port/entities: the parser's entity and table layer
iujunhyung Sep 23, 2026
a04ee5f
Merge origin/main: upstream's polyline vertices, OCS fields and 3DFAC…
iujunhyung Sep 23, 2026
d52a84d
Pin what the parser reads on top of upstream's polyline and OCS reads
iujunhyung Sep 23, 2026
0c93c99
Changelog: say the local-patches entry once
iujunhyung Sep 23, 2026
a91ba46
Frame the CLI's picture with the renderer's Crop option
iujunhyung Sep 23, 2026
daf30f9
Add uncad-export, the crate the LLM/VLM package lives in, with its font
iujunhyung Sep 23, 2026
35950f0
Give the package its own polyline arithmetic
iujunhyung Sep 23, 2026
bc0c030
Read a text's codes into the string the package's records carry
iujunhyung Sep 23, 2026
ff2d330
Derive a dimension's value, its trust and its label in the export
iujunhyung Sep 23, 2026
ffcaebb
Frame the package's pictures: crop mode, padding, lattice and groups
iujunhyung Sep 23, 2026
82dd555
Write the package: overview, records, string index, report and manifest
iujunhyung Sep 23, 2026
2a7b8f0
Split the package into frames with a tile pyramid each
iujunhyung Sep 23, 2026
895b8e2
Draw every paper layout as a sheet, with its own texts as records
iujunhyung Sep 23, 2026
bf0c437
Write the LLM/VLM package from the command line: uncad export
iujunhyung Sep 23, 2026
b3376e8
Answer --version, add --include-hidden, and refuse what the CLI does …
iujunhyung Sep 23, 2026
9e33963
docs: say what uncad-export and `uncad export` are, and where they sit
iujunhyung Sep 23, 2026
1796a3a
docs: bring over the LLM/VLM package design
iujunhyung Sep 23, 2026
3941fc7
docs: state what the LLM/VLM package does not do yet
iujunhyung Sep 23, 2026
055ef9b
docs: record that the ported export is slower, with the measurement
iujunhyung Sep 23, 2026
70826b9
docs: record the whole-corpus comparison of the ported export
iujunhyung Sep 23, 2026
bfcc297
docs: hand the port over -- status, setup, what remains, PR drafts
iujunhyung Sep 23, 2026
e2aaec2
docs: address the handoff to upstream
iujunhyung Sep 23, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
* text=auto eol=lf
*.sh text eol=lf
# Hand-written DXF fixtures: keep their bytes (CRLF, and a code page
# that is not UTF-8) exactly as the generator wrote them
crates/uncad/tests/fixtures/*.dxf -text
211 changes: 189 additions & 22 deletions CHANGELOG.md

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
[workspace]
resolver = "2"
members = ["crates/libredwg-sys", "crates/uncad", "crates/uncad-cli"]
members = ["crates/libredwg-sys", "crates/uncad", "crates/uncad-export", "crates/uncad-cli"]

[workspace.package]
edition = "2021"
# Minimum supported Rust version, for all three crates. Measured, not guessed:
# Minimum supported Rust version, for all four crates. Measured, not guessed:
# 1.87 fails (`slice::as_chunks`, stable since 1.88, in crates/uncad) and 1.88
# passes. CI's `msrv` job reads this value and checks the workspace with
# exactly that toolchain -- raise the two together, never this line alone.
Expand Down
65 changes: 51 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
[![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](LICENSE)

An open-source Rust library that parses CAD files (DWG/DXF) into a model and
exports that model as JSON, SVG or PNG. Read-only: it does not write DWG or DXF,
exports that model as JSON, SVG or PNG -- and, through the `uncad-export` crate,
as a directory an LLM or VLM can read (overview and tile images, sheet images,
and JSON records that point into them). Read-only: it does not write DWG or DXF,
and does not convert between them.

## Quick start
Expand All @@ -19,12 +21,28 @@ cargo test --workspace
let db = uncad::parse("drawing.dwg")?; // same model for DWG and DXF (entities + tables)
println!("{} entities", db.entities.len());

// The header variables that give the numbers a meaning come beside the model:
let (db, header) = uncad::parse_with_header("drawing.dxf")?;
println!("units: {:?}", header.units()); // $INSUNITS; None when the file does not say

// Bytes already in memory (from the network, an archive, ...):
let bytes = std::fs::read("drawing.dwg")?;
let db = uncad::parse_bytes(&bytes, uncad::Format::Dwg)?;

let json = db.to_json(uncad::ToJsonOptions { pretty: true })?; // the model, serialized as-is
std::fs::write("drawing.json", json)?;

// Rendering is the iron-render-cad crate's (uncad-cli uses it):
let result = iron_render_cad::to_svg(&db, iron_render_cad::ToSvgOptions::default());
std::fs::write("drawing.svg", result.svg)?;

// The LLM/VLM package (the uncad-export crate; uncad-cli's `uncad export`):
let report = uncad_export::export_file(
std::path::Path::new("drawing.dwg"),
std::path::Path::new("drawing.pkg"),
&uncad_export::ExportOptions::default(),
)?;
println!("{} files, {} sheets", report.files.len(), report.sheets.len());
```

## CLI
Expand All @@ -38,18 +56,27 @@ cargo run -p uncad-cli -- drawing.dwg -o drawing.png --scale 2 # rasterize at
cargo run -p uncad-cli -- drawing.dwg -o drawing.svg --no-trim # keep outlying coordinates
cargo run -p uncad-cli -- drawing.dwg -o sheet.svg --space paper # sheet borders / title blocks
cargo run -p uncad-cli -- drawing.dwg -o all.svg --space all # every space in one document
cargo run -p uncad-cli -- drawing.dwg -o all.svg --include-hidden # also what layers hide
cargo run -p uncad-cli -- export drawing.dwg -o drawing.pkg # the LLM/VLM package
cargo run -p uncad-cli -- export drawing.dwg -o drawing.pkg --profile openai-patch --max-levels 2
cargo run -p uncad-cli -- --version
```

An option the CLI does not know is an error that names it, on both commands.
`uncad export` writes `manifest.json` last; start reading a package there (its
`README.txt` says the same in prose).

## Scope

1. **DWG** — read through [LibreDWG](https://www.gnu.org/software/libredwg/)
(GPLv3+), bound directly via Rust FFI (`bindgen`). All versions.
2. **DXF** — read through the same LibreDWG engine, chosen by file extension.
**DXF saved as R2007 or later is refused** with `ParseError::UnsupportedDxfVersion`
rather than read incompletely — see `docs/CAVEATS.md`, "DXF reading". R2000/R2004
DXF and every DWG version are unaffected.
LibreDWG's own DXF importer is documented as working "for most objects", so
it is less complete than its DWG reading ([`docs/CAVEATS.md`](./docs/CAVEATS.md)).
2. **DXF** — read through the same LibreDWG engine, chosen by file extension
(`parse`) or by the caller (`parse_bytes`). Every version, R2007 and later
included: such a file holds its strings in two widths in LibreDWG's memory,
and each is read in its own — see `docs/CAVEATS.md`, "DXF saved as R2007 or
later is read". LibreDWG's own DXF importer is documented as working "for most
objects", so it is less complete than its DWG reading, and slower than linear
in the entity count ([`docs/CAVEATS.md`](./docs/CAVEATS.md)).
3. **Output** — the parsed model as JSON (`CadDatabase::to_json`, from
`uncad-model`). SVG and PNG come from the
[`iron-render-cad`](https://github.com/iyulab/iron-render-cad) crate (MIT), which
Expand Down Expand Up @@ -81,8 +108,11 @@ well as Windows.
## License

**GPLv3-or-later**. LibreDWG (GPLv3+) is the only third-party component linked
in, and its license carries over. Copyright and license details for third-party
components are in [`docs/THIRD_PARTY_NOTICES.md`](./docs/THIRD_PARTY_NOTICES.md).
in, and its license carries over. The vendored copy carries five local patches;
`crates/libredwg-sys/NOTICE.md` is their modification notice, inside the crate
so that it reaches the published tarball. Copyright and license details for
third-party components are in
[`docs/THIRD_PARTY_NOTICES.md`](./docs/THIRD_PARTY_NOTICES.md).

## Repository layout

Expand All @@ -91,12 +121,17 @@ lib/libredwg/ LibreDWG upstream, as a git submodule. Not used by the
it is the source vendor/ is regenerated from, and where the
real-file test fixtures (test/test-data/) come from
crates/
libredwg-sys/ raw FFI (cc + bindgen). vendor/libredwg/ holds the unmodified
subset of C sources actually compiled (for publishing to
crates.io); shim/ holds the C accessors for opaque types, and
libredwg-sys/ raw FFI (cc + bindgen). vendor/libredwg/ holds the subset of C
sources actually compiled (for publishing to crates.io), with
five local patches marked "uncad local patch" and listed in
NOTICE.md; shim/ holds the C accessors for opaque types, and
vendor-config/config.h stands in for autotools
uncad/ the safe API: parse() -> uncad_model::CadDatabase
uncad-cli/ the CLI binary (uncad)
uncad/ the safe API: parse() / parse_bytes() -> uncad_model::CadDatabase,
and the drawing's Header beside it from parse_with_header()
uncad-export/ the LLM/VLM package: export_file() / export_package() write a
directory of images and JSON records; bundles a Noto Sans KR
subset (OFL-1.1) so Korean text renders anywhere
uncad-cli/ the CLI binary (uncad), including `uncad export`
crates/*/tests/ integration tests against the public API. crates/*/examples/ are
manual-check tools, and #[cfg(test)] blocks inside src/*.rs are
unit tests -- docs/ARCHITECTURE.md's "Test layout" says which
Expand All @@ -115,3 +150,5 @@ Further reading:
test layout, the FFI/bindgen boundary, thread safety, the entity model
- [`docs/CAVEATS.md`](./docs/CAVEATS.md) — entity type coverage, known
limitations and bugs, cross-platform notes
- [`docs/VLM_EXPORT_DESIGN.md`](./docs/VLM_EXPORT_DESIGN.md) — the LLM/VLM
package `uncad export` writes: its files, coordinates, tiling and crop rules
2 changes: 1 addition & 1 deletion crates/libredwg-sys/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ license.workspace = true
repository.workspace = true
homepage.workspace = true
readme.workspace = true
description = "Raw FFI bindings to the LibreDWG C sources (vendored, unmodified upstream), built natively via cc + bindgen (no Emscripten/WASM)."
description = "Raw FFI bindings to the LibreDWG C sources (a vendored subset of upstream carrying five local patches, see NOTICE.md), built natively via cc + bindgen (no Emscripten/WASM)."
keywords = ["dwg", "dxf", "ffi", "libredwg", "cad"]
categories = ["external-ffi-bindings"]
links = "libredwg"
Expand Down
Loading
Loading