DEV Community

NullPointerZen
NullPointerZen

Posted on

I pushed five tools past their image height limits and got five different failures

A 20-page A4 handout rendered at 216 DPI and stacked into one tall image should come out at 1785×50520 pixels, and 40 pages doubles that. I pushed five kinds of tools past their height limits: Pillow, macOS's own ImageIO, a canvas in Chromium, a canvas in Firefox, and two web tools that turn PDFs into long images. Each one failed in its own way. One threw an error, one quietly wrote nothing, one cropped, one returned null, and the web tools shrank the whole picture. Only the error and the null announce themselves. The rest you have to go looking for.

Some context. I build small internal tools for a content team, and editors keep asking for PDFs as one long image they can drop into a group chat. The handout here is one I laid out myself, 8 A4 pages with a 7pt line of text at the bottom of each page so I could judge sharpness, then duplicated to 20, 40 and 60 pages. Everything ran on one Apple M4 Mac. The browsers were Chromium 149, Firefox 151 and a WebKit 26.5 build, which is not the same thing as Safari.

The wall is WebP's 16383

WebP stores width and height in 14 bits each, so no side can go past 16383. JPEG's spec allows 65535, but the encoder Pillow uses stops at 65500. PNG allows over two billion. On the canvas side, Chromium and Firefox cap a single side at 65535, and Chromium and WebKit also cap area at about 268 million pixels. For an A4 page at 216 DPI each page is 2526 pixels tall, so the seventh page already breaks the WebP limit. Six pages is 15156. Seven is 17682.

The loud ones

Pillow is the honest one. I rendered pages with MuPDF and stacked them with Pillow 11.3. Saving the 8-page 1785×20208 image as WebP raised Image size exceeds WebP limit of 16383 pixels and wrote nothing. A narrow test strip 65501 pixels tall failed as JPEG too, with a note that the maximum supported dimension is 65500. PNG took everything, including a 1785×151560 file for 60 pages. Annoying, but at least it tells you.

Firefox 151 is the other loud one, in its own way. Ask its canvas for a 1786×20208 WebP or a 1786×65535 JPEG and the toBlob callback gets null. You can check for that.

The quiet ones

macOS ImageIO was the one I almost missed. I drew the long image with CoreGraphics and handed it to ImageIO. A 1785×50520 JPEG wrote fine. At 1785×101040 the finalize call just returned false. No exception, no log line, no file on disk. PNG and HEIC at the same size were fine. If your batch script ignores that return value, the failure disappears.

Chromium 149 gives you a perfectly normal Blob with the right type. Ask for a 1786×20208 WebP and you get 1786×16383. Ask for a 1786×65535 JPEG and you get 65500. I had checked this before with a gradient and a colour bar, and it is a crop from the bottom, not a resize. WebKit 26.5 takes a third route and gives you a full-size PNG when you ask for WebP.

The web tools chose to shrink. I used ImgIng (https://imging.ai/) and its PDF to image option that joins the whole PDF into one long image, alongside another online long-image tool. ImgIng keeps the height under 16383 for WebP, JPG and PNG alike. At 216 DPI the 20-page handout came out at 578×16380, which works out to about 70 DPI, and 60 pages became 192×16380. I checked that the aspect ratio held and the last line of text was still there. The other tool caps height at 27350, so 40 pages became 483×27350. Both just say done and show the dimensions. Neither says it scaled anything down. I had network logging on for both and saw no upload of the PDF.

What I do now

I read the width and height back after every export and compare them with what I expected. Null and errors get caught in code. False return values get checked. Crops and shrinks only show up when you look at the size. For A4 at 216 DPI with WebP, I split anything past six pages into separate images. How these very tall images behave in a phone chat app I have not tested. I only simulated an iPhone 13 screen size on the desktop.

Top comments (0)