# djvu-rs WASM demo

Browser-based DjVu viewer powered by djvu-rs compiled to WebAssembly.

## Build

```sh
# Install wasm-pack if you haven't already:
curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh

# From the repo root — builds pkg/ inside this directory:
wasm-pack build --target web --out-dir examples/wasm/pkg

# Serve locally (any static file server works):
python3 -m http.server 8080 --directory examples/wasm
# Then open http://localhost:8080
```

## Lazy HTTP Range loading

`range_lazy.md` shows the `wasm32` integration shape for large remote books:
implement an `AsyncRead + AsyncSeek` reader that fetches `Range:
bytes=start-end` with `gloo::net::http::Request`, then pass it to
`djvu_rs::djvu_async::from_async_reader_lazy_local`.

## npm package

```sh
# Publish to npm (requires npm login):
wasm-pack build --target web --out-dir examples/wasm/pkg --release
cd examples/wasm/pkg
npm publish
```

## Threaded rendering (`wasm-threads`, opt-in, experimental)

`wasm-threads` layers a rayon Web Worker thread pool (via
[`wasm-bindgen-rayon`](https://docs.rs/wasm-bindgen-rayon)) over the existing
`parallel`-feature compositor/IDWT code paths, so they run multi-threaded in
the browser instead of single-threaded. It is **not** part of the default
`wasm` build and is **not MSRV/stable** — see `make wasm-threads-check` and the
WASM_THREADS entry in `PERF_EXPERIMENTS.md` for the full feasibility writeup,
including a measured result: it helped only marginally (or regressed sharply
on small/downscaled renders) on this codec's per-page workload, because
wasm Worker dispatch/sync overhead is much higher than native OS threads.
Treat this as infrastructure for further experiments, not a shipped win.

### One-time setup

```sh
rustup toolchain install nightly
rustup component add rust-src --toolchain nightly
rustup target add wasm32-unknown-unknown --toolchain nightly
```

### Build

```sh
# Quick compile check:
make wasm-threads-check

# Full wasm-pack build (produces initThreadPool() + worker glue):
RUSTFLAGS='-C target-feature=+atomics,+bulk-memory \
  -C link-arg=--shared-memory -C link-arg=--max-memory=1073741824 -C link-arg=--import-memory \
  -C link-arg=--export=__wasm_init_tls -C link-arg=--export=__tls_size \
  -C link-arg=--export=__tls_align -C link-arg=--export=__tls_base' \
RUSTUP_TOOLCHAIN=nightly \
wasm-pack build . --target web --release --out-dir examples/wasm/pkg-threads \
  -- -Z build-std=panic_abort,std --features wasm-threads
```

### Serve with COOP/COEP + directory-import fallback

The page needs `Cross-Origin-Opener-Policy: same-origin` and
`Cross-Origin-Embedder-Policy: require-corp` response headers (required for
`SharedArrayBuffer`/`initThreadPool` — without them it throws). Plain
`python3 -m http.server` can't send custom headers, and wasm-bindgen-rayon's
worker glue does a bundler-style bare-directory `import('../../..')` to reach
the main JS module, which plain browser module resolution can't follow either
— so also copy `djvu_rs.js` to `pkg-threads/index.js` as a fallback target and
serve directory URLs with an `index.js`-serving static server, e.g.:

```py
# minimal COOP/COEP + index.js-fallback server — see the snippet used to
# validate this in the WASM_THREADS journal entry for a drop-in version.
```

### JS usage

```js
import init, { WasmDocument, initThreadPool } from './pkg-threads/djvu_rs.js';
await init();
await initThreadPool(navigator.hardwareConcurrency); // spins up the Worker pool
const doc = WasmDocument.from_bytes(new Uint8Array(await (await fetch('book.djvu')).arrayBuffer()));
const pixels = doc.page(0).render(150);
```
