Skip to main content

pro-ledin-ocr

skills.sh

Layered extraction for text PDFs, scanned PDFs, and images (PNG/JPG/TIFF/HEIC/WEBP). Readable PDF text layers are extracted directly and tables are reconstructed with Camelot. Scans use Poppler and Tesseract; EasyOCR, PaddleOCR, structured PaddleOCR-VL over MLX, and automated vision are opt-in engines.

  • Import name: pro.ledin.ocr
  • Console scripts: ocr, peepshow-sink-ocr
  • Interactive vision handoff: python -m pro.ledin.ocr.vision_handoff
  • PyPI: pro-ledin-ocr

Install

pip install pro-ledin-ocr             # baseline, Camelot tables, OpenAI-compatible vision
pip install "pro-ledin-ocr[easyocr]" # EasyOCR
pip install "pro-ledin-ocr[paddle]"  # PaddleOCR
pip install "pro-ledin-ocr[paddle-vl]" # PaddleOCR-VL document parser client
pip install "pro-ledin-ocr[all]"     # all optional Python engines/tools

Baseline PDF OCR also needs Poppler (pdftoppm, pdftotext, pdfinfo) and Tesseract with required language data:

brew install poppler tesseract tesseract-lang       # macOS
sudo apt install poppler-utils tesseract-ocr-all    # Debian/Ubuntu

Windows and engine-specific setup: skills/ocr/references/engine-setup.md.

CLI

ocr myfile.pdf --probe                         # triage only; NDJSON result
ocr myfile.pdf --format md,json                # multiple formats to stdout
ocr myfile.pdf --format md,txt,json --out results/
ocr report.pdf --table-flavor lattice            # ruled tables
ocr report.pdf --table-flavor stream             # borderless tables
ocr report.pdf --no-tables                        # legacy linear text
ocr scan.png --format md
ocr russian_doc.pdf --lang rus+eng --format md
ocr scan.pdf --preprocess full
ocr slides.pdf --engine vision --pages 9,12 \
  --vision-api-key "$KEY" --vision-model my-vision-model \
  --vision-prompt "Preserve tables and empty cells"
ocr scan.pdf --auto-escalate easyocr,vision \
  --vision-api-key "$KEY" --vision-model my-vision-model
export OCR_VISION_API_KEY="$KEY"
export OCR_VISION_MODEL="my-vision-model"
export OCR_VISION_API_URL="http://localhost:8000/v1"
ocr scan.pdf --engine vision
ocr table.pdf --engine paddleocr-vl-mlx \
  --paddle-vl-server-url http://127.0.0.1:8111/ \
  --paddle-vl-model PaddlePaddle/PaddleOCR-VL-1.6

Engine resolution is explicit: --engine overrides OCR_ENGINE, otherwise tesseract is used. There is no auto engine. Valid engines are tesseract, easyocr, paddleocr, paddleocr-vl-mlx, and vision. The MLX engine runs full layout parsing inside PaddleOCRVL; its loopback service handles only the VLM stage and must not receive document images directly.

For readable text PDFs, table extraction is a native pre-OCR stage rather than an OCR engine. It is enabled by default. Camelot auto is attempted first and non-stream page results are compared with a stream candidate. Override with --table-flavor lattice|stream|network|hybrid, or disable with --no-tables. The ml flavor is intentionally unsupported because this path must not require a visual model.

--auto-escalate overrides OCR_AUTO_ESCALATE. Both accept an ordered, comma-separated chain such as easyocr,vision. OCR validates every dependency before baseline work. Flagged pages run through every engine in chain, in order. Any attempt failure stops run and prevents workflow-cache write. Without a configured chain, quality report recommends targeted page rerun.

Interactive multimodal-agent handoff is separate and does not call vision API:

python -m pro.ledin.ocr.vision_handoff slides.pdf \
  --pages 9,12 --dpi 200 \
  --vision-prompt "Read visible text and preserve table structure"

Handoff accepts --pages, --dpi, --vision-prompt, and --vision-prompt-file; it intentionally has no --max-pages option.

See ocr --help for complete flag reference.

Output formats

--format accepts md, txt, json, comma-separated combinations, or all (md,txt,json). Comma-format behavior introduced in 0.4.0 remains unchanged: with multiple formats, --out is a directory and files use input stem. Multiple inputs also use directory mode; duplicate stems are rejected. JSON output for multiple inputs requires --out; --searchable-pdf accepts one input.

--json-report was removed in 0.4.0. Use --format json --out report.json for JSON-only output or --format md,json --out results/ for multiple files. One invocation cannot mix stdout format with independently named JSON sidecar.

Markdown replaces accepted table regions with pipe tables. Complex or spanning grids use HTML tables inside Markdown; uncertain spans are flattened and marked with table_spans_flattened. Plain-text output remains the original linear text. JSON pages add a tables array containing normalized rows, bbox, Camelot flavor, parsing metrics, validation coverage, rendered representation, and issues. Rejected candidates never replace source text.

Peepshow sink

peepshow-sink-ocr reads peepshow JSON from stdin, recognizes primary frames, and atomically writes <outputDir>/ocr.json:

peepshow video.mp4 --sink ocr
peepshow video.mp4 \
  --sink-cmd 'peepshow-sink-ocr --engine tesseract --lang rus+eng'

Named sink configuration uses PEEPSHOW_SINK_OCR_* variables, including PEEPSHOW_SINK_OCR_AUTO_ESCALATE. Automated vision works in sink mode with the base package's OpenAI client, key, and model. Interactive handoff does not run inside sink processes. See skills/ocr/references/peepshow-sinks.md.

Library

from pro.ledin import ocr

pages = ocr.recognize(
    "scan.pdf",
    ocr.RecognizeOptions(
        engine="vision",
        vision_api_key="key",
        vision_model="model",
        vision_prompt="Preserve checkbox states and labels.",
        extract_tables=True,
        table_flavor="auto",
    ),
)
markdown = ocr.to_markdown(pages, "scan.pdf")

recognize() never calls sys.exit(); catch ocr.OcrError for recoverable failures such as unsupported input, missing dependencies, or vision config.

Call probe_engine_requirements() before recognition when an orchestrator needs structured, side-effect-free dependency diagnostics. It does not import OCR engines, download models, access network, or read generic credential variables:

requirement = ocr.probe_engine_requirements(
    "vision",
    vision_api_key="key",
    vision_model="model",
)
if not requirement.available:
    print(requirement.to_dict())

probe_pdf_requirements() covers PDF rendering and text-layer extraction:

requirement = ocr.probe_pdf_requirements()

RequirementResult carries engine, stable code, backward-compatible missing_component, complete missing_components tuple, components_relation, ocr_extra, component_type, and optional first_run_note. missing_component is always first tuple item. components_relation is all when every listed component is required and any when installing one listed component is enough — PDF backends use any because Poppler or PyMuPDF satisfies them. Paddle preflight reports both paddleocr and paddle when both import modules are absent. EasyOCR, OpenAI, binary, and configuration failures use one-item tuples. Recognition raises OcrRequirementError, an OcrError subclass, with same data under .result and direct attributes. Existing numeric .code remains CLI exit status; .requirement_code/.stable_code is stable requirement code. Current codes: ok, missing_tesseract_binary, missing_easyocr_package, missing_easyocr_dependency, missing_paddleocr_package, missing_paddle_runtime, missing_openai_package, missing_vision_api_key, missing_vision_model, unsupported_engine, missing_pdf_render_backend, and missing_pdf_text_backend.

Probes stay cheap by checking module specs rather than importing. A module can still exist by spec and fail to import (broken build, namespace shell, failed native library load), so every engine import site converts that failure into the same OcrRequirementError with the same stable code. A raw ImportError never escapes recognition.

EasyOCR transitive failures use missing_easyocr_dependency and name the failing component when determinable, for example numpy, pillow, or torch. Optional local helpers degrade safely: broken pytesseract falls back to the Tesseract CLI; broken Pillow skips basic preprocessing; broken OpenCV, NumPy, or Pillow in enhanced/full preprocessing falls back to basic, then the original image when Pillow is also unusable.

Python package failures identify distribution extra such as pro-ledin-ocr[easyocr]; platform-specific binary/runtime commands remain in the engine setup guide rather than assuming a source-tree ocr.py invocation.

RecognizeOptions(verbose=True) enables progress logging only. The 17-line capability dump is a CLI diagnostic (ocr -v); library callers opt in with ocr.recognize(path, options, caps=ocr.Caps(report=True)).

Engine tiers

Tier Engine Best for Cost
0 Text layer + Camelot Real text layers and native tables Free, local
1 tesseract (default) Clean scans and typed text Free
2 easyocr Handwriting and degraded scans Free, heavy
2.5 paddleocr CJK, multilingual, angled text Free, heavy
2.75 paddleocr-vl-mlx Local tables and complex document layout on Apple Silicon Free, heavy
3 vision Automated tables, charts, complex layouts API cost
Handoff python -m pro.ledin.ocr.vision_handoff Interactive agent reading Agent/model tokens

Full docs: skills/ocr/SKILL.md, skills/ocr/references/engines.md, skills/ocr/references/engine-setup.md, skills/ocr/references/peepshow-sinks.md, and skills/ocr/references/troubleshooting.md.

Development

uv sync --extra dev
uv run --extra dev pytest
uv build

License

MIT

Metadata

Release files for pro-ledin-ocr 0.6.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pro-ledin-ocr 0.6.1
File Size Uploaded
pro_ledin_ocr-0.6.1.tar.gz 78.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for pro-ledin-ocr 0.6.1
File Interpreter ABI Platform
pro_ledin_ocr-0.6.1-py3-none-any.whl Python 3 none any Details

Total release size: 124.9 kB

Release files / pro_ledin_ocr-0.6.1.tar.gz

Download URL pro_ledin_ocr-0.6.1.tar.gz
Size 78.2 kB
Tags Source
SHA-256 checksum
How to use checksums
2793958ad4f27f95bf8f3d40144adcc3ef6a7b1dd9461fc90d98a8fc1e7502e0
BLAKE2b-256 checksum
How to use checksums
6ed5eab6fc6c0d5d12c8bb91bf63d7f88afc6f5c22bb9eadf43fe6f4e14e546c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / pro_ledin_ocr-0.6.1-py3-none-any.whl

Download URL pro_ledin_ocr-0.6.1-py3-none-any.whl
Size 46.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
cc402c721c02a254ec8770b9faf6c3bf4dfafe7509b3016c4a626c7bf8ba77e6
BLAKE2b-256 checksum
How to use checksums
82ed25de1c1b0e885d520f9751fdb1730fb8af9867b5e60fdc53683aea4b1038
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release history Release notifications | RSS feed

0.7.0

2 release files

This release

0.6.1 This release

2 release files

0.6.0

2 release files

0.5.1

2 release files

0.5.0

2 release files

0.4.0

2 release files

0.3.0

2 release files

0.2.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page