Repository navigation
Compressing to a target file size before embedding: binary search on quality, then on resolution #1798
geMH
started this conversation in
Show and tell
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Disclosure up front: I built the tool linked at the end of this post. No affiliation with
pdf-lib — just a heavy user. Everything below is what I actually ran into.
I've been building a browser-only image → PDF tool, and the part that took the longest
wasn't the PDF assembly — it was honoring a simple user request: "make the output under 200 KB."
pdf-lib can't tell you how big the output will be before you embed the images. JPEG size isn't
a linear function of quality, and a 4000px photo and a 1200px screenshot compress very
differently at the same quality setting. So you can't just pick q=0.8 and promise anything.
What ended up working:
Binary search the quality. Encode at 0.92, if over budget jump to 0.6, narrow for five
iterations, floor at 0.25. Cheap, and it converges fast on photos.
If still over, drop resolution. Re-encode with the long edge capped — 4000 → 2400 → 1600
→ 1000 → 640 — running the same quality search at each step. One thing I got wrong at first:
screenshots and photos degrade in opposite directions. Text in a screenshot goes mushy long
before a photo does, so for photos I keep resolution and grind quality; for screenshots I
drop resolution and keep quality high. Guessing this wrong was the biggest quality complaint
I had.
Build the PDF and actually measure it. Even after all that, the assembled file can land a
few KB over — page structure and embedded metadata aren't free. So I build once, check the
buffer length, and if it's over, re-run with a 0.82 multiplier on the budget (max 3 rounds).
Not elegant, but it's the difference between "usually under 200 KB" and actually promising a
ceiling.
Three things that bit me hard:
Re-encode through canvas, never pass original bytes through. If you copy the JPEG straight
into the PDF you also copy its EXIF — including GPS. For a tool people run on passport photos
and ID scans, that's the whole ballgame. The side effect is you can never just concatenate.
Transparent PNGs come out black.
canvas.toBlob('image/jpeg')has no alpha channel and thedefault backing store is transparent black. Fill white first. Obvious in hindsight, cost me an
afternoon of "why are all the logos dark".
Mobile Safari will kill your tab. A 12MP image is ~48 MB as a raw bitmap; hold a dozen and
iOS reclaims the canvas. So: decode one → encode → write the compressed blob → release the
bitmap. Slower, but it's the difference between working and crashing on an iPhone.
Testing on a 4.1 MB HEIC set requested at "under 200 KB" came out at 193.7 KB with the page still
readable at 100% zoom. It's not always that close — a ceiling is a ceiling, not a target.
Genuinely curious whether anyone here has a smarter budget allocation than "search then verify",
especially for mixed batches where image 1 is a 12MP photo and image 2 is a 500px logo. Right now
I treat them as independent and the batch budget is just the sum, which I suspect is leaving
quality on the table.
The tool is at https://offbasetools.com/tools/heic-to-pdf — everything runs in the tab, nothing
gets uploaded.
All reactions