diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 00000000..7ec6f88e --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,6 @@ +# getrandom 0.3 (pulled by ahash via acadrust) needs this cfg to select its +# browser backend on wasm32-unknown-unknown, alongside its `wasm_js` feature +# (enabled in Cargo.toml for the wasm target). Without it the web build fails +# with "wasm32-unknown-unknown targets are not supported by default". +[target.wasm32-unknown-unknown] +rustflags = ['--cfg', 'getrandom_backend="wasm_js"'] diff --git a/Cargo.lock b/Cargo.lock index ef2d9792..15b165e5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -24,6 +24,7 @@ dependencies = [ "env_logger", "flate2", "fontdb", + "getrandom 0.3.4", "glam 0.33.0", "iced", "image", diff --git a/Cargo.toml b/Cargo.toml index 8a14631d..1ed05e10 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,7 +24,9 @@ solid3d = ["dep:truck-meshalgo", "dep:truck-shapeops", "dep:lzma-sys"] [dependencies] # Stable, dependency-free add-on contract (manifest + ribbon/CadModule types). # Plugin authors target this crate's semver, not OpenCADStudio internals. -ocs_plugin_api = { path = "crates/ocs_plugin_api", features = ["host"] } +# The `host` feature (out-of-process plugin runtime: interprocess, libloading, +# memmap2, rkyv) is native-only — see the per-target sections below; the web +# build gets the dependency-free manifest/ribbon contract. # vtkio (pulled in transitively via truck-meshalgo) depends on xz2 → lzma-sys, # which by default links the system liblzma dynamically. On macOS that bakes a # Homebrew dylib path (/opt/homebrew/.../liblzma.5.dylib) into the binary, so @@ -44,7 +46,6 @@ clap = { version = "4", features = ["derive"] } # Opt-in logging via --log / RUST_LOG (surfaces wgpu / iced / winit diagnostics). env_logger = "0.11" acadrust = "0.3.4" -printpdf = "0.9.1" flate2 = "1" image = { version = "0.25", default-features = false, features = ["png", "jpeg", "bmp", "tiff"] } inventory = "0.3" @@ -80,14 +81,25 @@ windows-sys = { version = "0.61", features = ["Win32_UI_Shell", "Win32_UI_Window acadrust = { git = "https://github.com/HakanSeven12/acadrust", branch = "main" } [target.'cfg(not(target_arch = "wasm32"))'.dependencies] +# Native enables the plugin host runtime (out-of-process plugins). +ocs_plugin_api = { path = "crates/ocs_plugin_api", features = ["host"] } rayon = "1" open = "5" +# PDF export / printing. Native-only: printpdf pulls a wasm-incompatible +# `memchr` (1.0.2) via lopdf → nom_locate, and the web build has no filesystem. +printpdf = "0.9.1" ureq = { version = "3", default-features = false, features = ["rustls"] } # Parse the GitHub Releases API response for the plugin marketplace. serde_json = "1" [target.'cfg(target_arch = "wasm32")'.dependencies] +# Web gets the dependency-free manifest/ribbon contract only (no plugin host). +ocs_plugin_api = { path = "crates/ocs_plugin_api" } console_error_panic_hook = "0.1" +# ahash (via acadrust) pulls getrandom 0.3, which needs the `wasm_js` backend +# enabled to support wasm32-unknown-unknown (plus the matching rustflag in +# .cargo/config.toml). +getrandom = { version = "0.3", features = ["wasm_js"] } wasm-bindgen = "0.2" # fetch() returns a JS Promise; bridge it to a Rust future for the per-script # web font loader (#141). diff --git a/src/io/pdf_export.rs b/src/io/pdf_export.rs index d1dcffef..a5437c0c 100644 --- a/src/io/pdf_export.rs +++ b/src/io/pdf_export.rs @@ -9,15 +9,43 @@ // drawing origin at the paper origin. use crate::io::plot_style::PlotStyleTable; -use crate::scene::model::hatch_model::{HatchModel, HatchPattern}; +use crate::scene::model::hatch_model::HatchModel; +#[cfg(not(target_arch = "wasm32"))] +use crate::scene::model::hatch_model::HatchPattern; use crate::scene::WireModel; +#[cfg(not(target_arch = "wasm32"))] use printpdf::{ Color, Line, LineCapStyle, LineJoinStyle, LinePoint, Mm, Op, PaintMode, PdfDocument, PdfPage, PdfSaveOptions, Point, Polygon, PolygonRing, Pt, Rgb, WindingOrder, }; +#[cfg(not(target_arch = "wasm32"))] use std::io::Write; use std::path::Path; +// The web build has no `printpdf` (it pulls a wasm-incompatible `memchr` via +// lopdf → nom_locate) and no filesystem, so PDF export is native-only; the web +// build gets these stubs so the call sites still compile. +#[cfg(target_arch = "wasm32")] +pub fn export_pdf( + _wires: &[WireModel], + _hatches: &[HatchModel], + _wipeouts: &[HatchModel], + _paper_w: f64, + _paper_h: f64, + _offset_x: f32, + _offset_y: f32, + _rotation_deg: i32, + _path: &Path, + _plot_style: Option<&PlotStyleTable>, +) -> Result<(), String> { + Err("PDF export is not available in the web version.".into()) +} + +#[cfg(target_arch = "wasm32")] +pub async fn pick_pdf_path_owned(_stem: String) -> Option { + None +} + // ── Public entry point ──────────────────────────────────────────────────── /// Export `wires` to a PDF file. @@ -26,6 +54,7 @@ use std::path::Path; /// - `offset_x` / `offset_y`: added to every wire coordinate so the drawing /// origin maps to the bottom-left corner of the page. /// - `rotation_deg`: 0 | 90 | 180 | 270 — rotates the entire drawing on the page. +#[cfg(not(target_arch = "wasm32"))] pub fn export_pdf( wires: &[WireModel], hatches: &[HatchModel], @@ -54,6 +83,7 @@ pub fn export_pdf( } /// Show a PDF save-file dialog and return the chosen path (or None if cancelled). +#[cfg(not(target_arch = "wasm32"))] pub async fn pick_pdf_path_owned(stem: String) -> Option { rfd::AsyncFileDialog::new() .set_title("Export as PDF") @@ -67,6 +97,7 @@ pub async fn pick_pdf_path_owned(stem: String) -> Option { // ── PDF builder ─────────────────────────────────────────────────────────── +#[cfg(not(target_arch = "wasm32"))] fn build_pdf( wires: &[WireModel], hatches: &[HatchModel], @@ -235,6 +266,7 @@ fn build_pdf( doc.save(&PdfSaveOptions::default(), &mut warnings) } +#[cfg(not(target_arch = "wasm32"))] fn flush_line(ops: &mut Vec, pts: &[LinePoint]) { if pts.len() < 2 { return; @@ -252,6 +284,7 @@ fn flush_line(ops: &mut Vec, pts: &[LinePoint]) { /// rings so islands and holes render correctly under the even-odd rule. /// Mirrors `scene::paper_canvas::draw_hatch`: solid → fill, pattern → outline, /// gradient → solid fill of the averaged colour. +#[cfg(not(target_arch = "wasm32"))] fn emit_hatch(ops: &mut Vec, hatch: &HatchModel, ox: f32, oy: f32) { if hatch.boundary.is_empty() { return; diff --git a/src/io/print_to_printer.rs b/src/io/print_to_printer.rs index 4ab4abf1..2e0f9524 100644 --- a/src/io/print_to_printer.rs +++ b/src/io/print_to_printer.rs @@ -7,15 +7,34 @@ // // The function is async so the UI remains responsive while the job is queued. +#[cfg(not(target_arch = "wasm32"))] use crate::io::pdf_export; use crate::io::plot_style::PlotStyleTable; use crate::scene::model::hatch_model::HatchModel; use crate::scene::WireModel; +// Printing routes through the native PDF pipeline + the OS print command, so it +// is native-only; the web build gets a stub so the call site still compiles. +#[cfg(target_arch = "wasm32")] +pub async fn print_wires( + _wires: Vec, + _hatches: Vec, + _wipeouts: Vec, + _paper_w: f64, + _paper_h: f64, + _offset_x: f32, + _offset_y: f32, + _rotation_deg: i32, + _plot_style: Option, +) -> Result { + Err("Printing is not available in the web version.".into()) +} + /// Render `wires` (plus hatch / wipeout fills) to a temp PDF and dispatch it /// to the default system printer. /// /// Returns `Ok(printer_name)` on success or `Err(message)` on failure. +#[cfg(not(target_arch = "wasm32"))] pub async fn print_wires( wires: Vec, hatches: Vec, @@ -47,6 +66,7 @@ pub async fn print_wires( } /// Platform-specific dispatch of a PDF path to the system printer. +#[cfg(not(target_arch = "wasm32"))] fn dispatch_to_printer(path: &std::path::Path) -> Result { #[cfg(target_os = "windows")] {