OmniFile-Processor / modules /export /markdown_exporter.py
Dr. Abdulmalek
deploy: OmniFile AI Processor v4.3.0
900df0b
"""
modules/export/markdown_exporter.py
════════════════════════════════════
تصدير نتائج OCR إلى Markdown مع الحفاظ على البنية.
مستوحى من: Docling (IBM) — docling.txt في مجلد الاقتراحات.
يدعم:
- العناوين (H1, H2, H3) بناءً على حجم النص النسبي
- الجداول بتنسيق Markdown مع محاذاة RTL
- تسميات الصور (caption)
- القوائم النقطية والرقمية
- الفقرات مع الحفاظ على الفراغات
"""
import re
from typing import Optional
def blocks_to_markdown(layout_data: dict, rtl: bool = True) -> str:
"""
تحويل layout_data إلى نص Markdown.
Args:
layout_data: dict يحتوي على 'blocks' (نفس تنسيق layout_preserving.py)
rtl: True = إضافة ماركر RTL للنصوص العربية
Returns:
نص Markdown منسّق
"""
lines = []
rtl_marker = "\u202b" if rtl else "" # RIGHT-TO-LEFT EMBEDDING
for block in layout_data.get("blocks", []):
btype = block.get("type", "paragraph")
text = block.get("text", "").strip()
if btype == "header":
lines.append(f"## {rtl_marker}{text}\n")
elif btype == "paragraph":
if text:
lines.append(f"{rtl_marker}{text}\n")
elif btype == "caption":
if text:
lines.append(f"*{rtl_marker}{text}*\n")
elif btype == "table":
md_table = _table_to_markdown(block.get("cells", []), rtl_marker)
if md_table:
lines.append(md_table + "\n")
elif btype == "image":
img = block.get("image_file", "")
caption = block.get("caption", "")
if img:
lines.append(f"\n![{caption}]({img})\n\n")
elif btype == "list_item":
prefix = block.get("list_prefix", "-")
lines.append(f"{prefix} {rtl_marker}{text}\n")
# فراغ بين الكتل
lines.append("")
return "\n".join(lines).strip()
def _table_to_markdown(cells: list[list], rtl_marker: str = "") -> str:
"""تحويل خلايا جدول إلى Markdown table."""
if not cells:
return ""
rows = []
col_count = max(len(row) for row in cells)
# صف العنوان
header = cells[0]
header_cells = [f"{rtl_marker}{str(c).strip()}" for c in header]
# تعبئة الخلايا الناقصة
while len(header_cells) < col_count:
header_cells.append("")
rows.append("| " + " | ".join(header_cells) + " |")
# صف الفاصل (RTL alignment)
rows.append("| " + " | ".join(["---:"] * col_count) + " |")
# بقية الصفوف
for row in cells[1:]:
cells_fmt = [f"{rtl_marker}{str(c).strip()}" for c in row]
while len(cells_fmt) < col_count:
cells_fmt.append("")
rows.append("| " + " | ".join(cells_fmt) + " |")
return "\n".join(rows)
def detect_list_items(text: str) -> list[dict]:
"""
كشف عناصر القوائم العربية والإنجليزية.
الأنماط المدعومة: •, -, *, 1., أ), a)
"""
patterns = [
(r"^[•\-\*○▪▸►]\s+(.+)", "-"),
(r"^\d+[\.\)]\s+(.+)", "1."),
(r"^[أ-ي][\.\)]\s+(.+)", "أ)"),
(r"^[a-z][\.\)]\s+(.+)", "a)"),
]
items = []
for line in text.split("\n"):
line = line.strip()
for pattern, prefix in patterns:
m = re.match(pattern, line)
if m:
items.append({"type": "list_item", "text": m.group(1), "list_prefix": prefix})
break
else:
if line:
items.append({"type": "paragraph", "text": line})
return items
def export_to_markdown(layout_data: dict, output_path: Optional[str] = None,
rtl: bool = True) -> str:
"""
واجهة رئيسية: تصدير layout_data إلى ملف Markdown.
Args:
layout_data: dict بنفس تنسيق export_to_docx
output_path: مسار الملف (اختياري — إذا None يُرجع النص فقط)
rtl: True للنصوص العربية
Returns:
نص Markdown
"""
md = blocks_to_markdown(layout_data, rtl=rtl)
if output_path:
with open(output_path, "w", encoding="utf-8") as f:
f.write(md)
return md