mutool draw renders blank pages on a contract with JBIG2 images - how to fix
Fixes mutool draw rendering blank pages on contracts with JBIG2 images: confirm the JBIG2 filter, install jbig2dec and re-render, or switch to PyMuPDF which bundles the decoder. Use when scanned pages render blank but look fine in a reader. Not for universally broken installs or genuinely blank pages.
TL;DR
mutool draw rendering blank pages on a contract with JBIG2 images means your mutool build has no JBIG2 decoder - JBIG2 is the compression format scanners use for black-and-white pages, and without the decoder the page has nothing to draw. Install jbig2dec (or use a MuPDF build that bundles it) and re-render. If you cannot change the build, convert with PyMuPDF, which ships its own JBIG2 support, instead of mutool draw.
The query
mutool draw renders blank pages on a contract with JBIG2 images - how to fixUse this when
- mutool draw produces blank pages but the PDF looks fine in a reader
- The blank pages are scanned black-and-white contract pages
- Other PDFs render correctly with the same mutool binary
Not for
- Blank output on all PDFs - that is a broken mutool install, not a codec problem
- Blank pages in the PDF itself (the reader also shows blank) - the content is genuinely missing
- Text extraction returning nothing - a different pipeline from rasterization
Steps
1. Confirm the pages use JBIG2
Inspect the page's images: JBIG2Decode in the filter list confirms the codec hypothesis. Text-only or JPEG pages rendering fine alongside blank scanned pages is the classic signature.
mutool show contract.pdf grep JBIG2Decode | headExpected output: JBIG2Decode filter entries on the blank pages, absent on the pages that render.
2. Install the JBIG2 decoder and re-render
Install jbig2dec through your package manager, then re-run the exact same mutool draw command. MuPDF picks up the decoder at runtime when it is present on the system.
sudo apt-get install -y jbig2dec
mutool draw -o page-%d.png contract.pdfExpected output: the previously blank pages now render as scanned page images.
3. If you cannot install system packages, switch renderers
PyMuPDF (fitz) bundles JBIG2 decoding and renders the same pages without system dependencies. Use it for the rasterization step and keep mutool for everything else.
import fitz
doc = fitz.open("contract.pdf")
for i, page in enumerate(doc):
page.get_pixmap(dpi=200).save(f"page-{i}.png")Expected output: all pages render, including the JBIG2 ones.
4. Pin the working renderer in your pipeline
Document which renderer handles scanned PDFs in your pipeline config so the next environment (CI, Docker) gets the decoder from the start instead of rediscovering blank pages in production. Expected output: fresh environments render scanned contracts correctly on first run.
Tool compatibility
mutool from MuPDF 1.20+; jbig2dec system package; PyMuPDF as the bundled-decoder alternative. Poppler-based tools (pdftoppm) usually handle JBIG2 out of the box for comparison.
Variant phrasings
- mutool draw blank pages JBIG2
- mutool draw renders blank scanned PDF pages
- MuPDF JBIG2Decode blank page missing decoder
- mutool draw scanned contract blank pages jbig2dec
Why it happens
JBIG2 is a specialized codec for bi-level (black-and-white) scanned images, and MuPDF deliberately keeps it as an optional external decoder rather than bundling it. A mutool built or installed without jbig2dec can parse the PDF structure perfectly - so it reports no error - but has no way to decompress the page image, so it draws nothing. The silence is the trap: no error, just blank pages.
Edge cases / pitfalls
- Some contracts mix JBIG2 pages with JPEG pages; only the JBIG2 ones go blank, which makes the pattern obvious once you know it.
- Docker images based on minimal bases often lack jbig2dec - add it to the Dockerfile, not just the dev machine.
- Re-render after installing; pages rendered while the decoder was missing stay blank on disk.
- JBIG2 in this context is about rendering; text extraction from these pages needs OCR regardless of the decoder.
Provenance
Resolved from the public thread: https://vectle.com/posts/pstdVRavF7MoVnwqrx3_etgg