W/jsq browser DWG bridge source, 2026-09-18 # Browser DWG bridge This W/jsq-owned bridge uses the unchanged acadrust 0.5.5 source at commit a738c70c27b5d47c562ea9ea8dd7cccf03289500. Cargo.lock pins dependencies. It never accesses files or network while converting a drawing. The bridge adapts the library's common dimension flag immediately before DWG writing: DWG bit 0 is the inverse of DXF group 70 bit 128; DWG bit 1 corresponds to DXF bit 32 (private anonymous picture). The upstream writer uses the DXF polarity, which discards user-positioned dimension text. Keep the inversion at this writer boundary; do not emit incorrect DXF flags or change original manufacturing geometry. Reference: ODA DWG specification 5.4.1 section 20.4.22, page 125: https://www.opendesign.com/files/guestdownloads/OpenDesign_Specification_for_.dwg_files.pdf Build with Rust and the wasm32-wasip1 target: cargo build --locked --release --target wasm32-wasip1 --manifest-path scripts/dwg-wasm/Cargo.toml The artifact is target/wasm32-wasip1/release/muxin_dwg.wasm (under CARGO_TARGET_DIR when set). After verification, copy it to assets/vendor/acadrust/huahui-dwg.wasm; the existing URL is retained for compatibility. This updates local assets only and does not deploy. Verified on this workstation using the existing WSL HalouERP toolchain; RUSTUP_HOME=/tmp/huahui-dwg-build/rustup, CARGO_HOME=/tmp/huahui-dwg-build/cargo, CARGO_TARGET_DIR=/tmp/muxin-dimension-wasm-target. The build was offline. No HUAHUI source or binary was changed. Regression: node tests/dwg-dimension-regeneration-browser.cjs then python -X utf8 tests/verify-dwg-autocad.py OUTPUT_DIRECTORY 96-172-left 96-172-right 135-left 125-145-right l-left l-right t-right. AutoCAD 2025 and ezdxf are needed for native re-open, full block rebuild, grip-equivalent point editing and AUDIT checks. tests/render-dwg-dimension-proof.py DWG generates native PDF/PNG before and after regeneration in a temporary directory without saving over the input. When rebuilding, update assets/vendor/acadrust/muxin-bridge-source.txt with Cargo.toml, Cargo.lock and src/lib.rs so the served bridge source matches the binary. ===== Cargo.toml ===== [package] name = "muxin-dwg" version = "1.0.0" edition = "2021" publish = false [lib] crate-type = ["cdylib"] [dependencies] acadrust = { git = "https://github.com/hakanaktt/acadrust", rev = "a738c70c27b5d47c562ea9ea8dd7cccf03289500" } [profile.release] opt-level = "s" lto = true codegen-units = 1 strip = true panic = "abort" ===== Cargo.lock ===== # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 4 [[package]] name = "acadrust" version = "0.5.5" source = "git+https://github.com/hakanaktt/acadrust?rev=a738c70c27b5d47c562ea9ea8dd7cccf03289500#a738c70c27b5d47c562ea9ea8dd7cccf03289500" dependencies = [ "ahash", "anyhow", "bitflags", "byteorder", "encoding_rs", "flate2", "indexmap", "itoa", "memmap2", "nom", "once_cell", "rayon", "ryu", "thiserror", "web-time", ] [[package]] name = "adler2" version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" [[package]] name = "ahash" version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" dependencies = [ "cfg-if", "getrandom", "once_cell", "serde", "version_check", "zerocopy", ] [[package]] name = "anyhow" version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470" [[package]] name = "bitflags" version = "2.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ded4057c258ba199e2d26386d3af3780957ecaee6c4ef4041c6b4b8b97c0b06" [[package]] name = "bumpalo" version = "3.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" [[package]] name = "byteorder" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "cfg-if" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "core_detect" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f8f80099a98041a3d1622845c271458a2d73e688351bf3cb999266764b81d48" [[package]] name = "crc32fast" version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8498c871161e1742aaa9d52551b2d6ebdd4c3d45a3be423e3728f33b955be550" dependencies = [ "cfg-if", ] [[package]] name = "crossbeam-deque" version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "622f3fc73690be383c7214310406f28a90e6edeadc3cea882f9d71e495b9711a" dependencies = [ "crossbeam-epoch", "crossbeam-utils", ] [[package]] name = "crossbeam-epoch" version = "0.9.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc74980687109a3b14c72fd458107bf0baa1da1a1a805e178d15501ba9b86d9d" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-utils" version = "0.8.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a31eee39dddec8330830986fcd7625edb5a24ec90ea038215273bbc3adb08ac6" [[package]] name = "either" version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "252afb9ae5eaa683babdc6a068b3f5726eb19e05070c731f9b2a23a7c3e8ed34" [[package]] name = "encoding_rs" version = "0.8.41" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b5ef0006ac9ab233c38522f5ae99cae3625151de8f706cacee1cba4b8e2832a" dependencies = [ "cfg-if", "core_detect", "multiversion", "multiversion_no_op", "rustversion", "scopeguard", "simdutf8", ] [[package]] name = "equivalent" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "flate2" version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e634e2e0ebac1ee034020da1ca582e17ffe4e0f5e985823721e168928136dcb" dependencies = [ "crc32fast", "miniz_oxide", "zlib-rs", ] [[package]] name = "futures-core" version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e" [[package]] name = "futures-task" version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd417de3d1d015fc3bfd2b1ea46dfc7bab72ef86f1cc7cc9c78e728b34a6d1fd" [[package]] name = "futures-util" version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d50a92467f8ba5dd6e3ee5d4bd04d73ab2e4e1c44474a0674821dfce14b79bc" dependencies = [ "futures-core", "futures-task", "pin-project-lite", "slab", ] [[package]] name = "getrandom" version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", "libc", "r-efi", "wasip2", ] [[package]] name = "hashbrown" version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" [[package]] name = "indexmap" version = "2.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cc4e190f5d26ca7051642629da2c52fc03bde85a03197c99408dcd291734c855" dependencies = [ "equivalent", "hashbrown", ] [[package]] name = "itoa" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "js-sys" version = "0.3.105" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce57d20d1ea864ce2ac172ab472d409214f4fd359f0b2a2775abdf522e2af99e" dependencies = [ "cfg-if", "futures-util", "wasm-bindgen", ] [[package]] name = "libc" version = "0.2.189" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" [[package]] name = "memchr" version = "2.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" [[package]] name = "memmap2" version = "0.9.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d1219ed1b7f229ee7104d281dd01d6802fe28bb6e95d292942c4daacdeb798c0" dependencies = [ "libc", ] [[package]] name = "minimal-lexical" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b63fbc4a50860e98e7b2aa7804ded1db5cbc3aff9193adaff57a6931bf7c4b4c" dependencies = [ "adler2", "simd-adler32", ] [[package]] name = "multiversion" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4ca4bea16ffc3f443cf7d866912118196bfef4c6a1556ca00f9f9b00bb43f7c" dependencies = [ "multiversion-macros", ] [[package]] name = "multiversion-macros" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d416831a7317ef4b08bee00b69cbbb9c8763da7959a7026244d6266869f9c83" dependencies = [ "proc-macro2", "quote", "rustversion", "syn 3.0.5", ] [[package]] name = "multiversion_no_op" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "743fb55ba31b18fb1ecef6bdc9aa2743314978ac084044301a7eee33fb99a20d" [[package]] name = "muxin-dwg" version = "1.0.0" dependencies = [ "acadrust", ] [[package]] name = "nom" version = "7.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" dependencies = [ "memchr", "minimal-lexical", ] [[package]] name = "once_cell" version = "1.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" [[package]] name = "pin-project-lite" version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" [[package]] name = "proc-macro2" version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.47" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" dependencies = [ "proc-macro2", ] [[package]] name = "r-efi" version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" [[package]] name = "rayon" version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb39b166781f92d482534ef4b4b1b2568f42613b53e5b6c160e24cfbfa30926d" dependencies = [ "either", "rayon-core", ] [[package]] name = "rayon-core" version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" dependencies = [ "crossbeam-deque", "crossbeam-utils", ] [[package]] name = "rustversion" version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" [[package]] name = "ryu" version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" [[package]] name = "scopeguard" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "serde" version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba" dependencies = [ "serde_core", ] [[package]] name = "serde_core" version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" dependencies = [ "proc-macro2", "quote", "syn 3.0.5", ] [[package]] name = "simd-adler32" version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a219298ac11a56ea9a6d2120044824d6f01aeb034955e7af7bc16858527deea" [[package]] name = "simdutf8" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" [[package]] name = "slab" version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" [[package]] name = "syn" version = "2.0.119" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "syn" version = "3.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "12df2e0110f65b775f769bb17ef989067a1d931b2eb822bd4346631eeada89f9" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "thiserror" version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", "syn 2.0.119", ] [[package]] name = "unicode-ident" version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" [[package]] name = "version_check" version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "wasip2" version = "1.0.4+wasi-0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487" dependencies = [ "wit-bindgen", ] [[package]] name = "wasm-bindgen" version = "0.2.128" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aecb87a33d3b0c5e3b7aa46336eaf486cffafbd281b195e4c8b80d50df2351bf" dependencies = [ "cfg-if", "once_cell", "rustversion", "wasm-bindgen-macro", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" version = "0.2.128" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a690d511e3c1a8b3a55e33511e3c2c00c78415cd23650f32b808627f5696b9ed" dependencies = [ "quote", "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" version = "0.2.128" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "411e4887f0071ef2d2164a9d5fdf2d20efbef78fccd3a78b0c10a1dc5295e48a" dependencies = [ "bumpalo", "proc-macro2", "quote", "syn 3.0.5", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" version = "0.2.128" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81941cd78d0c92026c33e5e01312845a4cb1e9af3407f9134b100dd03144103e" dependencies = [ "unicode-ident", ] [[package]] name = "web-time" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" dependencies = [ "js-sys", "wasm-bindgen", ] [[package]] name = "wit-bindgen" version = "0.57.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" [[package]] name = "zerocopy" version = "0.8.57" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d35102a9f36d089ccae9e4c6802bc118be4487b80aaffc0ab4e0cf5ce92d2873" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" version = "0.8.57" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "146c01f5ab44258da43cf276c74a2763db2ff3969c9c652c3f2de07041d0b2bc" dependencies = [ "proc-macro2", "quote", "syn 2.0.119", ] [[package]] name = "zlib-rs" version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34b31d188d9d685a4f9c7b46d6e36631b07058d2cfe190267adce54dc230bf12" ===== src/lib.rs ===== // Browser-only DXF -> DWG bridge. No files or network are opened. use acadrust::{DxfReader, DwgWriter, entities::EntityType}; use std::{cell::RefCell, io::Cursor}; thread_local! { static OUTPUT: RefCell> = const { RefCell::new(Vec::new()) }; } #[no_mangle] pub extern "C" fn allocate(size: usize) -> *mut u8 { Box::into_raw(vec![0u8; size].into_boxed_slice()) as *mut u8 } #[no_mangle] pub unsafe extern "C" fn deallocate(ptr: *mut u8, size: usize) { drop(Box::from_raw(std::ptr::slice_from_raw_parts_mut(ptr, size))); } #[no_mangle] pub unsafe extern "C" fn convert(ptr: *const u8, size: usize) -> i32 { let data = std::slice::from_raw_parts(ptr, size).to_vec(); let result = DxfReader::from_reader(Cursor::new(data)) .and_then(|reader| reader.read()) .and_then(|mut document| { // acadrust a738c70 writes the user-positioned bit with the DXF // polarity. DWG common dimension flags bit 0 is its inverse; // bit 1 marks the private anonymous picture block. Adapt only at // this writer boundary, keeping input DXF semantics correct. // ODA DWG specification 5.4.1, section 20.4.22 (page 125). for entity in document.entities_mut() { if let EntityType::Dimension(dimension) = entity { let base = dimension.base_mut(); base.text_user_positioned = !base.text_user_positioned; if !base.block_name.is_empty() { base.dwg_flags_byte |= 0x02; } } } DwgWriter::write_to_vec(&document) }); match result { Ok(bytes) => { OUTPUT.with(|output| *output.borrow_mut() = bytes); 0 } Err(error) => { OUTPUT.with(|output| *output.borrow_mut() = error.to_string().into_bytes()); 1 } } } #[no_mangle] pub extern "C" fn output_ptr() -> *const u8 { OUTPUT.with(|output| output.borrow().as_ptr()) } #[no_mangle] pub extern "C" fn output_len() -> usize { OUTPUT.with(|output| output.borrow().len()) } #[no_mangle] pub extern "C" fn clear_output() { OUTPUT.with(|output| *output.borrow_mut() = Vec::new()); }