Every PDF compressor I tried had the same two problems: my file got uploaded to a server I know nothing about, and occasionally the "compressed" result came back larger than the original. So I built my own — it runs entirely in the browser, and it has one rule I refused to break:
The output must never be bigger than the input.
You can try it here: Compress PDF — Pushpendra Tech Tools
The stack
No backend at all. Just two libraries loaded from a CDN:
-
PDF.js (Mozilla) — renders each page of the source PDF to a
<canvas> - pdf-lib — rebuilds a fresh PDF with those rendered pages embedded as JPEGs
The whole tool is one static HTML page, like every other tool in my free tools library.
Why naive re-rendering fails
My first version did the obvious thing: render every page at 150 DPI, JPEG at quality 0.72, rebuild. On a normal A4 scan it worked beautifully. Then I tested it on a PDF whose pages were defined with huge page dimensions (some scanner apps and img2pdf-style generators do this — page size in points equals the image's pixel size).
Result: a 4.8 MB file "compressed" to 11.8 MB. The renderer happily produced enormous canvases, and JPEG-encoded noise at that size is big.
Three fixes got me to the never-bigger guarantee:
1. Cap the render by pixels, not just DPI
DPI alone is meaningless when the page box is lying. Each quality level now has both a DPI target and a maximum canvas dimension — high: 200 DPI / cap 3000px, balanced: 150 DPI / cap 2300px, max: 100 DPI / cap 1600px, with JPEG quality stepping down alongside.
2. Always compute a lossless candidate first
Before rendering anything, I load the file with pdf-lib and re-save it with object streams enabled. That alone shrinks some PDFs (dead objects, uncompressed streams) with zero quality loss — and it gives me a safe baseline candidate.
3. Step down automatically, then pick the smallest
The rendered pass runs at the user's chosen level. If the result isn't smaller than the original, the tool retries at the next stronger level on its own. Finally it compares all candidates and only offers a download when the winner is genuinely smaller. If nothing beats the original, the UI says so honestly: "This PDF is already well optimised."
The results
- Realistic A4 scan (300 DPI, 3 pages): 1.7 MB → 390 KB (−78%)
- The adversarial noise PDF that used to balloon to 11.8 MB: 4.8 MB → 3.9 MB (−20%)
Not magic — scanned documents are mostly smooth paper tones that JPEG loves, and pathological inputs are handled gracefully instead of embarrassingly.
The honest trade-off
Re-rendering means pages become images inside the PDF: text is no longer selectable or searchable. For the actual use case — scanned certificates, marksheets and ID proofs being squeezed under a form's 200 KB / 300 KB upload limit — that's irrelevant, but the page says it upfront, because a tool that hides its trade-offs is a tool I wouldn't trust with my own documents.
The bigger lesson for me: "compressor" is a promise, not a feature. Testing against the file that breaks your assumptions is worth more than testing against ten files that don't.
More browser-only tools (merge, split, convert, OCR, background removal — all client-side) live at pushpendra.tech/tools. Happy to answer questions about the approach in the comments.
Top comments (0)