import atexit import base64 import io import json import os import tempfile import uuid import zipfile from pathlib import Path import gradio as gr import requests from PIL import Image # API Configuration API_URL = "https://cf38vaydqdl2l4p2.aistudio-hub.baidu.com/layout-parsing" TOKEN = os.getenv("API_TOKEN") LOGO_PATH = Path(__file__).parent / "pp-structurev3.png" with open(LOGO_PATH, "rb") as image_file: LOGO_BASE64 = ( f"data:image/png;base64,{base64.b64encode(image_file.read()).decode('utf-8')}" ) TEMP_DIR = tempfile.TemporaryDirectory() atexit.register(TEMP_DIR.cleanup) CSS = """ :root { --sand-color: #FAF9F6; --white: #ffffff; --shadow: 0 4px 6px rgba(0, 0, 0, 0.1); --text-color: #F3F4F7; --black:#000000; --link-hover: #2b6cb0; --content-width: 1200px; } body { display: flex; justify-content: center; background-color: var(--sand-color); color: var(--text-color); font-family: Arial, sans-serif; } .gradio-container { max-width: var(--content-width) !important; width: 100% !important; margin: 20px auto; padding: 20px; background-color: var(--white); } #component-0, #tabs, #settings { background-color: var(--white) !important; padding: 15px; } .upload-section { width: 100%; margin: 0 auto 30px; padding: 20px; background-color: var(--sand-color) !important; border-radius: 8px; box-shadow: var(--shadow); } .center-content { display: flex; flex-direction: column; align-items: center; text-align: center; margin-bottom: 20px; } .header { margin-bottom: 30px; width: 100%; } .logo-container { width: 100%; margin-bottom: 20px; } .logo-img { width: 100%; max-width: var(--content-width); margin: 0 auto; display: block; } .nav-bar { display: flex; justify-content: center; background-color: var(--white); padding: 15px 0; box-shadow: var(--shadow); margin-bottom: 20px; } .nav-links { display: flex; gap: 30px; width: 100%; justify-content: center; } .nav-link { color: var(--black); text-decoration: none; font-weight: bold; font-size: 24px; transition: color 0.2s; } .nav-link:hover { color: var(--link-hover); text-decoration: none; } button { background-color: var(--text-color) !important; color: var(--black) !important; border: none !important; border-radius: 4px; padding: 8px 16px; } button:hover { opacity: 0.8 !important; } .file-download { margin-top: 15px !important; } .loader { border: 5px solid #f3f3f3; border-top: 5px solid #3498db; border-radius: 50%; width: 50px; height: 50px; animation: spin 1s linear infinite; margin: 20px auto; } @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } .loader-container { text-align: center; margin: 20px 0; } """ MAX_NUM_PAGES = 10 def url_to_bytes(url, *, timeout=10): resp = requests.get(url, timeout=timeout) resp.raise_for_status() return resp.content def bytes_to_image(image_bytes): return Image.open(io.BytesIO(image_bytes)) def embed_images_into_markdown_text(markdown_text, markdown_images): for img_path, img_url in markdown_images.items(): # HACK markdown_text = markdown_text.replace( f'') # Navigation bar with gr.Row(elem_classes=["nav-bar"]): gr.HTML( """ """ ) # Upload section with gr.Column(elem_classes=["upload-section"]): file_input = gr.File( label="Upload Document", file_types=[".pdf", ".jpg", ".jpeg", ".png"], type="filepath", ) process_btn = gr.Button("Analyze Document", variant="primary") gr.Markdown( f"*Please note that only the first {MAX_NUM_PAGES} pages will be processed.*" ) loading_spinner = gr.Column(visible=False, elem_classes=["loader-container"]) with loading_spinner: gr.HTML( """

Processing, please wait...

""" ) # Results display section with gr.Column(): gr.Markdown("### Results") layout_ordering_images = [] markdown_display_list = [] for i in range(MAX_NUM_PAGES): with gr.Row(): layout_ordering_images.append( gr.Image( label=f"Layout Ordering Image {i}", show_label=True, visible=False, ) ) markdown_display_list.append( gr.Markdown( visible=False, container=True, show_copy_button=True, latex_delimiters=[ {"left": "$$", "right": "$$", "display": True}, {"left": "$", "right": "$", "display": False}, ], ) ) # Download section with gr.Column(elem_classes=["download-section"]): gr.Markdown("### Result Export") download_all_btn = gr.Button("Download Full Results (ZIP)", variant="primary") download_file = gr.File(visible=False, label="Download File") # Interaction logic def toggle_spinner(): return gr.update(visible=True) def hide_spinner(): return gr.update(visible=False) def update_display(results): ret_img = [] ret_cont = [] cnt = 0 for img, cont in zip( results["layout_ordering_images"], results["markdown_content_list"] ): ret_img.append(gr.update(value=bytes_to_image(img), visible=True)) ret_cont.append(gr.update(value=cont, visible=True)) cnt += 1 for _ in range(cnt, MAX_NUM_PAGES): ret_img.append(gr.update(visible=False)) ret_cont.append(gr.update(visible=False)) return ret_img + ret_cont process_btn.click(toggle_spinner, outputs=[loading_spinner]).then( process_file, inputs=[file_input], outputs=[results_state] ).then(hide_spinner, outputs=[loading_spinner]).then( update_display, inputs=[results_state], outputs=layout_ordering_images + markdown_display_list, ) download_all_btn.click( export_full_results, inputs=[results_state], outputs=[download_file] ).success(lambda: gr.update(visible=True), outputs=[download_file]) if __name__ == "__main__": demo.launch( server_name="0.0.0.0", server_port=7860, share=True, favicon_path=LOGO_PATH, )