Toolium

PDF to Image: The 32,767-Pixel Canvas Limit

Hemanth Gedda7 min read

One of my favorite bugs to have shipped a fix for was in the early PDF-to-Image tool, and it taught me to never trust the file extension. A user reported that their downloaded "page.png" file would not open in Photoshop. I tried to repro and got the same error. The file was definitely downloading. Photoshop just refused it. Eventually I opened the file in a hex editor and saw that the first bytes were "JFIF" - the JPEG file signature - even though my export code claimed to be writing PNG. The bug was that I had passed the wrong MIME type to canvas.toBlob and was getting JPEG bytes inside a file with a .png extension. Photoshop, which actually reads file headers, was correct to reject it. I had been trusting the extension I gave the download. The fix was a one-liner; the lesson stuck.

That kind of failure - quiet, hard to diagnose, only visible to users with strict downstream tools - is the category of bug I think about the most when building these tools. The PDF-to-Image converter has a few more of them, all of which the current version handles correctly. This post is the inventory.

The basic rasterization flow

The PDF to Image tool uses Mozilla's pdf.js to render each PDF page into an in-memory canvas at the scale you choose, then exports the canvas as PNG or JPG. The flow:

  1. Load the PDF using pdf.js. This produces a PDFDocumentProxy with a known page count.
  2. For each page, get the viewport at the requested scale. Scale 1 means 72 DPI; scale 2 is 144 DPI; scale 3 is 216 DPI; and so on.
  3. Render the page into a canvas at the viewport dimensions.
  4. Export the canvas as a Blob in the chosen format.
  5. Download each Blob as a separate file.

For text-heavy pages at scale 2 (the sweet spot for most uses), the output is crisp and matches what you would get from desktop tools. For pages with embedded fonts or unusual color spaces, pdf.js does the work and the result usually matches what Acrobat would produce.

The 32,767-pixel canvas limit

Chrome's canvas API has a maximum size limit of 32,767 pixels on either dimension. Firefox is similar. Safari has historically been lower (16,384). If you try to create a canvas larger than the limit, the canvas appears to succeed but rendering operations silently fail and the export produces a blank or partially-blank image.

A typical letter-sized PDF page is 612 x 792 points (8.5 x 11 inches at 72 DPI). At scale 40, that becomes 24,480 x 31,680 pixels - close to but under the Chrome limit. At scale 50, 30,600 x 39,600 - over the limit, and the export fails silently in Chrome.

The Toolium tool caps the scale at 5x, partly to prevent these silent failures, partly because nobody really needs higher than that for screen use. If you need higher resolution rasterization (for print at large dimensions), use a desktop tool like Ghostscript's pdftocairo or pdf2image with PIL in Python.

PNG vs JPG: when each wins

PNG is the default and is right for almost all cases. PDF pages are typically text and vector graphics, both of which compress better in PNG than JPEG (JPEG's DCT produces visible halos around text edges). For text-heavy documents, PNG output is both smaller and visually better.

JPG wins when the page is dominated by photographs (a magazine page with full-bleed images, for instance). The DCT compression handles continuous tones well, and the resulting JPEG can be much smaller than a lossless PNG of the same page.

The Toolium tool exposes both as options. The default is PNG because the majority of PDFs are text-heavy.

The format-mismatch bug

This is the bug from the opening anecdote. The original code passed the format to canvas.toDataURL() instead of canvas.toBlob(), and used "image/png" but downloaded with ".png" extension. The two paths produced different MIME types in some Chrome versions, and the result was JPEG bytes in a file with PNG extension. Strict tools rejected the files.

The current code passes the explicit format to canvas.toBlob() and uses the resulting Blob's type for both the file extension and the download MIME type. The two are guaranteed to match. If you ever see a Toolium download with a wrong extension, please email me; that should not be possible anymore but bugs lurk.

Encrypted PDFs

Like the other Toolium PDF tools, encrypted/password-protected PDFs are rejected up front rather than being silently rendered as black pages or garbage. The error message tells you the file is encrypted; remove the password with a desktop tool first.

Rendering speed and the first-page lag

The first page of any conversion takes longer than subsequent pages because pdf.js loads its compiled rendering code into a Web Worker on first use. The "compilation" pass is about 200 KB of WebAssembly and takes a few hundred milliseconds to parse and instantiate. After that, page-to-page rendering is fast (typically under 100ms per page at scale 2 for a text page).

This means a 1-page PDF feels slow (you wait for the worker load) and a 50-page PDF feels fast per-page after the first. There is no way around this without preloading the worker on page entry, which would slow down the initial page load.

Output file size

For a typical text-heavy page at scale 2, the output PNG is around 100-300 KB. At scale 3, around 300-600 KB. For pages with embedded photos, larger. The "scale 2 PNG" combination is the right default - good visual quality, manageable file size, works for almost every destination.

What this tool does not do

  • Single PDF to single image (with all pages concatenated). Each page becomes its own image. To make a single tall image with all pages, concatenate the outputs in another tool.
  • Animated GIF output. The tool produces static PNG or JPG. For animated previews, a video-format output would be more appropriate; that is a different tool entirely.
  • OCR / text extraction. Converting to image discards the text layer of the PDF. If you need the text, use a different workflow (pdftotext on the command line, or a dedicated OCR tool).

Try the tool mentioned in this article

Open tool