Arabic PDF text extraction with ligature and RTL fixes
Project description
PDF2Text-Arabic
Arabic PDF text extraction built on PyMuPDF. Fixes ligature decomposition, RTL ordering, table extraction, and other issues that make raw PyMuPDF output unusable for Arabic.
What it fixes
| # | Problem | Fix |
|---|---|---|
| 1 | Ligature decomposition — PyMuPDF breaks Arabic ligatures (الله, لأ, لإ) into LTR-ordered zero-width chars | Detects zero-width clusters, reverses to RTL order |
| 1b | Lam-Alef swap — لا ligature decomposed as ال (alef before lam) | Detects width ratio, swaps to correct order |
| 2 | Presentation Forms — Returns U+FB50–FDFF / U+FE70–FEFF instead of standard Arabic | NFKC normalization |
| 3 | Line splitting — One visual line split into multiple rawdict lines at same y | Y-coordinate merging with tolerance |
| 4 | Number reversal — RTL sorting reverses digit sequences (2019 → 9102) | Detects LTR digit runs, reverses back |
| 5 | Arabic↔digit spacing — No space between Arabic text and numbers | Regex-inserts spaces at boundaries |
| 6 | Artifact spaces — Space chars with overlapping bboxes cause false word breaks | Only honors spaces with physical gaps > 0.5px |
| 7 | Invisible chars — Zero-width joiners, BOM, LTR/RTL marks, kashida | Stripped in post-processing |
Install
pip install pdf2text-arabic
From source:
pip install .
# or with uv
uv pip install .
Quick start
Python API
from pdf2text_arabic import extract_pdf, extract_page
# Extract entire PDF
text = extract_pdf("document.pdf")
# With cropping (remove headers/page numbers)
text = extract_pdf("document.pdf", crop_top=50, crop_bottom=30)
# Crop by percentage
text = extract_pdf("document.pdf", crop_top=5, crop_bottom=3, crop_unit="pct")
# Disable footnote separator detection
text = extract_pdf("document.pdf", detect_footer=False)
AI-friendly API (recommended for agents)
from pdf2text_arabic import extract_pdf_result, get_capabilities
caps = get_capabilities()
result = extract_pdf_result("document.pdf", on_empty="warn")
print(result.pages_total)
print(result.pages_with_text)
print(result.empty_pages)
print(result.warnings)
print(result.text)
Agent contract:
- Prefer
extract_pdf_result()over parsing logs. - Do not manually reorder Arabic text after extraction.
- Use
get_capabilities()before enabling OCR-specific behavior.
Single page
import fitz
from pdf2text_arabic import extract_page
doc = fitz.open("document.pdf")
text = extract_page(doc[0], crop_top=50, crop_bottom=30)
doc.close()
CLI
# Process all PDFs in a directory
pdf2text-arabic -i ./download -o ./output/plain_text
# Single file
pdf2text-arabic -f document.pdf -o ./output
# With cropping
pdf2text-arabic -i ./download --crop-top 50 --crop-bottom 30
# Crop by percentage, no footer detection
pdf2text-arabic -i ./download --crop-top 5 --crop-bottom 3 --crop-unit pct --no-footer
API reference
extract_pdf(pdf_path, **kwargs) → str
Extract text from all pages of a PDF.
| Parameter | Type | Default | Description |
|---|---|---|---|
pdf_path |
str |
— | Path to the PDF file |
crop_top |
float |
0 |
Crop from top of each page |
crop_bottom |
float |
0 |
Crop from bottom of each page |
crop_unit |
"px" | "pct" |
"px" |
Unit: points or percentage of page height |
detect_footer |
bool |
True |
Auto-detect footnote separator lines and exclude content below |
extract_page(page, **kwargs) → str
Extract text from a single fitz.Page. Same parameters as extract_pdf (except pdf_path).
extract_pdf_result(pdf_path, **kwargs) → ExtractionResult
Structured output for AI/automation:
text: final extracted textpages_total: total page countpages_with_text: number of non-empty extracted pagesempty_pages: list of page numbers that produced empty outputwarnings: machine-readable warning tokens (example:empty_page:3)
Features
Table extraction
Tables are automatically detected via PyMuPDF's find_tables(), extracted with proper Arabic cell ordering, and formatted as pipe-delimited text. Merged cells are filled down so every row is self-contained:
الجهات | عدد المقاعد | مقر الدائرة الانتخابية
طنجة – تطوان – الحسيمة | 2 | ولاية جهة فاس - مكناس
الشرق | 2 | ولاية جهة فاس - مكناس
فاس - مكناس | 2 | ولاية جهة فاس - مكناس
Footer detection
Automatically detects horizontal separator lines (both vector drawings and text-based dashes) in the bottom 40% of each page and excludes footnote text below them. Handles non-selectable drawn lines and selectable ------ text.
Page cropping
Crop headers and page numbers by fixed pixel amount or percentage of page height.
Project structure
pdf2text_arabic/
├── __init__.py # Public API: extract_pdf, extract_page
├── _chars.py # Character-level ligature/overlap fixes
├── _text.py # RTL text building, cleaning, line merging
├── _tables.py # Table detection and formatting
├── _footer.py # Footer separator detection
├── _extract.py # Page/PDF extraction orchestration
└── cli.py # CLI entry point
Integration with other projects
pip install pdf2text-arabic
from pdf2text_arabic import extract_pdf
def extract_law_text(path: str) -> str:
return extract_pdf(path, crop_top=50, crop_bottom=30, detect_footer=True)
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 pdf2text_arabic-0.1.5.tar.gz.
File metadata
- Download URL: pdf2text_arabic-0.1.5.tar.gz
- Upload date:
- Size: 17.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a537601680688fb428118aed6bf7fa8db49f762e9c9ce9b834a5d6d329cfe308
|
|
| MD5 |
55706d71dedc272f46e8cd3f3620cd0e
|
|
| BLAKE2b-256 |
f1c564077871e958cac71e0349baead477e3154dc9838616210849ff1b751301
|
Provenance
The following attestation bundles were made for pdf2text_arabic-0.1.5.tar.gz:
Publisher:
publish.yml on crazytajdine/PDF2Text-Arabic
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pdf2text_arabic-0.1.5.tar.gz -
Subject digest:
a537601680688fb428118aed6bf7fa8db49f762e9c9ce9b834a5d6d329cfe308 - Sigstore transparency entry: 1239247110
- Sigstore integration time:
-
Permalink:
crazytajdine/PDF2Text-Arabic@0ee44702dadf5ef8e3364772645d9bd2fd94df50 -
Branch / Tag:
refs/tags/0.1.5 - Owner: https://github.com/crazytajdine
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@0ee44702dadf5ef8e3364772645d9bd2fd94df50 -
Trigger Event:
release
-
Statement type:
File details
Details for the file pdf2text_arabic-0.1.5-py3-none-any.whl.
File metadata
- Download URL: pdf2text_arabic-0.1.5-py3-none-any.whl
- Upload date:
- Size: 19.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
56e7ab575117f83ce5633ef68a1cac491378b5aac1bbdb03e84c1bbf5e515cb2
|
|
| MD5 |
3f7da50a2bbe15d1707b521588b4b0ff
|
|
| BLAKE2b-256 |
6ca8fac4ddc5a5f942b9227b9d8122b054364388498f72862a336574a59ca4cb
|
Provenance
The following attestation bundles were made for pdf2text_arabic-0.1.5-py3-none-any.whl:
Publisher:
publish.yml on crazytajdine/PDF2Text-Arabic
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pdf2text_arabic-0.1.5-py3-none-any.whl -
Subject digest:
56e7ab575117f83ce5633ef68a1cac491378b5aac1bbdb03e84c1bbf5e515cb2 - Sigstore transparency entry: 1239247112
- Sigstore integration time:
-
Permalink:
crazytajdine/PDF2Text-Arabic@0ee44702dadf5ef8e3364772645d9bd2fd94df50 -
Branch / Tag:
refs/tags/0.1.5 - Owner: https://github.com/crazytajdine
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@0ee44702dadf5ef8e3364772645d9bd2fd94df50 -
Trigger Event:
release
-
Statement type: