Dev.to Security 🔐 Cybersecurity 👁 0 📖 5 min read

How I Built a 112-Tool PDF Toolkit That Never Uploads Your Files

Every online PDF tool I used in my freelance days made me uncomfortable. Smallpdf, iLovePDF, Adobe's web tools — they all work the same way. You drag your file into a browser window. The file uploads to a server you don

Every online PDF tool I used in my freelance days made me uncomfortable.

Smallpdf, iLovePDF, Adobe's web tools — they all work the same way. You drag your file into a browser window. The file uploads to a server you don't control. The server processes it. The result downloads back to you. The server "deletes" your file.

For a recipe PDF or a scanned receipt, none of that matters. For a client's tax return, a medical record, or a legal contract, it matters a lot. During those few seconds of processing, the document sits on infrastructure owned by someone else. You're trusting their security, their employee access controls, their logging, their breach response — and you have no way to verify any of it.

I wanted something that worked differently. So I built one.

The core idea
Process everything in the browser. Never send the file anywhere.

The technical phrase for this is client-side processing. Instead of a server doing the work, JavaScript running in the user's browser does it. The file stays in memory on the user's machine. The network never sees it.

You can verify this yourself in about 30 seconds:

Open any of my tools

Press F12 to open DevTools

Click the Network tab

Process a PDF

Watch the request list

If zero upload requests appear, the file never left your machine. If you see a POST request with your file in it, it went to a server.

For my toolkit, magictoolshub.com, you can also disconnect your internet after the page loads. The tools keep working. That's the strongest proof that nothing is being uploaded — there's no network to send it to.

The tech stack
The core libraries that make this possible:

pdf-lib — for manipulation. Merging, splitting, rotating, encrypting, adding watermarks, signing, embedding images. This is the Swiss Army knife. Written in TypeScript, compiles to plain JS, and can be bundled for browser use. The API is clean and the docs are decent.

pdf.js (Mozilla) — for rendering and text extraction. This is the same engine Firefox uses for its built-in PDF viewer, so it's mature and battle-tested. I use it for OCR input, text extraction, and image export.

Tesseract.js — for OCR. It's the JavaScript port of Tesseract, the standard open-source OCR engine. Runs entirely in the browser using WebAssembly. Slower than native Tesseract or cloud OCR, but the file never leaves the device.

JSZip — for batch operations where users want to download multiple results as a zip.

WebAssembly — not a library, but a compilation target. Some operations (OCR, compression, encryption) benefit from WASM because they're computationally heavy. Tesseract.js and certain PDF transformations run as WASM modules for this reason.

What was actually hard
I expected the PDF manipulation to be the hard part. It wasn't. pdf-lib and pdf.js handle most of the complexity.

The hard parts were:

  1. Memory management on large files. A 500-page scanned PDF can be 100+ MB. Loading it, processing it, and generating the output requires holding multiple large buffers in memory simultaneously. On a low-end laptop, this can crash the tab. I ended up streaming operations where possible and adding memory warnings when files exceed 50 MB.

  2. Font embedding for Arabic. The toolkit supports 24 languages, including Arabic and other RTL scripts. When you generate a new PDF with text in it (like a watermark or a page number), you have to embed the font. Getting proper Arabic shaping — where letters connect correctly — required using Cairo and Noto Naskh Arabic and being careful about how the text was drawn.

  3. Web Workers for the heavy stuff. Running OCR on the main thread blocks the UI. The user sees a frozen tab and assumes it's broken. Web Workers solve this — the processing happens in a background thread and the UI stays responsive. But passing large ArrayBuffer objects between the main thread and a worker has its own gotchas (you either copy the buffer, which doubles memory, or transfer it, which detaches the original).

  4. Browser compatibility for WASM. Modern browsers all support WebAssembly. Older browsers and some mobile webviews don't. I ended up detecting WASM support and degrading gracefully where possible, or refusing the operation with a clear message where it wasn't.

What I got wrong
I underestimated how many tools I actually needed. When I started, I thought 20 tools would cover 95% of use cases. Then users asked for OCR. Then for redaction. Then for Bates numbering (legal). Then for e-signatures. Then for PDF/A conversion (archival). Six months later I have 112 tools and I'm still adding.

I built in the wrong order. I built the tools first, then the marketing site, then the pricing page, then the payment integration. I should have built a working payment flow for a single tool before building 20 tools. The product work compounds; the distribution work doesn't.

I assumed people would understand the "no upload" benefit. They don't, until you show them. DevTools is a great demo, but you have to explicitly walk people through it. "We don't upload your files" is marketing speak. "Open DevTools and watch the Network tab" is a demonstration.

What I'd do differently
Start with a single tool. Pick the one that solves the most painful problem. Build it, market it, get to $100 MRR. Then expand.

Charge from day one. Free tools attract people who won't pay. A $9/month tool with no free tier attracts people who value the solution. The middle ground — freemium with a real free tier — is the hardest to get right.

Write about the architecture early. I built for months before writing anything. If I'd written one technical post per week from the start, I'd have 30 posts by now, each ranking in Google for a different long-tail query. That's compounding that I missed.

What's next
I'm currently working on improving the OCR speed. Browser-based OCR is genuinely slow for large scanned files, and the biggest complaint I get is that a 300-page scan takes 5+ minutes. Some of that is inherent to the WASM execution model. Some of it is optimization I haven't done yet.

I'm also looking at WebGPU for image processing operations. Compute shaders could speed up redaction rasterization, image-based watermarking, and certain compression operations by an order of magnitude on supported hardware.

If you're building something client-side and want to compare notes, my DMs are open.

webdev

javascript

webassembly

privacy

📰 Read the original article on Dev.to Security

Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.