Browser vs Node.js
The library works in both Node.js and browser environments, but there are important differences in setup and capabilities.
Node.js
Section titled “Node.js”Node.js supports two backends: WASM (default) and Native (optional, faster).
WASM loads automatically from node_modules:
import { PDFium } from '@scaryterry/pdfium';
// No configuration neededusing pdfium = await PDFium.init();Advantages
Section titled “Advantages”- Automatic WASM loading: No URL configuration
- File system access: Read/write PDFs directly
- More memory: System memory limits
- No CORS: Local file access without restrictions
Example
Section titled “Example”import { PDFium } from '@scaryterry/pdfium';import { promises as fs } from 'fs';
async function processPDF(inputPath: string, outputPath: string) { const data = await fs.readFile(inputPath);
using pdfium = await PDFium.init(); using document = await pdfium.openDocument(data);
for (const page of document.pages()) { using p = page; const text = p.getText(); console.log(`Page ${p.index + 1}: ${text.slice(0, 100)}...`); }
const output = document.save(); await fs.writeFile(outputPath, output);}Browser
Section titled “Browser”You must provide the WASM binary:
import { PDFium } from '@scaryterry/pdfium';
// Option 1: Fetch from URLconst wasmResponse = await fetch('/pdfium.wasm');const wasmBinary = await wasmResponse.arrayBuffer();using pdfium = await PDFium.init({ wasmBinary });
// Option 2: Import as asset (bundler-dependent)import wasmUrl from '@scaryterry/pdfium/pdfium.wasm?url';const response = await fetch(wasmUrl);const binary = await response.arrayBuffer();using pdfium = await PDFium.init({ wasmBinary: binary });WASM Configuration
Section titled “WASM Configuration”export default { optimizeDeps: { exclude: ['@scaryterry/pdfium'], }, assetsInclude: ['**/*.wasm'],};// Usageimport wasmUrl from '@scaryterry/pdfium/pdfium.wasm?url';
const response = await fetch(wasmUrl);const wasmBinary = await response.arrayBuffer();using pdfium = await PDFium.init({ wasmBinary });Webpack
Section titled “Webpack”module.exports = { experiments: { asyncWebAssembly: true, }, module: { rules: [ { test: /\.wasm$/, type: 'asset/resource', }, ], },};Considerations
Section titled “Considerations”- CORS: WASM must be served from same origin or with proper headers
- MIME type: Server must send
application/wasm - Memory: Browser memory limits apply
- Workers: Consider off-main-thread processing
Example
Section titled “Example”import { PDFium } from '@scaryterry/pdfium';
async function renderPDF(pdfUrl: string, wasmUrl: string) { // Load WASM const wasmResponse = await fetch(wasmUrl); const wasmBinary = await wasmResponse.arrayBuffer();
using pdfium = await PDFium.init({ wasmBinary });
// Load PDF const pdfResponse = await fetch(pdfUrl); const pdfData = await pdfResponse.arrayBuffer();
using document = await pdfium.openDocument(pdfData); using page = document.getPage(0);
// Render to canvas const { data, width, height } = page.render({ scale: 2 });
const canvas = document.getElementById('pdf-canvas') as HTMLCanvasElement; canvas.width = width; canvas.height = height;
const ctx = canvas.getContext('2d')!; const imageData = new ImageData( new Uint8ClampedArray(data), width, height ); ctx.putImageData(imageData, 0, 0);}Native Backend (Optional)
Section titled “Native Backend (Optional)”For higher performance, Node.js can use a native backend:
import { PDFium } from '@scaryterry/pdfium';
// Prefer native, fall back to WASMconst pdfium = await PDFium.init({ useNative: true });Install the platform package first:
pnpm add @scaryterry/pdfium-darwin-arm64 # macOS Apple Siliconpnpm add @scaryterry/pdfium-linux-x64-gnu # Linux x64See Native vs WASM Backends for details.
Entry Points
Section titled “Entry Points”The package provides several sub-path exports for different environments and use cases:
| Sub-path | Description |
|---|---|
@scaryterry/pdfium | Main auto-detecting entry — re-exports everything |
@scaryterry/pdfium/browser | Browser-specific — excludes native backend classes |
@scaryterry/pdfium/node | Node.js-specific — auto-loads WASM from node_modules |
@scaryterry/pdfium/worker | Self-executing Web Worker script for off-main-thread processing |
@scaryterry/pdfium/internal | Low-level handles, constants, and WASM helpers (advanced usage) |
@scaryterry/pdfium/types | Type-only export — interfaces, enums, and type definitions |
@scaryterry/pdfium/pdfium.wasm | Raw WASM binary for manual loading |
When to Use Each Entry Point
Section titled “When to Use Each Entry Point”Main entry — use for most applications. It detects the environment and selects the appropriate backend:
import { PDFium } from '@scaryterry/pdfium';Browser entry — use when bundling for the browser to avoid importing Node.js-specific code:
import { PDFium } from '@scaryterry/pdfium/browser';Node entry — use in Node.js when you want automatic WASM loading from the filesystem:
import { PDFium } from '@scaryterry/pdfium/node';Worker entry — pass the URL of this entry point when creating a WorkerProxy. It is self-executing and should not be imported directly:
const workerUrl = new URL('@scaryterry/pdfium/worker', import.meta.url);Types entry — use for type-only imports in libraries that depend on PDFium types without importing runtime code:
import type { RenderResult, PageObjectType } from '@scaryterry/pdfium/types';Internal entry — for advanced use cases that need direct access to WASM handles or memory utilities. This API is not covered by semver guarantees.
Feature Comparison
Section titled “Feature Comparison”| Feature | Node.js (WASM) | Node.js (Native) | Browser |
|---|---|---|---|
| WASM loading | Automatic | N/A | Manual (fetch) |
| File system | Native fs | Native fs | File API / fetch |
| Memory limit | System RAM | System RAM | Browser limit (~2-4GB) |
| Web Workers | N/A | N/A | Recommended for large PDFs |
| CORS | N/A | N/A | Required for cross-origin |
| Performance | Very good | Faster | Depends on device |
| Feature coverage | Complete | Core features | Complete |
File Handling
Section titled “File Handling”Node.js
Section titled “Node.js”import { promises as fs } from 'fs';
// Read fileconst data = await fs.readFile('input.pdf');
// Write fileawait fs.writeFile('output.pdf', bytes);Browser
Section titled “Browser”// From file inputasync function handleFileInput(file: File) { const data = await file.arrayBuffer(); using document = await pdfium.openDocument(data); // ...}
// From URLasync function loadFromURL(url: string) { const response = await fetch(url); const data = await response.arrayBuffer(); using document = await pdfium.openDocument(data); // ...}
// Download resultfunction downloadPDF(bytes: Uint8Array, filename: string) { const blob = new Blob([bytes], { type: 'application/pdf' }); const url = URL.createObjectURL(blob);
const link = document.createElement('a'); link.href = url; link.download = filename; link.click();
URL.revokeObjectURL(url);}Image Output
Section titled “Image Output”Node.js (sharp)
Section titled “Node.js (sharp)”import sharp from 'sharp';
const { data, width, height } = page.render({ scale: 2 });
// Save as PNGawait sharp(data, { raw: { width, height, channels: 4 } }) .png() .toFile('output.png');
// Save as JPEGawait sharp(data, { raw: { width, height, channels: 4 } }) .jpeg({ quality: 90 }) .toFile('output.jpg');Browser (Canvas)
Section titled “Browser (Canvas)”const { data, width, height } = page.render({ scale: 2 });
const canvas = document.createElement('canvas');canvas.width = width;canvas.height = height;
const ctx = canvas.getContext('2d')!;const imageData = new ImageData( new Uint8ClampedArray(data), width, height);ctx.putImageData(imageData, 0, 0);
// Convert to blobcanvas.toBlob((blob) => { // Use blob...}, 'image/png');
// Or data URLconst dataUrl = canvas.toDataURL('image/png');Workers
Section titled “Workers”Browser Workers
Section titled “Browser Workers”For large PDFs, use Web Workers:
// See Worker Mode guide for detailsimport { PDFium } from '@scaryterry/pdfium';
await using pdfium = await PDFium.init({ useWorker: true, workerUrl, wasmBinary,});await using document = await pdfium.openDocument(pdfData);Node.js Workers
Section titled “Node.js Workers”Node.js worker threads are supported automatically in high-level worker mode:
import { PDFium } from '@scaryterry/pdfium/node';
await using pdfium = await PDFium.init({ useWorker: true, workerUrl: new URL('./pdf-worker.js', import.meta.url), wasmBinary,});await using document = await pdfium.openDocument(pdfData);const page = await document.renderPage(0, { scale: 2 });Testing
Section titled “Testing”Jest (Node.js)
Section titled “Jest (Node.js)”module.exports = { testEnvironment: 'node', transformIgnorePatterns: [ '/node_modules/(?!@scaryterry/pdfium)', ],};Vitest (Browser)
Section titled “Vitest (Browser)”export default { test: { environment: 'jsdom', setupFiles: ['./test/setup.ts'], },};See Also
Section titled “See Also”- Installation Guide — Platform setup
- Native vs WASM Backends — Backend comparison
- Worker Mode — Browser workers
- Node.js Examples — Server-side examples
- Browser Examples — Client-side examples