800-page Medical Report — Web Worker Showcase
Generates a fictitious, fully synthetic medical report (anonymized data, deterministic seeded RNG) demonstrating how pdfnative scales to large documents off the main thread. Pages contain headers, footers, patient summaries, longitudinal vital signs tables, and laboratory results — typical real-world clinical document patterns.
Privacy: all patient names, IDs, dates, and values are generated client-side from a seeded random number generator. No real PHI is ever sent over the network.
The 5 000- and 10 000-page presets exceed the default 100 000-block safety rail, so the playground passes layout.maxBlocks (a pdfnative ≥ 1.3.0 option). On older versions the call falls back to the built-in cap and reports a friendly error.
What this showcase demonstrates
- Web Worker offload — UI stays responsive while the worker generates 800 pages. Toggle the checkbox to compare with main-thread generation.
- Streaming output —
buildDocumentPDFStreamyields chunks as the document is built; the worker forwards them to the main thread for progressive aggregation. - Long-form pagination — heading/paragraph/table block flow with automatic page breaks, headers, and footers.
- Synthetic data at scale — seeded RNG produces deterministic, anonymized patient cohorts, vital signs, and lab panels.
- Tagged PDF (optional) — enable PDF/A-2b output with full structure tree.
Code structure
// Main thread
const worker = new Worker(workerBlobUrl, { type: 'module' });
worker.onmessage = (e) => {
if (e.data.type === 'progress') updateProgress(e.data);
if (e.data.type === 'done') downloadPdf(e.data.bytes);
};
worker.postMessage({ pageCount: 800, tagged: true, seed: 42 });
// Inside the worker
import { buildDocumentPDFStream, concatChunks } from 'https://esm.sh/pdfnative';
const blocks = synthesizePatientReport(seed, pageCount);
const chunks = [];
for await (const chunk of buildDocumentPDFStream({ title, blocks }, {}, { chunkSize: 65536 })) {
chunks.push(chunk);
postMessage({ type: 'progress', bytes: chunks.reduce((s, c) => s + c.length, 0) });
}
postMessage({ type: 'done', bytes: concatChunks(chunks) }, [bytes.buffer]);