← All docs

WebAssembly

DynDyn.Pdf's fully-managed engine — the same DynDyn.Pdf.Core / DynDyn.Pdf.Engine assemblies that back the CLI, the library, and the server — also runs compiled to WebAssembly, inside a browser tab. A PDF dropped into the page is converted to HTML entirely client-side. The file is never uploaded.

This is the same conversion, not a lookalike: no separate code path, no reduced feature set beyond what a browser sandbox mechanically cannot do (see What it cannot do, below). A document converted in the browser and the same document converted by the CLI or the server, with the same options, produce the same HTML.

How it's loaded

The browser build (DynDyn.Pdf.Wasm) is a headless Blazor WebAssembly host — it renders no UI. It exposes exactly one JS-callable entry point:

DotNet.invokeMethodAsync('DynDyn.Pdf.Wasm', 'ConvertAsync', bytes, request)

where bytes is the PDF as a Uint8Array and request is a plain object matching the same option contract described below. The bundle is loaded as a normal <script> tag (wasm/_framework/blazor.webassembly.js, autostart="false") and started with Blazor.start({ loadBootResource }); there is deliberately no Blazor JS module initializer, since one would resolve its imports against the host page's URL rather than the bundle's own /wasm/ path and hang the boot promise.

Because DynDyn.Pdf.Wasm is not referenced by the server project (a ProjectReference would force every build of DynDyn.Pdf.Web to acquire the wasm-tools SDK workload) and is not part of the main solution, it is published separately and copied into the server's wwwroot/wasm/ as a build artifact:

pwsh scripts/publish-wasm.ps1

A deployment that never runs this script simply doesn't have wasm/_framework/blazor.boot.json present, and the demo page falls back to server-side conversion only.

Boot cost

The bundle is several megabytes of brotli-compressed WebAssembly + IL (the current published build totals roughly 6 MB across _framework/*.br before decompression) — a one-time download per tab, cached by the browser afterward. This is the tradeoff for zero-upload conversion: bandwidth and boot latency instead of a network round trip per document. The demo page's own boot timeout is generous (180 seconds) precisely because that first download can be slow on a cold cache.

Options

The browser entry point accepts the same option surface as the server upload and the CLI — pages, line grouping, semantic tags, decorative-text detection, style extraction, DPI, raster fallback, password, font-embedding policy, and the DoS-boundary limits (MaxPages, MaxPagePixels, MaxEstimatedModelBytes, timeout) — all as strings, mirroring how a multipart form field arrives, so "where the conversion runs" never changes "what settings it runs with". Unset/blank/unparsable values fall back to the same ConversionOptions defaults every other entry point uses.

Two fields are fixed by the browser environment rather than left to the caller:

What it can do

What it cannot do

Privacy

The whole point of this delivery form: the document never leaves the machine. No network request carries the PDF's bytes anywhere — conversion happens entirely inside the tab's WASM sandbox, and the resulting HTML is handed back to the calling page as an in-memory string (the demo wraps it in a Blob URL for preview/download). This is a genuine architectural property, not a policy promise: there is no code path in WasmConversionApi.ConvertAsync that performs a fetch or XHR with the input bytes.


Related: server.md documents the same options on the multipart-upload path when you do want server-side conversion; library.md documents the underlying ConversionOptions/ConversionResult types this entry point maps onto; security.md covers the DoS-boundary limits this entry point enforces more strictly than the library default (never lets a limit be disabled, only resized) because a browser tab has no operator to raise them back up.