@jackgreen2018/pdf-engine
v1.0.114
Published
A lightweight, high-performance PDF processing library that runs in the browser and Node.js via Rust/WASM. No server required, no file uploads, 100% client-side with Rust-level speed and safety.
Maintainers
Readme
Scope: full engine (see SCOPE.md).
pdf-engine — Rust/WASM PDF Processing Library
A high-performance PDF processing library for Node.js and the browser. Built with Rust + WebAssembly. Zero server, zero dependencies on the user side, 2× faster than pdf-lib on page count, 5× faster on merge (see benchmark).
Features
- Page Count: Quickly determine the number of pages in a PDF using a proper PDF parser
- Text Extraction: Extract text content from PDF files with accurate parsing
- PDF Merge: Combine multiple PDFs into a single file
- PDF Split: Split PDFs by page range
- PDF Encryption: Encrypt PDFs with RC4 (V1/V2) or AES-128 (V4) using user/owner passwords
- PDF Decryption: Decrypt password-protected PDFs
- Encrypted Detection: Check if a PDF is encrypted with
isEncrypted() - Stamp Image: Overlay PNG/JPEG images onto PDF pages with
stampImage() - Privacy: All processing happens in the user's browser or local environment
- Performance: Rust-powered with WASM for maximum speed
Installation
npm install @jackgreen2018/pdf-engineOr build from source:
git clone <this-repo>
cd pdf-engine
npm install
npm run buildUsage
Browser
import pdfEngine from '@jackgreen2018/pdf-engine';
// Initialize the engine
await pdfEngine.init();
// Get page count of a PDF file
const file = document.querySelector('input[type="file"]').files[0];
const arrayBuffer = await file.arrayBuffer();
const pageCount = await pdfEngine.getPageCount(new Uint8Array(arrayBuffer));
console.log(`Pages: ${pageCount}`);
// Extract text from a PDF
const text = await pdfEngine.extractText(new Uint8Array(arrayBuffer));
console.log(text);
// Merge multiple PDFs
const mergedPdf = await pdfEngine.merge([pdf1Buffer, pdf2Buffer, pdf3Buffer]);
// Split a PDF by page numbers
const pagesToExtract = [1, 3, 5];
const splitParts = await pdfEngine.split(new Uint8Array(arrayBuffer), pagesToExtract);
// Encrypt a PDF (V4 = AES-128)
const encrypted = await pdfEngine.encrypt(
new Uint8Array(arrayBuffer),
"user-password",
"owner-password",
4, // version: 1=RC4, 2=RC4-40bit, 4=AES-128
);
// Check if encrypted
const isEnc = await pdfEngine.isEncrypted(new Uint8Array(arrayBuffer));
// Stamp an image onto a PDF page
const stampedPdf = await pdfEngine.stampImage(
new Uint8Array(arrayBuffer),
imageBuffer,
100, // x position
100, // y position
50, // width
50, // height
);
// Decrypt a password-protected PDF
const decrypted = await pdfEngine.decrypt(encrypted, "user-password");Node.js
const pdfEngine = require('@jackgreen2018/pdf-engine');
async function processPdf() {
await pdfEngine.init();
const buffer = require('fs').readFileSync('document.pdf');
const pageCount = await pdfEngine.getPageCount(buffer);
console.log(`Pages: ${pageCount}`);
const text = await pdfEngine.extractText(buffer);
console.log(text);
}
processPdf().catch(console.error);Build
npm run buildThis compiles the Rust code to WASM using wasm-pack and builds the TypeScript definitions.
Development
# Install dependencies
npm install
# Build the project
npm run build
# Run benchmarks
npm run benchmark
# Test locally
npm testBenchmark
Performance benchmarks for pdf-engine operations:
npm run benchmark| Operation | pdf-engine (ms) | pdf-lib (ms) | |-----------|-----------------|--------------| | Page Count | 0.72 | 1.59 | | Text Extraction | 0.23 | — | | Merge (2 files) | 0.80 | 4.06 | | Split (pages [0,1]) | 0.24 | 1.77 | | Encrypt (V4 AES-128) | 0.79 | — | | Decrypt | 0.32 | — | | Stamp Image (1x1 PNG) | 1.24 | — |
extractText correctness: PASS — gated via tests/fixtures/text-fixture.pdf (FlateDecode-compressed, real-world content stream). v1.0.30 fixes extractText to return actual text instead of "No text extracted."
stampImage: PASS — v1.0.114 fixes empty-page stamping (pages without /Contents now initialize empty content stream before image insertion).
Run 2026-08-04 (v1.0.114); raw JSON at benchmark-results.json.
License
MIT
Verify locally
npm install @jackgreen2018/pdf-engine
node -e '
const fs = require("fs");
const m = require("@jackgreen2018/pdf-engine");
const buf = fs.readFileSync("test.pdf");
m.initialize().then(() => m.getPageCount(new Uint8Array(buf)))
.then(n => console.log("pages:", n));
'Acceptance Criteria Evidence
| AC | Verifiable requirement | Committed proof |
|---|---|---|
| AC-1 | Real Rust source with lopdf domain dependency compiles to a non-stub WASM artifact; JS and TypeScript declarations build. | Cargo.toml, src/, pkg/, dist/, evidence/cargo-build.log, evidence/wasm-pack-build.log, evidence/gate-artifact.log |
| AC-2 | Rust and package tests pass on the final source. | evidence/cargo-test.log, evidence/npm-test.log |
| AC-3 | The exact npm tarball installs in a clean consumer and page count, merge, and split return valid PDFs with correct page counts. | jackgreen2018-pdf-engine-1.0.18.tgz, evidence/npm-pack.log, evidence/local-install-smoke.log, evidence/gate-behavior.log |
| AC-4 | A runnable head-to-head benchmark against pdf-lib uses realistic identical input, validates output, and produces real numbers matching README. | benchmark.mjs, evidence/benchmark.log, evidence/benchmark.json, benchmark table above |
| AC-5 | Both required hard gates pass for /apps/pdf-engine before publication. | evidence/gate-artifact.log (exit 0), evidence/gate-behavior.log (exit 0) |
| AC-6 | Scoped package metadata has version, license, repository, correct artifact entry points, and .d.ts; package is public under @jackgreen2018/pdf-engine. | package.json, evidence/npm-whoami.log, evidence/npm-publish.log, evidence/npm-view.log, evidence/npm-page.log |
| AC-7 | README provides the install command, working example, measured benchmark table, npm URL, and direct AC-to-evidence index with no unsupported "verified" claim. | This README, evidence/AC-SUMMARY.md, all referenced files present |
| AC-8 | Final app repository commit is tagged v1.0.0; cycle scratch is absent and no web-deployment artifacts were added. | evidence/git-tag.log, final tree inspection |
| AC-9 | Encrypt/decrypt round-trip preserves page count; isEncrypted correctly reports true for encrypted and false for unencrypted PDFs. | tests/smoke.js encrypt/decrypt tests, benchmark encrypt gate |
See Also
- pdf-lib — A popular JavaScript PDF library for comparison
