DEV Community

AsyncMonk
AsyncMonk

Posted on

Splitting a 20-page PDF into 4 long images beat lowering the DPI

A user of one of my small tool sites asked for "export this PDF as one long image". Before writing it, I wanted to know what happens when the image gets tall, and whether I should shrink the resolution or split the output. So I generated an A4 handout (headers, tables, two CC BY-SA 4.0 photos from Wikimedia Commons by Jakub Fryš and Josep M. Gracia, and a 7pt footer line to judge sharpness), cloned it to 8, 20, 40 and 60 pages, and measured. Everything below ran on one Apple M4 Mac.

The format hits the wall first

WebP stores width and height in 14 bits, so 16383 px is the hard ceiling. Pillow 11.3 writes an 8×16383 WebP fine and refuses 8×16384 with "Image size exceeds WebP limit". JPEG allows 65535 on paper, but libjpeg in Pillow stops at 65500. PNG uses 31 bits per side; a 1785×151560 PNG wrote without complaint. macOS ImageIO was the sneaky one: asked for a 1785×101040 JPEG, it returned false and produced no file, no error message.

Then the canvas. Probing by binary search, Chromium 149 and Firefox 151 cap a single side at 65535. Chromium and WebKit 26.5 also cap area near 2^28 pixels (about 268 million). Exporting past the format limit with toBlob is where it gets quiet:

Request Chromium 149 Firefox 151 WebKit 26.5
1786×20208 as WebP 1786×16383, no error null PNG instead
1786×65535 as JPEG 1786×65500, no error null 1786×65535

Chromium crops the overflow rather than scaling it. Only Firefox's null is something your code can catch; for the other two you have to compare dimensions yourself.

What that means in A4 pages

At 216 DPI an A4 page is about 2526 px tall. That gives 6 pages per WebP and roughly 25 pages per JPEG before the 65500 limit. These page counts are arithmetic; I only ran the 8/20/40/60 cases.

For comparison I ran the same files through the PDF-to-image tool on ImgIng (https://imging.ai/) in Chromium 149. Its "whole PDF as one long image" mode scales the entire image down to fit under 16383 px tall, for JPEG and PNG too. My guess is it applies the WebP ceiling to every format, but that's inferred from output sizes only. At 8 pages, choosing 216 DPI gave 1446 px wide, which is 175 effective DPI (width ÷ 8.264 in). Choosing 300 produced a byte-identical file. At 20 pages it was 578 px wide, about 70 DPI. Nothing in the UI says it shrank. A free web tool I tried did the same thing with a 27350 px cap, and its page claims adjustment only above 50 pages while my 40-page file was already down to 483 px wide.

Split vs one image, for 20 pages

Approach Output Effective DPI Total bytes
4 parts × 5 pages 4 WebP, 1785×12630 216 4,346,012
One image, auto-shrunk 1 WebP, 578×16380 69.9 986,434
One image, local JPEG, no shrink 1785×50520 216 6,930,543

Splitting gives 9.5× the pixels for 4.4× the bytes. The 7pt footer line is 840×39 px in a split part and 272×13 px in the single shrunk image. Zoomed in 3× on a simulated iPhone 13 screen it's a blur you can only half guess at. The unshrunk 50,520 px JPEG keeps full detail, but I haven't tested whether phones or chat apps open it, so I won't ship that path.

The rule I'm going with

WebP output gets split every 6 pages, so each part stays at the chosen DPI. If someone insists on a single image I switch to JPEG, and past about 25 pages I split anyway, since Chromium silently crops beyond 65500. Whenever the tool shrinks or splits, the UI states the effective DPI. Encoding happens in the user's browser, so the extra 3.4 MB never touches my bandwidth bill.

What I haven't solved is where to cut. Cutting by page count can slice a table that spans two pages, and cutting by content means parsing PDF structure. For now I'm cutting by page and will check what users complain about.

Top comments (0)