PDF to Markdown converter with OCR support — local, fast, accurate.
Project description
Sahaf
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 (95.67% accuracy) 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 — Marker with 95.67% benchmark accuracy
- 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 .
Marker models (~2-3GB) are downloaded automatically on first conversion.
Quick Start
sahaf
Open http://localhost:8000 in your browser.
How It Works
- Upload — drag & drop a PDF or EPUB file
- Classify — PyMuPDF analyzes PDF type; EPUB chapters are counted
- Select range (optional) — pick specific pages or chapters to convert
- Convert — Marker processes PDF; ebooklib + markdownify handles EPUB
- Split (optional) — choose how many parts to split the output into
- Download — get a single
.mdor 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) + 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+
- 4-6GB RAM (when Marker models are loaded)
- GPU strongly recommended for PDF — CPU-only is extremely slow (~1 hour for a 27-page mixed PDF on i5 + 40GB RAM). A CUDA-capable GPU converts the same file in minutes.
- EPUB conversion is lightweight — no GPU needed, runs instantly
License
GPL-3.0
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file sahaf-0.2.1.tar.gz.
File metadata
- Download URL: sahaf-0.2.1.tar.gz
- Upload date:
- Size: 28.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f0b0f33a591ff71f8c5afaff03d6e3b82d0c45e47477925ba2dace01ec9fa1b2
|
|
| MD5 |
8a10db056f534b6dd143ef4af9471e1d
|
|
| BLAKE2b-256 |
e580ae213d40fbb587608e73b62669fcff0f267ae1c8751f01ceab74617559e3
|
Provenance
The following attestation bundles were made for sahaf-0.2.1.tar.gz:
Publisher:
publish.yml on arikusi/sahaf
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
sahaf-0.2.1.tar.gz -
Subject digest:
f0b0f33a591ff71f8c5afaff03d6e3b82d0c45e47477925ba2dace01ec9fa1b2 - Sigstore transparency entry: 1268856329
- Sigstore integration time:
-
Permalink:
arikusi/sahaf@269b9ed07a87ecd9df34996f86280b6e5791148e -
Branch / Tag:
refs/tags/v0.2.1 - Owner: https://github.com/arikusi
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@269b9ed07a87ecd9df34996f86280b6e5791148e -
Trigger Event:
release
-
Statement type:
File details
Details for the file sahaf-0.2.1-py3-none-any.whl.
File metadata
- Download URL: sahaf-0.2.1-py3-none-any.whl
- Upload date:
- Size: 27.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4cf6ad1aa2d385f59a2547b024be49fc52774b49e44f237190f11b53176c9bc0
|
|
| MD5 |
1e7e86f69021598089d9e30c1d583191
|
|
| BLAKE2b-256 |
cf1f002a4625b5225d6a377ee3226ed76cd72b8eddc03b0f409268c19ab3f32c
|
Provenance
The following attestation bundles were made for sahaf-0.2.1-py3-none-any.whl:
Publisher:
publish.yml on arikusi/sahaf
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
sahaf-0.2.1-py3-none-any.whl -
Subject digest:
4cf6ad1aa2d385f59a2547b024be49fc52774b49e44f237190f11b53176c9bc0 - Sigstore transparency entry: 1268856400
- Sigstore integration time:
-
Permalink:
arikusi/sahaf@269b9ed07a87ecd9df34996f86280b6e5791148e -
Branch / Tag:
refs/tags/v0.2.1 - Owner: https://github.com/arikusi
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@269b9ed07a87ecd9df34996f86280b6e5791148e -
Trigger Event:
release
-
Statement type: