Skip to main content

Fast PDF text and image/table extraction using PyMuPDF + YOLOv8 DocLayNet ONNX

Project description

SARAL Docling

Fast PDF text and image/table extraction.
Powered by PyMuPDF + YOLOv8 DocLayNet (bundled) — no model file needed.

pip install "saraldocling[cpu]"
saraldocling paper.pdf

License: This package bundles yolov8n-doclaynet.onnx which is released under AGPL-3.0. The entire saraldocling package is therefore also distributed under AGPL-3.0.
Full text: https://www.gnu.org/licenses/agpl-3.0.html

No OCR. Only PDFs with a native text layer are supported.
For scanned PDFs, pre-process with ocrmypdf first (see Limitations).


Table of contents


How it works

Stage What happens
1 — Text PyMuPDF extracts the native text layer from every page
2 — Images/Tables YOLOv8 DocLayNet (bundled ONNX) detects Picture and Table regions, re-renders those regions at 300 DPI, and saves them as PNGs

The ONNX model (yolov8n-doclaynet.onnx) is included inside the wheel — there is no separate download step.


Installation

Pick exactly one of [cpu] or [gpu]. Installing both puts two competing onnxruntime builds in your environment.

CPU — works everywhere, no GPU required

pip install "saraldocling[cpu]"

GPU — NVIDIA CUDA

Requires CUDA 11.8+ and cuDNN on the host.
Compatibility: https://onnxruntime.ai/docs/execution-providers/CUDA-ExecutionProvider.html

pip install "saraldocling[gpu]"

Apple Silicon — CoreML acceleration (M1/M2/M3/M4)

The [cpu] wheel works fine on ARM. For ANE-accelerated inference:

pip install "saraldocling[cpu]"
pip install onnxruntime-silicon      # replaces the cpu wheel

Linux / Debian server — one-time system packages

sudo apt-get install -y libgl1 libglib2.0-0
pip install "saraldocling[cpu]"

From source (development)

git clone https://github.com/yourname/saraldocling
cd saraldocling
pip install -e ".[cpu,dev]"

CLI usage

The bundled model runs automatically. No --model flag needed.

# Full pipeline: text + image/table crops (default)
saraldocling paper.pdf

# Text only — skip YOLO, much faster
saraldocling paper.pdf --no-images

# Custom output directory
saraldocling paper.pdf -o ./results

# Preview extracted text in the terminal
saraldocling paper.pdf --preview 800

# One .txt file per page
saraldocling paper.pdf --pages

# GPU (CUDA) — needs saraldocling[gpu]
saraldocling paper.pdf --gpu

# Apple Silicon CoreML
saraldocling paper.pdf --coreml

# Debug: see every step logged
saraldocling paper.pdf -v

# All flags
saraldocling --help

What gets written

paper_out/
├── extracted_text.txt        ← all pages joined
├── manifest.json             ← JSON summary of the run
├── pages/                    ← only with --pages
│   ├── page_001.txt
│   └── page_002.txt
└── extracted_images/         ← Picture + Table crops at 300 DPI
    ├── p0_Picture_231.png
    └── p2_Table_445.png

Output directory is auto-named <pdf_stem>_out/ next to the PDF unless you pass -o.


Python API

One-call pipeline (recommended)

from saraldocling import parse_pdf, ParseConfig

# Full pipeline — bundled model used automatically
result = parse_pdf(ParseConfig(
    pdf_path="paper.pdf",
    output_dir="./out",
))

print(result.num_pages)       # int
print(result.text[:500])      # full extracted text
print(result.image_paths)     # list[str] of saved PNG paths
# Text only — skip YOLO
result = parse_pdf(ParseConfig(
    pdf_path="paper.pdf",
    output_dir="./out",
    extract_images=False,
))

Lower-level: text extraction only

from saraldocling import PDFProcessor

with PDFProcessor("paper.pdf") as proc:
    full_text  = proc.extract_text()
    page_text  = proc.extract_text_by_page(1)      # 0-indexed
    page_image = proc.extract_page_image(0, dpi=150)  # PIL.Image

Lower-level: YOLO extraction only

from saraldocling import ImageExtractor

# CPU (default)
with ImageExtractor() as ext:
    paths = ext.extract_images_from_pdf("paper.pdf", "./out")

# GPU
with ImageExtractor(providers=["CUDAExecutionProvider", "CPUExecutionProvider"]) as ext:
    paths = ext.extract_images_from_pdf("paper.pdf", "./out")

# Apple Silicon
with ImageExtractor(providers=["CoreMLExecutionProvider", "CPUExecutionProvider"]) as ext:
    paths = ext.extract_images_from_pdf("paper.pdf", "./out")

Using a custom model (advanced)

result = parse_pdf(ParseConfig(
    pdf_path="paper.pdf",
    output_dir="./out",
    model_path="/path/to/custom.onnx",   # overrides the bundled model
))

Output structure

manifest.json example:

{
  "pdf": "/abs/path/to/paper.pdf",
  "pages": 12,
  "text_chars": 38201,
  "text_file": "/abs/path/to/paper_out/extracted_text.txt",
  "per_page_files": [],
  "image_paths": [
    "/abs/path/to/paper_out/extracted_images/p0_Picture_231.png",
    "/abs/path/to/paper_out/extracted_images/p3_Table_112.png"
  ]
}

Crop filenames follow the pattern p<page>_<Label>_<x>.png where <Label> is either Picture or Table.


FastAPI / server deployment

saraldocling is a plain pip package — drop it into any FastAPI app on Debian with no special system configuration beyond the apt packages above.

Minimal endpoint

# main.py
import shutil, uuid
from pathlib import Path

from fastapi import FastAPI, File, UploadFile
from fastapi.responses import JSONResponse
from saraldocling import parse_pdf, ParseConfig

OUTPUT_ROOT = Path("/tmp/saraldocling")

app = FastAPI()

@app.post("/parse")
async def parse(file: UploadFile = File(...)):
    job_dir = OUTPUT_ROOT / str(uuid.uuid4())
    job_dir.mkdir(parents=True, exist_ok=True)
    pdf_path = job_dir / file.filename

    with open(pdf_path, "wb") as f:
        shutil.copyfileobj(file.file, f)

    result = parse_pdf(ParseConfig(
        pdf_path=str(pdf_path),
        output_dir=str(job_dir),
    ))

    return JSONResponse({
        "pages":       result.num_pages,
        "text":        result.text,
        "image_paths": result.image_paths,
    })
pip install "saraldocling[cpu]" fastapi uvicorn python-multipart
uvicorn main:app --host 0.0.0.0 --port 8000

Pre-load the ONNX session at startup

Creating an ImageExtractor loads and optimises the ONNX graph (~300 ms). Do this once at startup and reuse it — session.run() is thread-safe:

from contextlib import asynccontextmanager
from fastapi import FastAPI
from saraldocling import ImageExtractor

extractor: ImageExtractor | None = None

@asynccontextmanager
async def lifespan(app: FastAPI):
    global extractor
    extractor = ImageExtractor()      # loads bundled model
    yield
    extractor.close()

app = FastAPI(lifespan=lifespan)

# then in your endpoint, call extractor.extract_images_from_pdf() directly

Configuration reference (ParseConfig)

Field Default Description
pdf_path required Path to the source PDF
output_dir required Root directory for all output files
extract_images True Run YOLO stage. False = text only
model_path None Custom .onnx path. None = use bundled model
conf_threshold 0.30 YOLO confidence cutoff
nms_threshold 0.45 NMS IoU cutoff
min_box_size 30 Minimum crop side-length in pixels
num_workers cpu_count Thread-pool size for page processing
onnx_providers ["CPUExecutionProvider"] ONNX Runtime execution providers

DocLayNet labels

The model detects 11 document element types.
Only Picture and Table regions are saved as crops.

Caption   Footnote   Formula   List-item   Page-footer
Page-header   Picture   Section-header   Table   Text   Title

Limitations

Not OCR — requires a native text layer in the PDF. For scanned documents:

pip install ocrmypdf
ocrmypdf scanned.pdf scanned_ocr.pdf
saraldocling scanned_ocr.pdf

Thread safety — both PDFProcessor and ImageExtractor use internal mutex locks (mirroring the Go SafeDocument). Safe for concurrent use.


License

This package is distributed under AGPL-3.0 because it bundles yolov8n-doclaynet.onnx, which carries that licence.

What this means in practice:

  • Free to use for any purpose, including commercial.
  • If you modify the source and run it as a network service, you must make your modified source available under AGPL-3.0.
  • The full licence text is at https://www.gnu.org/licenses/agpl-3.0.html

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

saraldocling-1.0.0.tar.gz (10.6 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

saraldocling-1.0.0-py3-none-any.whl (10.6 MB view details)

Uploaded Python 3

File details

Details for the file saraldocling-1.0.0.tar.gz.

File metadata

  • Download URL: saraldocling-1.0.0.tar.gz
  • Upload date:
  • Size: 10.6 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.4

File hashes

Hashes for saraldocling-1.0.0.tar.gz
Algorithm Hash digest
SHA256 b44405f456472e71a2cc40f5832b6257e8a7c3bd902b50bd012b8d13db57641e
MD5 f985ae447422255d6020ea1276979784
BLAKE2b-256 1419d72e050f49839d621a5aa9b938ab384498e1e54f066d52913d18d5269389

See more details on using hashes here.

File details

Details for the file saraldocling-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: saraldocling-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 10.6 MB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.4

File hashes

Hashes for saraldocling-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 115368e4e6a74ec02638094a3dbf790b32ba0398cf4cec67471965087f25ccb7
MD5 ac1d2ecb874aeb260953cba439ecc4fe
BLAKE2b-256 30b90d779f9c8773990f7b3e97cf0d628c90f4f0b40d0b10e579799bbd27ba13

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page