DEV Community

Jack-hao615
Jack-hao615

Posted on

30 PDF Tools, Zero File Uploads — What Running pdf-lib in the Browser Actually Takes

Every online PDF tool asks you the same uncomfortable question: do you trust us with this file?

Merge a contract. Redact an ID scan. Compress last year's tax records. The honest answer for most tools is "your file goes to a server, gets processed, and you have to believe our retention policy." We built AmiPDF around the opposite default: if a task can run in the browser, it must run in the browser. That turns out to be about 30 out of our 40+ tools.

This post is the architecture rundown — what client-side PDF processing actually looks like, where it bites, and the honest list of things we still run server-side.

The stack, in one paragraph

Two libraries carry almost everything:

  • pdf-lib for every write operation: merge, split, rotate, page numbers, watermarks, crop, metadata, encryption.
  • pdf.js for every read/render operation: page previews, the workspace viewer, text extraction for search-based redaction.

Everything else is glue. The site itself is static hosting behind a CDN — no accounts, no database, no session state. When you merge three PDFs on AmiPDF, your browser opens the files with FileReader, hands bytes to pdf-lib, and gets a Blob back. The file never crosses the network.

What is genuinely easy

The bread-and-butter operations are embarrassingly simple once you accept one mental model: a PDF is just bytes you transform locally.

import { PDFDocument } from 'pdf-lib'

const merged = await PDFDocument.create()
for (const file of files) {
  const src = await PDFDocument.load(await file.arrayBuffer())
  const pages = await merged.copyPages(src, src.getPageIndices())
  pages.forEach(p => merged.addPage(p))
}
const out = await merged.save()
Enter fullscreen mode Exit fullscreen mode

That is a working merge tool. Rotate, reorder, extract pages, stamp page numbers, attach watermarks — all the same shape: load, transform, save. None of it needs a server, and none of it ever did. The reason most sites upload your file anyway is that servers made their analytics, quotas, and upsells easier — not because the browser couldn't do it.

Where it bites

Three problems ate most of our debugging time.

1. pdf.js text layers lie about spaces

Our redaction tool works like a search box: type a word, we highlight every occurrence, you click to black it out. That means mapping your query onto the rendered text layer. Except pdf.js inserts spaces where a PDF only has glyph-position changes — runs that visually touch get a space inserted between them, and runs that overlap can also produce phantom whitespace.

The fix is unglamorous: normalize whitespace on both sides, and keep an offset map from normalized positions back to original text-layer nodes so a match lands on the right highlight rect. If you're building anything that searches inside rendered PDFs, budget real time for this. It was the single worst bug of the entire project — search "worked" except on documents where it silently matched nothing.

2. Memory is the real quota

Server-side tools limit uploads by policy. Client-side tools get limited by physics: a 200 MB PDF is 200 MB in an ArrayBuffer, and pdf-lib will copy it more than once while saving. We tuned compression to stream where possible and to do page-level work (rotate one page, extract a range) without materializing multiple full copies. Practical ceiling on a mid-range phone is lower than on a desktop, and the honest move is to say so in the UI rather than let the tab crash.

3. Fonts

Embedding fonts for watermarks and page numbers means carrying font bytes you usually don't have. Standard 14 fonts get you surprisingly far; everything beyond that is a subsetting problem.

The honest server-side list

Three categories genuinely cannot run in a browser at acceptable quality, and pretending otherwise would be worse than the upload:

  1. Office ↔ PDF conversion. LibreOffice in a headless container does this properly. No browser-side library matches it for fidelity.
  2. OCR. Tesseract has WASM builds, but for scan-heavy documents the native server build is dramatically faster and produces a real text-embedded PDF via its PDF renderer.
  3. High-fidelity PDF → Word/Excel/PPT. Layout reconstruction is a research-grade problem; we run a two-engine pipeline with different speed/quality tradeoffs.

For those lanes the rules are: files are deleted immediately after the job, we keep no accounts (there are none), and we don't log file contents. "Server only when necessary" only means something if the exceptions are named, so we name them.

The economics nobody mentions

Client-side processing isn't just a privacy stance — it inverts the cost model. A conversion server bills you per megabyte forever; a static site bills you (almost) nothing per user. That is why AmiPDF has no signup and no daily limits: the marginal cost of the 30 browser tools is close to zero, so charging or nagging would be artificial scarcity.

The server lanes are metered and rate-limited because they cost real CPU — and that's fine, because they're the minority of tasks.

Try it

amipdf.com — 40+ tools, 14 languages, no account. If you have a privacy-sensitive workflow, I'd genuinely like to know which tool you'd want verified as fully local next.


We're launching on Product Hunt soon — if browser-first tools are your thing, keep an eye out.

Top comments (0)