OCRmyPDF
Adds a searchable OCR text layer to scanned PDFs and images, producing validated PDF/A output by default.
Repository Health
Technical Analysis
OCRmyPDF is a command-line tool and Python library that adds an invisible, accurately positioned OCR text layer to scanned PDF and image files, making them searchable and copy-paste friendly without altering their visual appearance. It wraps Tesseract OCR, Ghostscript, and a battery of PDF/A validation and image-optimization steps into a single pipeline that produces standards-compliant output by default.
Beyond text recognition, OCRmyPDF automatically deskews and cleans scanned pages, optimizes embedded images to shrink file size, validates both input and output PDFs, and distributes work across all available CPU cores to handle documents with thousands of pages. A pluggy-based plugin system lets you swap in alternative OCR engines such as EasyOCR, PaddleOCR, or Apple Vision, and it supports more than 100 languages through Tesseract’s language packs.
What You Get
- Searchable PDF/A output generated by default and validated against the PDF/A standard
- Accurate OCR text placement beneath the original page image for reliable copy-paste
- Automatic page deskewing and image cleanup before recognition
- Image optimization that frequently produces output smaller than the input file
- Multi-core processing that scales to documents with thousands of pages
- A pluggy-based plugin interface for swapping in alternative OCR engines
Common Use Cases
- Making scanned archives and paper records full-text searchable
- Preparing scanned PDFs for long-term digital archiving via PDF/A
- Batch-processing large volumes of incoming scanned documents
- Integrating OCR into document-management systems like Paperless-ngx
Under The Hood
Architecture
OCRmyPDF is organized as a page-synchronous pipeline coordinated through _pipelines/ocr.py: input is triaged and parsed into a PdfContext (src/ocrmypdf/_jobcontext.py), then fanned out per page via an Executor abstraction (_concurrent.py) so OCR, image cleanup, and text-layer generation run concurrently across CPU cores before results are grafted back into a single PDF by OcrGrafter (_graft.py) and finalized through PDF/A conversion (pdfa.py) and image optimization (optimize.py). An OcrmypdfPluginManager built on pluggy (_plugin_manager.py, pluginspec.py) mediates every extensible step — OCR engine selection, image filters, PDF rendering — with a readers-writer lock guarding the interpreter-global state that plugin installation mutates, so concurrent jobs sharing a plugin set can run without racing paths that swap the active engine underneath an in-flight job. This is a clean example of a synchronous core wrapped in a well-defined extension seam rather than a monolith.
Tech Stack
The project is pure Python (3.11+) built with hatchling, using pikepdf for low-level PDF manipulation, pypdfium2 for rasterization, Pillow and pi-heif for image handling, fpdf2/uharfbuzz for direct OCR-text rendering, pydantic for options modeling, and rich for CLI progress/output. It shells out to external Ghostscript and Tesseract OCR binaries rather than binding them natively, keeping those heavyweight dependencies swappable and independently upgradable. Optional extras add watchfiles/cyclopts for a watch-folder mode and streamlit for an experimental web UI.
Code Quality
The repository ships an extensive test suite (71 files under tests/) run with pytest in parallel (-n auto), enforces mypy and ruff (lint + format) as pinned dev dependencies, and mirrors those exact checks in both a prek pre-commit/pre-push config and GitHub Actions CI (.github/workflows/build.yml) so local and CI checks can’t drift. The package ships a py.typed marker, uses typed dataclasses/enums (e.g. a typed ExitCode IntEnum in exceptions.py mapped to specific exit codes rather than bare exception strings), and follows consistent docstring conventions enforced via ruff’s pydocstyle rules — this is a mature, well-tooled codebase.
API Design
Alongside the CLI (cli.py, exposed via the ocrmypdf console script), api.py offers a small, well-documented Python surface centered on a single ocr() function whose docstring explicitly documents concurrency caveats (plugin sets and max_image_mpixels are process-global, so differing configurations need separate processes) — an unusually candid piece of API documentation. The plugin specification (pluginspec.py) gives third-party engine authors a narrow, well-typed contract to implement, evidenced by real community plugins (OCRmyPDF-EasyOCR, OCRmyPDF-PaddleOCR, OCRmyPDF-AppleOCR) built against it without needing to touch core code.