pdf2image

A Python wrapper around poppler's pdftoppm and pdftocairo that converts PDF files into a list of Pillow Image objects.

Library
PyPI
v1.17.0
1,982stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
48/100Fair
Development Activity0
Maintenance20
Community72
Maturity60
Momentum40

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
64/100Good
Architecture62
Code Quality58
Innovation55
Learning Curve80

pdf2image is a thin Python wrapper around the poppler-utils command-line tools pdftoppm and pdftocairo, letting you convert PDF documents into Pillow (PIL) Image objects with a couple of function calls. It doesn’t reimplement PDF rendering itself — it shells out to the same battle-tested poppler binaries that ship with most Linux distros and are installable via Homebrew on macOS or a standalone build on Windows, then parses the raw PPM/PGM/JPEG/PNG output back into Pillow images (or writes them straight to disk).

The two entry points, convert_from_path and convert_from_bytes, cover the common cases of having a PDF on disk versus in memory (e.g. an upload buffer), and both accept the same rich set of keyword arguments: DPI, page ranges, output format, grayscale, transparency, cropbox usage, password-protected PDFs, multi-threaded rendering, and a paths_only mode that avoids loading everything into memory at once for large documents. A companion pair of functions, pdfinfo_from_path / pdfinfo_from_bytes, exposes poppler’s pdfinfo output (page count, page size, PDF version, etc.) as a plain dictionary.

It’s a popular building block behind OCR pipelines, PDF thumbnail generators, and document-preview features — anywhere a PDF needs to become an image before further processing with Pillow, Tesseract, or a computer-vision model.

What You Get

  • convert_from_path() and convert_from_bytes() to turn a PDF on disk or in memory into a list of Pillow images in one call
  • pdfinfo_from_path() / pdfinfo_from_bytes() to read page count, page size, and other PDF metadata via poppler’s pdfinfo
  • Fine-grained render controls: DPI, first/last page range, grayscale, transparency, cropbox, output format (ppm/jpeg/png/tiff), and image size resizing
  • Multi-threaded conversion via thread_count, with an internal thread-safe filename generator to keep output files from colliding
  • Support for password-protected PDFs (userpw/ownerpw) and a configurable timeout that raises PDFPopplerTimeoutError instead of hanging
  • A paths_only mode that writes pages to an output folder and returns file paths instead of loading every page into memory, useful for very large PDFs

Common Use Cases

  • Rendering PDF pages to images before running OCR (e.g. with Tesseract) to extract text from scanned documents
  • Generating page thumbnails or previews for a document-management or file-upload UI
  • Feeding PDF pages into computer-vision or ML pipelines that expect image input rather than PDF
  • Batch-converting archives of PDFs into PNG/JPEG image sets for indexing or display
  • Extracting a single page or page range from a PDF as an image without opening the whole document in memory

Under The Hood

Architecture The library is a thin single-purpose wrapper: pdf2image.py holds two public conversion functions (convert_from_path, convert_from_bytes) that both funnel into the same internal pipeline — normalize arguments, resolve the poppler command (pdftoppm or pdftocairo) and its version via _get_poppler_version, build a CLI argument list with _build_command, spawn one or more subprocess.Popen processes per thread_count, then hand the raw stdout bytes to a format-specific parser in parsers.py (parse_buffer_to_ppm/_pgm/_jpeg/_png) that slices poppler’s concatenated output back into individual Pillow images. convert_from_bytes is a shim that writes the buffer to a temp file and calls convert_from_path, so there is effectively one code path. A small generators.py module supplies thread-safe output-filename generators (uuid_generator, counter_generator) so concurrent subprocess writes never collide. If poppler’s binaries or version change behavior, nearly everything downstream (parsing, flags, hide_annotations gating) is affected, since there is no abstraction layer between the CLI output format and the parsing logic.

Tech Stack The only runtime dependency declared in setup.py is pillow, which pdf2image uses purely as the image container (PIL.Image.open on the parsed byte buffers); it does no PDF parsing of its own and instead shells out to poppler-utils (pdftoppm, pdftocairo, pdfinfo) via the standard library’s subprocess. Packaging is classic setuptools (setup.py + setup.cfg, find_packages), targeting Python 3.7+ with a py.typed marker for type-checker support. CI runs on CircleCI using a Docker-based Python image, installing poppler-utils at the OS level before running the test suite, then re-running tests with poppler uninstalled to verify the library’s own error path (PDFInfoNotInstalledError) rather than crashing.

Code Quality Tests live in a single root-level tests.py (not a tests/ package) using unittest, with @unittest.skipIf(not POPPLER_INSTALLED, ...) guards so the suite still runs meaningfully in environments without poppler installed, plus an optional memory_profiler-based profiling decorator gated behind a PROFILE_MEMORY env var. Error handling is explicit and typed via a small custom exception hierarchy (PopplerNotInstalledError, PDFInfoNotInstalledError, PDFPageCountError, PDFSyntaxError, PDFPopplerTimeoutError) rather than letting subprocess or OS errors leak through unwrapped. Type hints are used throughout the public functions and a py.typed marker is shipped, though there’s no configured linter/formatter (no ruff/black/flake8 config found in the repo) and no mypy config, so type coverage isn’t enforced in CI beyond what CircleCI’s python3 tests.py run exercises.

API Design The public surface is intentionally small — four functions, all synchronous, all returning either a list of Pillow images or a plain dict — which keeps the getting-started cost low: convert_from_path('file.pdf') is a complete, working call with sane defaults (200 DPI, PPM format, single-threaded). Keyword arguments are extensive (DPI, page range, format, threading, password, cropbox, transparency, grayscale, size, timeout, paths_only) but all optional with documented defaults, so advanced use doesn’t complicate the basic case. The trade-off is that behavior is coupled to the installed poppler version — some flags (jpegopt, hide_annotations) are silently disabled below certain poppler versions rather than raising, which favors graceful degradation over explicit feedback when a caller’s environment doesn’t support a requested option.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search