arafix
Recover broken Arabic text from PDFs — diagnose first, then apply a graded repair ladder. Not a single hammer, and not “just run OCR.”
| Core | Zero dependencies (stdlib only) for text stages 0–2 |
pip install "arafix[pdf]" — geometric extract + Arabic repair |
|
| Layout | Multi-column RTL, headers/footers, simple tables (layout=auto) |
| 1.0 | Core lexicon, smart BiDi/LTR, hybrid mojibake, stress-gated (FPR=0, RAR=100%) |
| Quality | Cluster-aware diacritics, PDF homoglyph fold, scientific metrics (MCS/DBR/BFE/SHDR) |
| Status | Stable 1.0.0 — production-ready for native Arabic PDF recovery |
Install
pip install arafix # text repair only
pip install "arafix[pdf]" # recommended — PDF extract
pip install "arafix[all]" # + fonttools (CMap / stage 3)
pip install "arafix[markitdown]" # MarkItDown plugin
30-second start
from arafix import repair_text, extract_pdf, PipelineConfig
# Presentation-form garbage → readable Arabic
print(repair_text("\ufee3\ufeae\ufea3\ufe92\ufe8e").text) # مرحبا
# Ambiguous lam-alef via embedded core lexicon
print(repair_text("صدرت المجالت العلمية").text) # صدرت المجلات العلمية
# Hybrid mojibake (windowed) — Latin/code left intact
print(repair_text("المÙCustomer Report (Status: 200 OK)").text)
# → المCustomer Report (Status: 200 OK)
# Native (not scanned) Arabic PDF
doc = extract_pdf("thesis.pdf")
print(doc.text)
print(doc.confidence, doc.pages[0].n_columns)
from arafix import reverse_visual_line, ReorderConfig
# Smart LTR: page ranges, currency parens, sentence period
print(reverse_visual_line("(140-125 .ص) ثحبلا عجرم"))
# → مرجع البحث (ص. 125-140)
print(reverse_visual_line(")00.052,1 DSU-( يفاصلا"))
# → الصافي (-USD 1,250.00)
arafix diagnose thesis.pdf -v
arafix extract thesis.pdf -o out.txt
arafix extract paper.pdf --layout full -v --tables
arafix eval thesis.pdf --truth thesis.txt --scientific
python scripts/eval_unified.py --pdf thesis.pdf --truth thesis.txt -v
python scripts/stress_test_report.py --skip-ultra
What it fixes (and what it doesn’t)
| Symptom | Cause | Stage / tool |
|---|---|---|
| Reversed letter order | Visual storage order | 2 + smart LTR |
Isolated Arabic glyphs (ﻣﺮﺣﺒﺎ) |
Presentation forms | 1 |
ال… / hybrid mojibake |
UTF-8 (or CP1256) misread | 0 windowed |
المجالت / االنترنيت |
Lam-alef broken before reorder | 1a→2→1b + core lexicon |
(ص. 140-125) page ranges |
LTR island + academic order | normalize_page_ranges |
(-USD 1,250.00) / 3.5% |
Accounting / percent islands | smart LTR + paren repair |
.2024 sentence glue |
Period stuck to year island | relocate_sentence_punctuation |
()مقدمة |
Engine bidi vs neutrals | geometric extract |
Misplaced harakat / َحرب |
Mn glued to wrong base | extract + clusters |
ی/ھ vs ي/ه |
PDF ToUnicode lookalikes | fold_pdf_homoglyphs |
| Two columns mixed | Line-joined gutters | layout (0.8+) |
| Empty / PUA soup | Broken ToUnicode / scan | 3 / 4 (OCR not shipped) |
Configuration highlights (1.0)
from arafix import PipelineConfig, NormalizeConfig, ReorderConfig, repair_text
cfg = PipelineConfig(
use_core_lexicon=True, # embedded micro-lexicon for ambiguous لا/ال
enable_lam_alef_repair=True,
normalize=NormalizeConfig(
strip_tatweel_in_pf_runs=True,
fold_pdf_homoglyphs=True, # set False for intentional Farsi Yeh
),
reorder=ReorderConfig(
smart_ltr_restore=True,
normalize_page_ranges=True,
repair_ltr_parens=True,
relocate_sentence_punct=True,
),
)
repair_text(broken, cfg)
Philosophy: never invent characters; never “fix just in case”; every decision carries evidence and confidence. Release gated by FPR = 0 and RAR ≥ 98% on the 50-pack stress corpus.
Further reading: INTEGRATING.md · DEPLOY.md · CHANGELOG.md · RELEASING.md · CITATION.cff
arafix — التوثيق العربي
استرجاع النص العربي من ملفات PDF المعطوبة. سلّمٌ من خمس درجات، لا مطرقةٌ واحدة.
المسألة
عندك ملف PDF عربي أصليّ — لا صورة ممسوحة، بل نصٌّ حقيقيّ مُصدَّر من Word. تفتحه فتقرؤه بلا عناء. تستخرجه ببايثون فيخرج:
| ما ترى | العلّة | الدرجة العلاجية |
|---|---|---|
ا ب ح ر م |
ترتيب بصريّ مخزَّن معكوساً | ٢ |
م ر ح ب ا متفرقة |
أشكال رسومية مطبوخة (U+FB50–FEFF) | ١ |
المتوسط |
موجيبيك — علّة أنبوبك لا علّة الملف | ٠ |
المجالت بدل المجلات |
رباط «ﻻ» فُكّ قبل إصلاح الاتجاه | ١أ+٢+١ب |
()مقدمة بدل (مقدمة) |
بِدي المحرّك يبعثر المحايدات | القراءة الهندسية |
نشُرت بدل نُشرت |
العَكس على المحارف لا على العناقيد | ٢ |
`` أو ????? |
خريطة ToUnicode تالفة | ٣ |
| لا شيء | لا طبقة نصية (ممسوح ضوئياً) | ٤ |
العلل خمس، والعلاجات خمسة، ولكلٍّ دواؤه. أكثر ما يُتداول من حلول يخلط بينها، فيطبّق دواء الثانية على الرابعة، ثم يستنتج أن «العربية مستحيلة في PDF».
تصحيحان لخرافتين شائعتين
❌ «الـ OCR هو الحل الأسرع والأدق للعربية.»
خطأ. OCR العربي آخر الدواء لا أوّله: أبطأ بمراتب، ويخطئ في الهمزة والتشكيل والأرقام، ويهدم بنية الجداول. لا تنزل إليه إلا حين تنعدم طبقة النص أصلاً (الدرجة ٤). ما دون ذلك يُحلّ بسطرٍ إلى عشرين.
❌ «الرباط ﻻ حرفان مثل fi في اللاتينية.»
خطأ، والفرق ليس تفصيلاً. رباط لام-ألف في العربية إلزاميّ لا اختياريّ: لا يوجد خطٌّ يرسم لاماً ثم ألفاً منفصلتين. فهو في ملف الـ PDF جليفٌ واحد. ومن فكّه إلى حرفين ثم عكس السطر، عكس الحرفين معه فصارت «لا» ← «ال». وهذا مصدر أشهر عطبٍ في استخراج العربية:
الانترنيت → االنترنيت المجلات → المجالت
الأطاريح → األطاريح الإجراء → اإلجراء
❌ «المشكلة في الحروف؛ فإن خرجت العربية سليمةً فقد نجوت.»
خطأ، وهو أخبث ما في الباب. الحروف أمتنُ ما في السطر، والترقيم أهشُّه.
المحايدات (( ) [ ] . ! ,) لا اتجاه لها في يونيكود، فيتنازعها ما حولها،
فتُخرج المحرّكاتُ عربيةً سليمةً وترقيماً مبعثراً:
(مقدمة الدراسة) → ()مقدمة الدراسة
الفقرة [أ-ج] هنا → ج[ هنا-الفقرة ]أ
ولاحظ أن ؟ و؛ تنجوان دائماً حيث تعطب ! و. — لأنهما عربيّتان
(صنف AL قويّ الاتجاه) لا محايدتين. من لا يقرأ العربية لا يرى العطب أصلاً.
❌ «
الميعني أن الـ CMap تالف.»
خطأ. هذا موجيبيك: بايتات UTF-8 فُكّت بـ Latin-1. الملف سليم، والعطب في
كودك أنت. علاجه .encode('latin-1').decode('utf-8'). أما الـ CMap
التالف فعلامته رموز PUA (U+E000–F8FF) أو خانات فارغة، وعلاجه شيء آخر
تماماً (الدرجة ٣).
التثبيت
# النواة (الدرجات ٠–٢): بلا أيّ تبعيّة — بايثون قياسيّ خالص
pip install arafix
# مع دعم PDF (مستحسن)
pip install "arafix[pdf]"
# كل شيء بما فيه الدرجة ٣
pip install "arafix[all]"
# جسر MarkItDown (إضافة PDF عربية + post-process)
pip install "arafix[markitdown]"
# من المصدر
git clone https://github.com/bio-colab/arafix
cd arafix && pip install -e ".[dev]" && pytest
الـ sdist يحمل الاختبارات والأمثلة عمداً: مَن حمّل المصدر يجب أن يستطيع
تشغيل pytest عليه فيتحقق بنفسه، لا أن يصدّق شهادتنا. وللنشر انظر
RELEASING.md.
قرارٌ مقصود: النواة بلا تبعيّات. الدرجات ٠–٢ تعمل في أيّ بيئة — Colab مقيّد، خادم بلا إنترنت، Lambda. التبعيّات كلها اختيارية.
جرّبها الآن (٣٠ ثانية، بلا ملفٍّ منك)
# ١) ولّد ملفاً معطوباً عمداً — يحاكي مُصدِّراً رديئاً حقيقياً
python examples/make_broken_pdf.py broken.pdf
# ٢) شخّص. لاحظ: هذا الأمر لا يكتب شيئاً، يريك فقط
arafix diagnose broken.pdf -v
# ٣) عالِج
arafix extract broken.pdf
المخرَج قبل وبعد:
─ ما تراه أدوات بايثون:
ﺩﺭﺍﺳﺔ ﻤﻘﺎﺭﻧﺔ ﻔﻲ ﺎﻟﺴﻴﺎﺳﺔ ﺎﻟﻌﺎﻣﺔ
ﻔﻲ ﻤﺠﻠﺔ ﻤﺤﻜﻤﺔ2024 ﻧﹹﺸﺮﺕ ﻬﺬﻩ ﺎﻟﺪﺭﺍﺳﺔ ﻌﺎﻡ
─ بعد arafix:
دراسة مقارنة في السياسة العامة
في مجلة محكمة2024 نُشرت هذه الدراسة عام
from arafix import repair_text
r = repair_text(broken_text)
r.text # النص بعد العلاج
r.diagnosis.summary() # 'presentation_forms، visual_order'
r.confidence # 0.94
[s.value for s in r.stages_applied] # ['hygiene', 'diagnose', 'normalize', 'reorder']
r.notes # لماذا فُعل كل شيء
from arafix import repair_blocks, fix_table, TextBlock
fix_table([["خلية معطوبة", "سليمة"], ["…", "…"]])
out = repair_blocks([
TextBlock(cell, id=f"r{i}c{j}", role="cell")
for i, row in enumerate(grid)
for j, cell in enumerate(row)
])
out.by_id()["r0c1"].text
بعد MarkItDown أو أيّ مستخرج
انظر INTEGRATING.md.
from arafix import fix_markitdown, fix_any
from markitdown import MarkItDown # اختياري
fixed = fix_markitdown(MarkItDown().convert("thesis.pdf"))
# أو: fix_any(open("paste.txt", encoding="utf-8").read())
from arafix import extract_pdf
doc = extract_pdf("thesis.pdf")
print(doc.text)
print(doc.confidence) # أدنى ثقة عبر الصفحات
for page in doc.pages: # كل صفحة تُشخَّص وحدها — عمداً
if page.repair.confidence < 0.7:
print(page.page_number, page.repair.diagnosis.summary())
from arafix import diagnose
d = diagnose(text)
d.defects # [PRESENTATION_FORMS, VISUAL_ORDER]
d.confidence_in(Defect.PRESENTATION_FORMS) # 1.0 ← شاهدٌ قاطع
d.confidence_in(Defect.VISUAL_ORDER) # 0.93 ← شاهدٌ ظنّيّ
d.confidence # 0.93 ← أضعف حلقة
for e in d.evidence:
print(e) # final_only_letters=+0.940 :: ة/ى في أول 47 كلمة مقابل آخر 3
ولاحظ أن الثقة مفصولة لكل علّة. الرقم الواحد يُخفي أن بعض شواهدنا قاطعة وبعضها ظنّيّ، فيظلم الأولى ويجمّل الثانية:
| نوع الشاهد | الثقة | لماذا |
|---|---|---|
| قاطع (نطاقٌ أو اختبارٌ جبريّ) | ١٫٠ دائماً | فحصُ نطاقٍ على ٥ محارف قاطعٌ كفحصه على ٥٠٠٠. حجمُ العيّنة لا دخل له. |
| ظنّيّ (الاتجاه) | بدرجة شاهده | |
| «سليم» — شهادةُ نفي | ٠٫٣ ← ٠٫٩ بحجم العيّنة | وحدَها يحكمها الحجم، إذ هي استدلالٌ بغياب الدليل. وسقفُها ٠٫٩ عمداً: غيابُ العلّة ليس برهانَ سلامة. |
لاحظ الشواهد. الفرق بين أداةٍ تقول «النص معكوس» وأداةٍ تقول «معكوس لأن ٩٤٪ من التاءات المربوطة وقعت أوّل الكلمة» هو الفرق بين أداةٍ تُصدَّق وأداةٍ تُستعمل على عمى.
الضبط
from arafix import PipelineConfig, NormalizeConfig, repair_text
cfg = PipelineConfig(
normalize=NormalizeConfig(
strip_diacritics=False, # افتراضيّ: الاسترجاع لا التعديل
unify_alef=True, # للبحث والفهرسة فقط
),
thresholds={"visual_order": 0.45}, # عتبة أشدّ
force_reorder=False,
)
r = repair_text(text, cfg)
from arafix import repair_lam_alef_transposition
r = repair_lam_alef_transposition("االنترنيت والمجالت")
r.text # 'الانترنيت والمجالت' ← القاطع أُصلح
r.fixed_decisive # 1
r.suspects_left # 1
r.suspect_words # ['المجالت'] ← مُبلَّغٌ عنه، غير مُخمَّن
# ومع معجم، يُحسم المُبهَم أيضاً:
repair_lam_alef_transposition("المجالت", lexicon=my_words).text # 'المجلات'
يمرّ هذا تلقائياً داخل repair_text / extract_pdf؛ ومرِّر
PipelineConfig(lexicon=...) لتزويده بالمعجم.
المعمار
النص الخام ◄── extractors/: القراءة الهندسية من تيار الرسم لا من بِدي المحرّك
│
├─ ٠ diagnose.py ◄── لا تعالج قبل أن تعرف. لا يكتب شيئاً.
│ ├── detect_mojibake اختبار جبريّ قاطع
│ ├── detect_presentation_forms عدّ نطاقيّ
│ ├── detect_pua عدّ نطاقيّ
│ └── detect_visual_order ٣ شواهد لغوية، تصويت مرجَّح
│
├─ ١أ normalize.py ◄── المفردات وحدها. ما يغيّر العنقود يُؤجَّل.
├─ ٢ order.py ◄── بصريّ ← منطقيّ، بحماية الأرقام واللاتينية
├─ ١ب normalize.py ◄── الرباطات والتشكيل الفاصل — بعد استقرار الترتيب
├─ ⚕ lamalef.py ◄── ترقيع عطبٍ وَرِثناه من أداةٍ أخرى
├─ ٣ cmap.py ◄── إعادة بناء الخريطة من الخط المضمَّن
└─ ٤ OCR ◄── آخر الدواء (لم يُنفَّذ بعد)
القرارات المعمارية، ولماذا اتُّخذت
١. كل جدول يونيكود مُولَّد، لا مكتوب بيد.
٧٣١ مدخلةً مشتقّةً من unicodedata — لا خطأ مطبعياً ممكناً، وتتحدّث مع
نسخة يونيكود تلقائياً. الاستثناءات وحدها يدوية، وكلٌّ منها مُبرَّرٌ في
تعليقٍ إلى جانبه.
٢. تطبيعٌ مُوجَّه لا NFKC.
NFKC يحلّ المشكلة ويحلّ معها عشرين لم تطلبها: يقلب R² إلى R2،
وfile إلى file، و① إلى 1. في بحثٍ أكاديميّ فيه رموز رياضية، هذا
تخريبٌ صامت. فنحن نطبّع نطاق الأشكال العربية وحده.
٣. لا مرحلة تُرجع نصاً عارياً. كلٌّ تُرجع كائناً يحمل النص ومعه سببَ ما فعلت ودرجةَ ثقتها. القرار للمستعمل، والمكتبة قابلة للتدقيق.
٤. لا درجةَ تُطبَّق بلا شاهد.
المكتبة لا تعالج «احتياطاً». عكسُ نصٍّ سليمٍ تخريبٌ بأيدينا. وأهمّ
اختبارٍ في الحزمة اسمه test_does_not_touch_healthy_text.
٥. التطبيع مُشطَّرٌ حول الاتجاه: ١أ ← ٢ ← ١ب. هذا القرار كُتب أوّلاً «التطبيع قبل الاتجاه»، وكان نصفَ صواب. فالتطبيع الكامل قبل الاتجاه يفكّ «ﻻ» إلى حرفين، ثم يعكسهما العكسُ إلى «ال». فصار: تُطبَّع المفردات (فتنكشف التاء المربوطة لكاشف الاتجاه)، ويبقى الرباط ذرّةً، ثم يُعكس، ثم يُفكّ الرباط. الدرجة ١ تفتح عين الدرجة ٢ ولا تسلّمها سكيناً.
٥ب. معيارُ التأجيل تغيُّرُ بنية العنقود، لا طولُ التفكيك.
يُؤجَّل صنفان: الرباطات (محرفٌ يصير محرفين)، وأشكال التشكيل الفاصلة
U+FE70–FE7F (محرفٌ فئتُه Lo يصير علامةً لاصقة Mn). كلاهما يغيّر
وحدةَ العكس، فتطبيعُهما المبكر يهدم ما بعده. ومعيارُ الطول وحده يُعمي عن
الثاني: تفكيك U+FE79 هو [كشيدة + ضمّة]، ونحن نطرح الكشيدة فيعود الطول
واحداً فيبدو بريئاً. فالفئةُ تفضح ما لا يفضحه الطول.
٥ج. الكاشف يقرأ طبقتين، لأن التطبيع يفقأ عيناً وهو يفتح أخرى.
التاء المربوطة لا تُرى إلا بعد التطبيع، وصيغُ الوصل لا تُرى إلا قبله.
فيأخذ detect_visual_order النصّ المطبَّع ومعه الأصل الرسوميّ
(shaped_source)، فيشهد كلٌّ من طبقته.
٥د. هويّة الوصل برهانٌ لا أمارة.
في العربية: joins_forward(a) == joins_backward(b) لكل حرفين متجاورين —
لا تتخلّف أبداً في نصٍّ منطقيّ. فخرقُها مرّةً واحدة يُثبت الانعكاس.
شاهدٌ لا تماثليّ: يدحض ولا يُزكّي. (كان الفحص أوّلاً على طرفَي الكلمة
وحدهما، فأفلتت منه كلماتٌ كـ«الإجراء» طرفاها منفصلان.)
٦. text[::-1] خطأ، لا اختصار — لثلاثة أسباب لا سبب.
(أ) الأرقام واللاتينية LTR في الحالين، فالعكس يفسد 2024 فتصير 4202
وGDP_2024 تصير 2024_GDP. (ب) الأقواس مِرآتية: جليفُ أقصى اليسار
في سطرٍ عربيّ هو ( وإن كان المحرف المنطقيّ هناك )، فبلا مرآةٍ تخرج
)مقدمة(. (ج) وحدةُ العكس العنقودُ لا المحرف: التشكيل عرضُه صفر
ويشترك في موضع حرفه، فعكسُ المحارف يُلصقه بالجار (أولاً ← أوًلا).
٧. القراءة من تيار الرسم، لا من بِدي المحرّك.
قياسٌ لا رأي: على ١٢ سطراً فيها ترقيم، أخفق مسار get_text() في ٩
وأخفق المسار الهندسيّ في صفر. ونقرأ بـ get_texttrace لا rawdict،
وهذا شرط: rawdict يعيد ترتيب محارفه ببِدي MuPDF قبل تسليمها، فيهدم
الربط الذي جئنا نستشهد به.
٨. ولكلٍّ من ربط العنقود وترتيبه شاهدٌ مختلف — والخلطُ بينهما فخّ.
الربط (أيّ علامةٍ لأيّ حرف؟) من التيار: الهندسة تكذب هنا، إذ
العلامة عرضُها صفر فتُرسَم عند القلم بعد أن تجاوز حرفَها، فـ x عندها
يساوي x للحرف التالي. الترتيب (أيّ عنقودٍ قبل أيّ؟) من
الهندسة: التيار قد يكون بصرياً، وx وحده يقول أين وقع كلُّ شيء.
٩. لا نكتب قارئ PDF.
كتابته عملُ سنين، وموجودٌ منه ما يكفي. كل محرّك يُغلَّف خلف Extractor
واحد، فتبديله سطرٌ وإضافةُ جديدٍ ملفٌّ واحد.
١٠. cid1234 لا يُفكّ.
رقمٌ داخليّ للخط بلا دلالة. من يفكّه يخترع من عنده — وهذا خطٌّ أحمر:
المكتبة تعجز صراحةً ولا تخترع أبداً.
التوسيع
محرّك استخراج جديد
from arafix.extractors import Extractor, RawPage, register
@register
class PdfMinerExtractor(Extractor):
name = "pdfminer"
@classmethod
def available(cls) -> bool:
try:
import pdfminer; return True
except ImportError:
return False
def pages(self, path):
from pdfminer.high_level import extract_pages
...
yield RawPage(number=i, text=text)
def font_bytes(self, path):
return {}
سطرٌ واحد (@register)، ولا يُمسّ شيءٌ آخر في المكتبة. ثم:
extract_pdf(path, PipelineConfig(extractor="pdfminer")).
كاشف علّة جديد
١. أضف عضواً إلى Defect في types.py.
٢. اكتب دالةً في diagnose.py تُرجع (score, Evidence).
٣. نادها داخل diagnose() مع عتبةٍ في DEFAULT_THRESHOLDS.
٤. اكتب اختباراً يوثّق القرار لا السطر.
شاهد اتجاه جديد
أضف دالة _signal_* تُرجع (score, detail) في [-1, 1]، وسجّل وزنها في
_ORDER_WEIGHTS. التصويت يُعاد تطبيعه على الشواهد الحاضرة وحدها، فغياب
شاهدٍ لا يُميّع النتيجة.
خارطة الطريق
- الدرجة ٠ — التشخيص بشواهد
- الدرجة ١ — التطبيع المُوجَّه
- الدرجة ٢ — الاتجاه بحماية LTR
- الدرجة ٣ — الخريطة من
cmapوأسماء الجليفات - الرباطات — تشطير التطبيع حول الاتجاه + ترقيع رجعيّ (0.2.0)
- المحايدات — قراءة هندسية، مرآة الأقواس، عكسٌ عنقوديّ (0.3.0)
- معجم عربيّ مدمج خفيف (
arafix.lexicon.core) +use_core_lexicon(1.0.0) - الدرجة ٣+ — مطابقة الشكل (perceptual hash للجليف)
- الدرجة ٤ — غلاف OCR
- استخراج بنيويّ (جداول، حواشٍ، أعمدة) — layout 0.8.0
- محرّكات: pdfminer، pypdf، pdftotext
- القياس — CER/WER ومقارنةُ المسارات على ملفك (0.4.0)
- نظافة الاستخراج — NBSP / soft-hyphen (0.7.0)
-
repair_blocks/ جداول — إصلاحٌ مستقلّ لكل خلية (0.7.0) - معجم الوثيقة الداخليّ يحسم «المجالت» عبر الصفحات (0.7.0)
- جسر MarkItDown — plugin +
fix_markitdown(0.7.0) - أعمدة RTL + ترويسة/تذييل + جداول بنيوية (0.8.0)
- حماية التشكيل العنقودية + طيّ هجائن PDF + جزر LTR (0.9.0)
- طبقة علمية MCS/DBR/BFE/SHDR + corpus انحدار حقيقي (0.9.0)
- LTR ذكي: نطاقات صفحات، عملات، ترقيم جملة (0.9.2)
- موجيبيك هجين + CP1256 (0.9.3) + stress corpus 50 (1.0.0)
- حزمة ملفات مرجعية أوسع (أكثر من corpus واحد)
- حسمُ «المجالت» بنموذج n-gram على مستوى المحرف (اقتباساً من CAMeL)
- إخراج PDF قابل للبحث بالنصّ المصحَّح
الدرجة ٣+ فكرتها: ارسم كل جليفٍ من الخط المضمَّن، وقارنه بصرياً بمرجعٍ لكل حرفٍ عربيّ. هذا OCR على مستوى الجليف لا الصفحة: مساحة البحث ٣٦ حرفاً × ٤ أشكال، لا لغةٌ كاملة. أدقّ بمراتب وأسرع.
الاختبارات
pytest # unit + integration + real-PDF floors
pytest tests/test_scientific_floors.py -v
pytest --doctest-modules src/arafix
ruff check src tests
كل اختبارٍ يوثّق قراراً لا سطر كود. فإن كسرته يوماً، عرفت من اسمه
أيّ قرارٍ كسرت ولماذا اتُّخذ أوّلاً. ومن 0.9.0: corpus حقيقي + بوابات
MCS/DBR/BFE/SHDR في test_scientific_floors.
حدودٌ مُعلَنة
بصراحةٍ تسبق الاستعمال:
- الدرجة ٤ (OCR) غير منفَّذة، ولا حزمةَ
arafix[ocr]. المكتبة تكشف الحاجة وتقولها ولا تدّعي. وكان هناكextraباسمocrيجرّpytesseractبلا كودٍ يستعمله — وعدٌ بلا سند، فحُذف. لا نبيع تبعيّةً مقابل نيّة. - الدرجة ٣ تعجز عن الخطوط CID ذات الأسماء العديمة الدلالة. تُصرّح بالعجز (تغطية منخفضة) ولا تخترع.
- كاشف الاتجاه احتماليّ لا حتميّ — ولذلك يُرجع درجةً وشواهد لا حكماً.
- ترقيع لام-ألف الموروث: يُصلح القاطع (ألفان متجاورتان) يقيناً. المُبهَم
(
المجالت) يُحسَم بمعجم النواة المضمَّن (use_core_lexicon=True) أوlexicon=من المستعمل أو حصاد الوثيقة — بلا تخمين إملائي أعمى. والوقاية من الاستخراج ما زالت أتمّ: ما تعالجه المكتبة من أوّله يخرج سليماً. - الأعمدة والجداول مدعومة منذ 0.8.0 عبر
layout="auto"|"columns"|"full"(ميزاب أفقي + RTL). الكشف إحصائيّ: صفحات بثلاثة أعمدة متداخلة أو جداول بلا فجوات واضحة قد تحتاج ضبطLayoutConfig. - طيّ الهجائن افتراضيّ يستهدف العربية الفصحى؛ عطّله للفارسية/الأوردية
إن احتجتَ الإبقاء على
ی/ھ. - لا نخترع تشكيلاً غائباً من طبقة PDF. إن رسم الملف شدّةً بلا تنوين،
لا تُضاف. القياس (
DBR) يقارن الالتصاق لا اختراع العلامات. - Corpus انحدار حقيقي في
tests/fixtures/real_pdf_narrative/+ FLAW fixtures + stress 50-pack (scripts/stress_test_report.py).arafix eval --compare --scientificعلى ملفاتك يبقى الحجّة الميدانية.
المساهمة
الأنفع بالترتيب: (١) ملفات تكسرها، (٢) محرّكات جديدة، (٣) شواهد اتجاه أقوى، (٤) الدرجة ٣+.
Citation / الاستشهاد
If you use arafix in academic work, please cite it as:
APA:
Sharar, E. (2026). arafix: Evidence-Based Repair of Broken Arabic Text in Native PDFs (Version 1.0.0) [Computer software]. https://doi.org/10.5281/zenodo.21733978
BibTeX:
@software{sharar_arafix_2026,
author = {Sharar, Elias},
title = {{arafix: Evidence-Based Repair of Broken Arabic Text in Native PDFs}},
year = {2026},
version = {1.0.0},
doi = {10.5281/zenodo.21733978},
url = {https://github.com/bio-colab/arafix},
license = {MIT}
}
See CITATION.cff for machine-readable citation metadata (GitHub «Cite this repository», Zotero, EndNote, Zenodo).
الترخيص
MIT — انظر LICENSE.
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 arafix-1.0.0.tar.gz.
File metadata
- Download URL: arafix-1.0.0.tar.gz
- Upload date:
- Size: 379.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
58896d182019f25da6c733a7cd880947eb1a45236dda379e6432bd59e9e7a9f3
|
|
| MD5 |
a307e6e6bf554f6d44996004058762fe
|
|
| BLAKE2b-256 |
affee15604ce07a4c0bef2e71605ce2d88f1b44f08d2b22a6dfc23b9351a5461
|
Provenance
The following attestation bundles were made for arafix-1.0.0.tar.gz:
Publisher:
publish.yml on bio-colab/arafix
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
arafix-1.0.0.tar.gz -
Subject digest:
58896d182019f25da6c733a7cd880947eb1a45236dda379e6432bd59e9e7a9f3 - Sigstore transparency entry: 2339954022
- Sigstore integration time:
-
Permalink:
bio-colab/arafix@83812459433702bd85bbdf427442475d6c1b8643 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/bio-colab
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@83812459433702bd85bbdf427442475d6c1b8643 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file arafix-1.0.0-py3-none-any.whl.
File metadata
- Download URL: arafix-1.0.0-py3-none-any.whl
- Upload date:
- Size: 102.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9cbad38ffc0c3b7f43442f195f6bebbcbc94eceac90584b4b1d8c354698cd099
|
|
| MD5 |
bef4f0f9521d0c3788c1134617894a0e
|
|
| BLAKE2b-256 |
eb4d5891995984508898d04547948507eb3d97e591b0783d27e23a48710819a7
|
Provenance
The following attestation bundles were made for arafix-1.0.0-py3-none-any.whl:
Publisher:
publish.yml on bio-colab/arafix
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
arafix-1.0.0-py3-none-any.whl -
Subject digest:
9cbad38ffc0c3b7f43442f195f6bebbcbc94eceac90584b4b1d8c354698cd099 - Sigstore transparency entry: 2339954030
- Sigstore integration time:
-
Permalink:
bio-colab/arafix@83812459433702bd85bbdf427442475d6c1b8643 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/bio-colab
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@83812459433702bd85bbdf427442475d6c1b8643 -
Trigger Event:
workflow_dispatch
-
Statement type: