import gradio as gr import os import re import tempfile import zipfile from huggingface_hub import HfApi, hf_hub_download, login # مكتبات تحويل الصيغ from fpdf import FPDF from docx import Document from docx.shared import Pt from docx.enum.text import WD_ALIGN_PARAGRAPH import arabic_reshaper from bidi.algorithm import get_display import openpyxl try: from ebooklib import epub EPUB_AVAILABLE = True except ImportError: EPUB_AVAILABLE = False # ================================================================= # إعدادات النظام العامة – مطابقة كاملة 100% مع الطبقة الثانية # ================================================================= # جلب التوكن بالكبير تماماً كما هو معرف في السبيس الآخر ومعالجته فوراً TOKEN = os.getenv("AIOCR_KEY") if TOKEN: # تنظيف التوكن تلقائياً من أي زوائد أو أسطر خفية قد تنتج عن اللصق TOKEN = TOKEN.strip().split()[0] try: login(token=TOKEN) api = HfApi(token=TOKEN) except Exception as login_err: print(f"⚠️ تنبيه تسجيل الدخول: {login_err}") api = HfApi() else: api = HfApi() REPO_ID = "Asem75/aiocr_asistant" FINAL_DIR = "Final_Arabic_Files" MAIN_PORTAL = "https://huggingface.co/spaces/Asem75/aiocr" # تصميم موحد ونظيف بدون أي تداخل برمجي custom_css = """ body, .gradio-container { background-color: #0a0a0a !important; color: #e0c080 !important; } .gr-box, .gr-form, .gr-panel { background-color: #1a1a1a !important; border: 1px solid #c8a44c !important; } .gr-button { background-color: #2a2a2a !important; color: #e0c080 !important; border: 1px solid #c8a44c !important; } .gr-button:hover { background-color: #3d2e1a !important; } .gr-textbox, .gr-dropdown { background-color: #121212 !important; color: #e0c080 !important; border: 1px solid #c8a44c !important; } #golden-editor textarea { background-color: #121212 !important; color: #f5d78c !important; font-size: 16px; font-family: 'Courier New', monospace; line-height: 1.6; direction: rtl; } .warning-banner { background-color: #3d2e1a; color: #ffcc00; padding: 8px; border-radius: 4px; margin-bottom: 10px; text-align: center; font-weight: bold; } .error-box { background-color: #4a1a1a; color: #ff9999; padding: 10px; border: 1px solid #ff3333; border-radius: 4px; font-size: 13px; margin: 5px 0; } """ def clean_filename(fn: str) -> str: patterns = [r'^Final_Result_', r'^OCR_Result_', r'^Processed_', r'^Result_', r'^Final_'] for pat in patterns: fn = re.sub(pat, '', fn, flags=re.IGNORECASE) name, _ = os.path.splitext(fn) return name.strip() # ------------------------------- # الرادار الذكي الموحد لقراءة الملفات # ------------------------------- def list_archive_files_reporting(): if not TOKEN: return None, "
🗄️ الرادار متصل بالخزنة، ولكن المجلد الموحد '{FINAL_DIR}' فارغ حالياً ولا يحتوي ملفات مستخرجة.
" return results, None except Exception as e: error_msg = str(e) return None, f"{text}
"""
tmp = tempfile.NamedTemporaryFile(delete=False, suffix=".html")
with open(tmp.name, 'w', encoding='utf-8') as f: f.write(html)
return tmp.name
def convert_to_xlsx(text, filename):
wb = openpyxl.Workbook()
ws = wb.active
ws.title = "AIOCR Output"
for i, line in enumerate(text.split('\n'), start=1):
ws.cell(row=i, column=1, value=line)
tmp = tempfile.NamedTemporaryFile(delete=False, suffix=".xlsx")
wb.save(tmp.name)
return tmp.name
def convert_to_epub(text, filename):
if not EPUB_AVAILABLE: raise gr.Error("مكتبة ebooklib غير مثبتة.")
book = epub.EpubBook()
book.set_identifier('aiocr_output')
book.set_title(filename)
book.set_language('ar')
chapter = epub.EpubHtml(title='Output', file_name='chap_1.xhtml', lang='ar')
chapter.content = f'{text}'
book.add_item(chapter)
book.toc = [epub.Link('chap_1.xhtml', 'Output', 'output')]
book.spine = [chapter]
book.add_item(epub.EpubNcx())
book.add_item(epub.EpubNav())
tmp = tempfile.NamedTemporaryFile(delete=False, suffix=".epub")
epub.write_epub(tmp.name, book, {})
return tmp.name
def convert_to_code(text, filename):
tmp = tempfile.NamedTemporaryFile(delete=False, suffix=".txt")
with open(tmp.name, 'w', encoding='utf-8') as f: f.write(text)
return tmp.name
def render_interface():
files_list, error_html = list_archive_files_reporting()
if error_html:
return error_html
html = 'جاري قراءة الخزنة الرقمية الموحدة...
") preview_box = gr.Textbox(label="📋 معاينة سريعة", lines=6, interactive=False) with gr.Column(scale=3): editor = gr.Textbox( label="📜 شاشة المحرر الذهبي الرئيسي", lines=20, elem_id="golden-editor", placeholder="انقر نقراً مزدوجاً (Double Click) على أي ملف للفتحه هنا بمجرد ظهوره بالأرشيف..." ) with gr.Row(): copy_btn = gr.Button("📋 نسخ النص") clean_btn = gr.Button("🧹 تنظيف المسافات") rtl_btn = gr.Button("↩️ محاذاة RTL") numeral_btn = gr.Button("🔢 أرقام عربية") with gr.Row(): filename_input = gr.Textbox(label="📝 اسم الملف النهائي", value="", scale=3) format_dropdown = gr.Dropdown( choices=["PDF", "DOCX", "TXT", "RTF", "HTML", "EXCEL (XLSX)", "EPUB", "CODE"], value="PDF", label="📄 تنسيق التصدير" ) with gr.Row(): download_single_btn = gr.DownloadButton("📥 تحميل المستند", variant="primary") download_zip_btn = gr.DownloadButton("🗜️ تحميل الأرشيف (ZIP)", variant="secondary") with gr.Row(): gr.HTML(f""" """) preview_filename = gr.Textbox(visible=False, elem_id="preview_filename") load_filename = gr.Textbox(visible=False, elem_id="load_filename") load_btn = gr.Button("تحميل", visible=False, elem_id="load_btn") timer = gr.Timer(12) demo.load(fn=render_interface, outputs=archive_html) refresh_btn.click(fn=render_interface, outputs=archive_html) timer.tick(fn=render_interface, outputs=archive_html) preview_filename.change(fn=get_preview, inputs=preview_filename, outputs=preview_box) def load_full(filename): text, clean_name = load_file_content(filename) return text, clean_name, filename load_btn.click(fn=load_full, inputs=load_filename, outputs=[editor, filename_input, current_file_state]) copy_btn.click( fn=None, inputs=None, outputs=None, js=""" () => { var txt = document.querySelector('#golden-editor textarea'); if(txt) navigator.clipboard.writeText(txt.value).then(() => alert('✔ تم نسخ النص!')); } """ ) clean_btn.click(fn=clean_spaces, inputs=editor, outputs=editor) rtl_btn.click(fn=rtl_align, inputs=editor, outputs=editor) numeral_btn.click(fn=convert_numerals, inputs=editor, outputs=editor) def handle_single_download(text, filename, fmt, current_file): if not text: raise gr.Error("لا يوجد نص.") format_map = { "PDF": (convert_to_pdf, ".pdf"), "DOCX": (convert_to_docx, ".docx"), "TXT": (lambda t, fn: tempfile.NamedTemporaryFile(delete=False, suffix=".txt").name, ".txt"), "RTF": (convert_to_rtf, ".rtf"), "HTML": (convert_to_html, ".html"), "EXCEL (XLSX)": (convert_to_xlsx, ".xlsx"), "EPUB": (convert_to_epub, ".epub"), "CODE": (convert_to_code, ".txt") } converter, ext = format_map.get(fmt, (lambda t, fn: tempfile.NamedTemporaryFile(delete=False, suffix=".txt").name, ".txt")) save_name = filename.strip() + ext if fmt == "TXT": tmp = tempfile.NamedTemporaryFile(delete=False, suffix=".txt") with open(tmp.name, 'w', encoding='utf-8') as f: f.write(text) filepath = tmp.name else: filepath = converter(text, save_name) return filepath download_single_btn.click(fn=handle_single_download, inputs=[editor, filename_input, format_dropdown, current_file_state], outputs=download_single_btn) def handle_zip_download(): files_list, _ = list_archive_files_reporting() if not files_list: raise gr.Error("لا توجد ملفات متوفرة.") zip_buffer = tempfile.NamedTemporaryFile(delete=False, suffix=".zip") with zipfile.ZipFile(zip_buffer, 'w') as zf: for fn in files_list: try: path = hf_hub_download(repo_id=REPO_ID, filename=f"{FINAL_DIR}/{fn}", repo_type="dataset", token=TOKEN) zf.write(path, arcname=fn) except: continue return zip_buffer.name download_zip_btn.click(fn=handle_zip_download, outputs=download_zip_btn) if __name__ == "__main__": demo.launch(server_name="0.0.0.0")