qpdf-run
v0.2.1
Published
Run qpdf WASM in the browser with a Uint8Array-first API.
Maintainers
Readme
qpdf-run
qpdf-run is a browser-only JavaScript wrapper for running qpdf WASM with a clean Uint8Array input/output API.
It solves the common browser integration problem around qpdf WASM: pass PDF bytes in, run normal qpdf command arguments, and get PDF bytes back without touching Emscripten FS.writeFile(), callMain(), or FS.readFile() directly.
The package boundary is intentionally narrow:
- run qpdf in the browser through a Web Worker
- accept
Uint8Array,ArrayBuffer, or typed-array inputs - run regular qpdf CLI-style arguments
- support multiple input and output files per command
- return output files as
Uint8Array - capture stdout, stderr, warnings, exit code, and duration
- clean temporary MEMFS files between runs
- keep PDF editing and document semantics out of this package
Current support:
- Browser: supported through a Web Worker and qpdf WASM
- Node.js: not supported in this release
Install
npm install qpdf-runThe package ships plain ESM JavaScript, TypeScript declarations, and the vendored qpdf WASM runtime.
It is useful when you want this:
const output = await qpdf.runOne({
input: pdfBytes,
inputName: 'input.pdf',
outputName: 'output.pdf',
args: ['--linearize', '--', 'input.pdf', 'output.pdf']
});instead of wiring qpdf WASM manually:
qpdf.FS.writeFile('/input.pdf', pdfBytes);
qpdf.callMain(['--linearize', '--', '/input.pdf', '/output.pdf']);
const output = qpdf.FS.readFile('/output.pdf');Included pieces:
src/index.js: public ESM entrypointsrc/browserRunner.js: browser runner APIsrc/worker.js: qpdf-run browser workersrc/index.d.ts: TypeScript declarationsvendor/qpdf/: vendored qpdf WASM runtimeexamples/browser/index.html: manual browser demoexamples/browser/smoke.html: automated browser smoke page
Bundler-safe asset subpaths:
qpdf-run/worker: browser worker scriptqpdf-run/qpdf.js: Emscripten qpdf JavaScript runtimeqpdf-run/qpdf.wasm: qpdf WASM binary
API
run()
import { createQpdfRunner } from 'qpdf-run';
const qpdf = await createQpdfRunner({
assetBaseUrl: '/qpdf/'
});
try {
const result = await qpdf.run({
inputs: {
'input.pdf': pdfBytes
},
args: [
'--qdf',
'--object-streams=disable',
'--decode-level=all',
'--',
'input.pdf',
'output.qdf.pdf'
],
outputs: ['output.qdf.pdf']
});
const outputBytes = result.outputs['output.qdf.pdf'];
} finally {
await qpdf.destroy();
}runOne()
For a single input and output:
const output = await qpdf.runOne({
input: pdfBytes,
inputName: 'input.pdf',
outputName: 'output.pdf',
args: ['--linearize', '--', 'input.pdf', 'output.pdf']
});Common Commands
Convert to QDF:
const qdf = await qpdf.runOne({
input: pdfBytes,
inputName: 'input.pdf',
outputName: 'output.qdf.pdf',
args: ['--qdf', '--object-streams=disable', '--decode-level=all', '--', 'input.pdf', 'output.qdf.pdf']
});Optimize for web:
const linearized = await qpdf.runOne({
input: pdfBytes,
inputName: 'input.pdf',
outputName: 'linearized.pdf',
args: ['--linearize', '--', 'input.pdf', 'linearized.pdf']
});Runner Options
createQpdfRunner() accepts:
assetBaseUrl: base URL forvendor/qpdf/; used to resolvelib/qpdf.jsandlib/qpdf.wasmqpdfJsUrl: explicit URL for the Emscripten qpdf JavaScript runtimewasmUrl: explicit URL forqpdf.wasmworkerUrl: explicit URL forsrc/worker.jstimeoutMs: worker request timeout; defaults to20000env: currently only'browser'
When a host app uses a bundler, prefer resolving explicit URLs through the package exports:
const workerUrl = new URL('qpdf-run/worker', import.meta.url).href;
const qpdfJsUrl = new URL('qpdf-run/qpdf.js', import.meta.url).href;
const wasmUrl = new URL('qpdf-run/qpdf.wasm', import.meta.url).href;
const qpdf = await createQpdfRunner({ workerUrl, qpdfJsUrl, wasmUrl });Results And Errors
run() resolves with:
type QpdfRunResult = {
ok: boolean;
outputs: Record<string, Uint8Array>;
stdout: string[];
stderr: string[];
warnings: string[];
exitCode: number | null;
durationMs: number;
};Failures throw QpdfRunError with one of:
QPDF_INIT_FAILEDQPDF_TIMEOUTQPDF_EXEC_FAILEDQPDF_OUTPUT_MISSINGQPDF_INVALID_INPUT
Browser Smoke
The automated browser smoke test starts a local static server, launches headless Chrome, runs qpdf twice, and verifies that a missing requested output throws QPDF_OUTPUT_MISSING.
npm run check
npm run smoke:browserIf Chrome is not available as google-chrome, set:
CHROME_BIN=/path/to/chrome npm run smoke:browserBrowser Demo
Start the static server:
npm run serveThen open:
http://127.0.0.1:8080/The root page redirects to the manual demo at /examples/browser/index.html. The demo lets you choose a PDF, run QDF conversion or web optimization, and download the generated PDF.
Buffer Ownership
Input bytes are copied before they are transferred to the worker. Output bytes returned from the worker are clean Uint8Array copies, not over-allocated MEMFS views.
Non-Goals
This package will not parse PDF text streams, patch text operators, match rendered text items, handle font fallback, perform whiteout/redraw logic, or implement semantic PDF editing. Those belong in a higher-level package.
