Skip to main content

Sahaf

CI PyPI Downloads License: GPL v3

Local PDF & EPUB to Markdown converter with automatic digital/scanned detection, OCR support, smart splitting, and page-range selection. Converts books to clean, self-contained Markdown files with embedded images using Marker and Surya OCR (90+ languages). No cloud APIs — runs entirely on your hardware.

Features

  • PDF & EPUB support — handles both formats natively
  • Automatic PDF classification — detects digital, scanned, or mixed PDFs via PyMuPDF
  • High-accuracy conversion — works with both Marker 1.x and Marker 2.x, whichever you install
  • Built-in OCR — Surya OCR supports 90+ languages (Turkish, English, Arabic, etc.)
  • Page/chapter range selection — convert only a specific section of the book (e.g. pages 19-88)
  • Smart splitting — split output into N parts, cutting at heading/paragraph boundaries instead of mid-sentence
  • Self-contained output — images embedded as base64 directly in Markdown, no separate files
  • Split preview — see exactly how parts will be divided before downloading
  • Bilingual UI — Turkish / English interface with one-click toggle
  • Dark/light theme — lavender-toned design, persistent toggle
  • Drag & drop UI — clean single-page web interface

Install

pip install sahaf

Or from source:

git clone https://github.com/arikusi/sahaf.git
cd sahaf
pip install -e .

Models are downloaded automatically on first conversion.

Choosing Marker 1 or Marker 2

Both lines work — Sahaf detects which one is installed and configures it accordingly. The difference only matters for scanned PDFs; digital PDFs and EPUBs convert the same either way.

Marker 1.x — OCR in-process, nothing else to install

pip install sahaf "marker-pdf<2"

OCR runs inside the Python process with torch, on any CUDA GPU including pre-Ampere cards (GTX 16xx, RTX 20xx). The safe choice for scanned books.

Marker 2.x — faster and more accurate, needs an OCR server

pip install sahaf

Marker 2 reads the PDF text layer and only OCRs what needs it (76.0% on olmOCR-bench in balanced mode). But its OCR model is served out of process, so scanned pages need one of:

  • Docker plus the NVIDIA Container Toolkit, on an Ampere or newer GPU — the server runs in bfloat16, which needs compute capability 8.0+. On Arch: sudo pacman -S nvidia-container-toolkit && sudo nvidia-ctk runtime configure --runtime=docker && sudo systemctl restart docker
  • llama.cpp (llama-server on your PATH) — works anywhere, but CPU inference is slow on book-length documents

Sahaf classifies each PDF before converting, so under Marker 2 a digital book runs in fast mode and never starts that server.

Quick Start

sahaf

Open http://localhost:8000 in your browser.

Uploads and converted files land in ~/.local/share/sahaf. To keep them elsewhere:

SAHAF_DATA_DIR=./sahaf-data sahaf

How It Works

  1. Upload — drag & drop a PDF or EPUB file
  2. Classify — PyMuPDF analyzes PDF type; EPUB chapters are counted
  3. Select range (optional) — pick specific pages or chapters to convert
  4. Convert — Marker processes PDF; ebooklib + markdownify handles EPUB
  5. Split (optional) — choose how many parts to split the output into
  6. Download — get a single .md or a ZIP with split parts, all images embedded inline

API

Method Path Description
POST /api/upload Upload PDF/EPUB, returns task_id
GET /api/classify/{task_id} Detect PDF type + page count, or EPUB chapter count
POST /api/convert/{task_id}?page_from=&page_to= Start conversion (optional page range)
GET /api/status/{task_id} Poll conversion progress
GET /api/result/{task_id} Get markdown + image list
GET /api/download/{task_id} Download .md with embedded images
GET /api/download/{task_id}/zip?parts=N Download ZIP with N split .md files
GET /api/split-preview/{task_id}?parts=N Preview split structure before download

Tech Stack

  • Backend: FastAPI + Uvicorn
  • PDF Classification: PyMuPDF
  • PDF Conversion: Marker (marker-pdf 1.x or 2.x) + Surya OCR
  • EPUB Conversion: ebooklib + markdownify
  • Smart Splitting: Custom algorithm — heading/HR/paragraph boundary detection
  • Frontend: Vanilla HTML/CSS/JS + marked.js
  • i18n: TR/EN with client-side toggle

Requirements

  • Python 3.10 – 3.13 (Marker's dependency stack pins pillow<11, which has no 3.14 wheels)
  • 4-6GB RAM (when Marker models are loaded)
  • GPU strongly recommended for scanned PDFs — those go through the OCR model; CPU inference is slow on book-length documents
  • 4GB+ VRAM — below 8GB, Sahaf automatically switches Marker to memory settings that fit, so a 6GB card converts scanned books without any configuration. Set FOUNDATION_CHUNK_SIZE or the *_BATCH_SIZE variables yourself to override
  • Under Marker 2, digital PDFs run without the OCR server at all
  • EPUB conversion is lightweight — no GPU needed, runs instantly

License

GPL-3.0

Download files

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

Source Distribution

sahaf-0.2.3.tar.gz (48.5 kB view details)

Uploaded Source

Built Distribution

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

sahaf-0.2.3-py3-none-any.whl (44.2 kB view details)

Uploaded Python 3

File details

Details for the file sahaf-0.2.3.tar.gz.

File metadata

  • Download URL: sahaf-0.2.3.tar.gz
  • Upload date:
  • Size: 48.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for sahaf-0.2.3.tar.gz
Algorithm Hash digest
SHA256 6e2e044ad74c8d06ce644dd898ba07e9e3d1b2a10bb4b9c0d75079e560099765
MD5 1108ebe34ad82c6cf21c22fc506182e5
BLAKE2b-256 ecfbfe2e7e41042f3fc2c7769fdd94a37766c0025243de94dda19eb65af766b7

See more details on using hashes here.

Provenance

The following attestation bundles were made for sahaf-0.2.3.tar.gz:

Publisher: publish.yml on arikusi/sahaf

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file sahaf-0.2.3-py3-none-any.whl.

File metadata

  • Download URL: sahaf-0.2.3-py3-none-any.whl
  • Upload date:
  • Size: 44.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for sahaf-0.2.3-py3-none-any.whl
Algorithm Hash digest
SHA256 dcca4633a67d85a0c38612dabe319781ee3744e33575ceb9dee172e55ed2d5c4
MD5 dab3545098393ed8e5269b4fbddfc3b6
BLAKE2b-256 f39de4138d7b7712e3152890c7762cbce1c9cf5ae9c99d1823446eeb33aaeede

See more details on using hashes here.

Provenance

The following attestation bundles were made for sahaf-0.2.3-py3-none-any.whl:

Publisher: publish.yml on arikusi/sahaf

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

0.2.3 This release

2 files

0.2.2

2 files

0.2.1

2 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