Compress, merge, convert — no npm, no React, no build step. Here’s how three of the tools actually work under the hood.
Every “free online PDF” site wants your email, throws a wall of ads at you, or quietly keeps your files. I wanted the opposite: drop a file, get a result, and have the thing forget you were ever there. So over a weekend I built pdfly — a browser-based PDF toolkit — with two rules: no JavaScript framework on the front end, and a single static binary on the back end.
Here’s what that looks like in practice, and how three of the tools are built.
The stack, and why it’s boring on purpose
The front end is plain HTML, CSS, and vanilla JavaScript. No React, no Vue, no bundler, no node_modules folder the size of a small moon. The whole UI is a handful of files the browser loads directly. Page loads are instant because there’s nothing to hydrate, and there’s no build step to babysit — I edit a file, refresh, done.
The back end is a single Go binary. Go compiles to one self-contained executable, which means “deploy” is really just “copy a file and restart a service.” No container runtime, no interpreter to install on the server. It runs behind Caddy on a tiny cloud VM, and that’s the entire production setup.
Now the tools.
Tool 1 — Compress (shelling out to Ghostscript)
Shrinking a PDF well is genuinely hard: you’re re-sampling images, subsetting fonts, and rewriting the document structure. Rather than reinvent that, pdfly leans on Ghostscript, the battle-tested PDF engine that’s been doing this for decades.
The tool exposes four quality presets — screen (72dpi), ebook (150dpi), printer (300dpi), and prepress — which map straight to Ghostscript’s -dPDFSETTINGS. The Go handler saves your upload to a temp directory, runs Ghostscript against it, streams back the smaller file, and deletes the temp directory. That defer os.RemoveAll(work) at the top of every handler is the whole privacy story: the work folder is gone the moment the request finishes.
Tool 2 — Merge (pure Go, zero external dependencies)
For combining PDFs I used pdfcpu, a PDF library written entirely in Go. That matters: merge, split, extract, encrypt, and decrypt all run inside the binary with no external program to shell out to. No Ghostscript, no system dependency — just Go calling Go.
The handler enforces one rule (“select at least two PDFs”), preserves the exact order you dragged the files into, and writes the merged result. Because the browser lets you reorder the list before uploading, the front end and back end agree on order without any clever syncing — the upload order is the merge order.
There’s a nice fallback story here too. Some PDFs in the wild are subtly malformed — a structure that strict parsers reject. When pdfcpu balks, pdfly quietly rebuilds a clean copy through Ghostscript and retries, so a slightly-broken file still works instead of throwing an error in your face.
Tool 3 — PDF → Word (knowing when to leave your language)
This is the one that broke my “single binary” purity, and I think that’s the honest part of the story. Converting a PDF back into an editable Word document — reconstructing paragraphs, tables, and layout — is a genuinely nasty problem, and the best open-source tool for it lives in the Python world (built on PyMuPDF). Go simply doesn’t have an equivalent that’s as good.
So pdfly shells out to a small Python helper for this one tool. The tradeoff: it’s memory-hungry, and the production VM is small, so the handler rejects anything over 30 MB up front rather than letting a giant file take the server down. It also only works on text-based PDFs, not scans — there’s no OCR pretending a photo is a document.
The lesson: use the right tool for the job even when it dents your architecture. One Python dependency for the one feature that truly needs it beats a worse pure-Go conversion.
What “we don’t keep your files” actually means
Every handler follows the same shape: save the upload to a temporary folder, do the work, stream the result, and defer the deletion of that folder. A background sweeper also clears out any editor sessions that were abandoned. There’s no database of your documents because there’s no database at all. The only thing that persists is a single integer — a counter of how many files have been processed — which powers the little stats number on the homepage.
Was a weekend enough?
For a working v1, yes — because I chose boring, proven pieces and let them do the heavy lifting. Ghostscript compresses, pdfcpu handles the pure-PDF operations, Python covers the one job Go can’t, and the front end stays a pile of static files anyone could read in an afternoon. No framework churn, no build pipeline, no server babysitting.
The best part of building small like this: six months from now, it’ll still just be a binary and a folder of HTML. Nothing to upgrade, nothing to break.
pdfly is free and needs no signup — pdfly.work.
Comments
Post a Comment