Bobholamovic commited on
Commit
455679a
·
1 Parent(s): 8767ac6

[Feat] Big update

Browse files
Files changed (2) hide show
  1. app.py +158 -346
  2. requirements.txt +1 -4
app.py CHANGED
@@ -1,66 +1,37 @@
 
1
  import base64
2
  import io
3
  import json
4
  import os
5
- import re
6
  import tempfile
 
7
  import zipfile
8
- import shutil
9
- import atexit
10
- from datetime import datetime
11
  from pathlib import Path
12
 
13
  import gradio as gr
14
  import requests
15
  from PIL import Image
16
 
17
- try:
18
- import pdf2image
19
- PDF2IMAGE_AVAILABLE = True
20
- except ImportError:
21
- PDF2IMAGE_AVAILABLE = False
22
-
23
- try:
24
- import fitz # PyMuPDF
25
- PYGMUPDF_AVAILABLE = True
26
- except ImportError:
27
- PYGMUPDF_AVAILABLE = False
28
-
29
  # API Configuration
30
- API_URL = "https://t707h6d9q6oftbx3.aistudio-app.com/layout-parsing"
31
  TOKEN = os.getenv("API_TOKEN")
32
 
33
- # Temporary directory management
34
- temp_dirs = []
35
-
36
- def cleanup():
37
- """Clean up temporary directories"""
38
- for dir_path in temp_dirs:
39
- try:
40
- shutil.rmtree(dir_path)
41
- except:
42
- pass
43
-
44
- atexit.register(cleanup)
45
 
46
- def image_to_base64(image_path):
47
- """Convert image to base64 encoding"""
48
- if not image_path or not Path(image_path).exists():
49
- return ""
50
- with open(image_path, "rb") as image_file:
51
- return f"data:image/png;base64,{base64.b64encode(image_file.read()).decode('utf-8')}"
52
 
53
- # Get current directory
54
- current_dir = Path(__file__).parent
55
- logo_path = current_dir / "pp-structurev3.png"
56
- logo_base64 = image_to_base64(logo_path)
57
 
58
  CSS = """
59
  :root {
60
- --sand-color: #FAF9F6;
61
- --white: #ffffff;
62
- --shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
63
- --text-color: #F3F4F7;
64
  --black:#000000;
65
  --link-hover: #2b6cb0;
66
  --content-width: 1200px;
@@ -82,9 +53,9 @@ body {
82
  background-color: var(--white);
83
  }
84
 
85
- #component-0,
86
- #tabs,
87
- #settings {
88
  background-color: var(--white) !important;
89
  padding: 15px;
90
  }
@@ -152,33 +123,6 @@ body {
152
  text-decoration: none;
153
  }
154
 
155
- .result-container {
156
- display: flex;
157
- gap: 20px;
158
- margin-bottom: 30px;
159
- width: 100%;
160
- }
161
-
162
- .pdf-preview {
163
- flex: 1;
164
- min-width: 0;
165
- }
166
-
167
- .markdown-result {
168
- flex: 1;
169
- min-width: 0;
170
- }
171
-
172
- .gallery-container {
173
- width: 100% !important;
174
- }
175
-
176
- .gallery-item {
177
- width: 100% !important;
178
- height: auto !important;
179
- aspect-ratio: auto !important;
180
- }
181
-
182
  button {
183
  background-color: var(--text-color) !important;
184
  color: var(--black) !important;
@@ -190,10 +134,6 @@ button:hover {
190
  opacity: 0.8 !important;
191
  }
192
 
193
- .radio-group {
194
- margin-bottom: 15px !important;
195
- }
196
-
197
  .file-download {
198
  margin-top: 15px !important;
199
  }
@@ -216,100 +156,40 @@ button:hover {
216
  text-align: center;
217
  margin: 20px 0;
218
  }
 
219
 
220
- /* PDF Viewer specific styles */
221
- .pdf-viewer-container {
222
- width: 100%;
223
- height: 600px;
224
- border: 1px solid #ddd;
225
- margin-top: 15px;
226
- background-color: #f9f9f9;
227
- display: flex;
228
- justify-content: center;
229
- align-items: center;
230
- }
231
 
232
- .pdf-viewer-container embed {
233
- width: 100%;
234
- height: 100%;
235
- }
236
 
237
- .no-preview-message {
238
- color: #666;
239
- font-size: 16px;
240
- text-align: center;
241
- padding: 20px;
242
- }
243
- """
244
 
245
- def clean_markdown_text(text):
246
- """Clean markdown text from HTML tags and excessive newlines"""
247
- if not text:
248
- return ""
249
- text = re.sub(r'<[^>]+>', '', text)
250
- text = re.sub(r'\n{3,}', '\n\n', text)
251
- return text.strip()
252
-
253
- def pdf_to_images(pdf_path, dpi=150):
254
- """Convert PDF to list of images with fallback methods"""
255
- images = []
256
-
257
- if PDF2IMAGE_AVAILABLE:
258
- try:
259
- images = pdf2image.convert_from_path(pdf_path, dpi=dpi)
260
- return images
261
- except Exception as e:
262
- print(f"pdf2image conversion failed: {str(e)}")
263
-
264
- if PYGMUPDF_AVAILABLE:
265
- try:
266
- doc = fitz.open(pdf_path)
267
- for page in doc:
268
- pix = page.get_pixmap(dpi=dpi)
269
- img = Image.frombytes("RGB", [pix.width, pix.height], pix.samples)
270
- images.append(img)
271
- return images
272
- except Exception as e:
273
- print(f"PyMuPDF conversion failed: {str(e)}")
274
-
275
- return None
276
-
277
- def create_pdf_preview(pdf_path):
278
- """Create PDF preview HTML with embedded viewer"""
279
- if not pdf_path or not Path(pdf_path).exists():
280
- return '<div class="no-preview-message">No PDF file available</div>'
281
-
282
- try:
283
- # Convert PDF to base64 for embedding
284
- with open(pdf_path, "rb") as f:
285
- pdf_bytes = f.read()
286
- pdf_base64 = base64.b64encode(pdf_bytes).decode("ascii")
287
-
288
- return f"""
289
- <div class="pdf-viewer-container">
290
- <embed
291
- src="data:application/pdf;base64,{pdf_base64}"
292
- type="application/pdf"
293
- width="100%"
294
- height="100%"
295
- >
296
- </div>
297
- """
298
- except Exception as e:
299
- print(f"Failed to create PDF preview: {str(e)}")
300
- return '<div class="no-preview-message">PDF preview generation failed</div>'
301
 
302
- def process_file(file_path, file_type):
 
 
 
 
 
 
 
 
 
 
 
 
 
303
  """Process uploaded file with API"""
304
  try:
305
  if not file_path:
306
  raise ValueError("Please upload a file first")
307
-
308
- if file_type == "pdf" and not str(file_path).lower().endswith('.pdf'):
309
- raise ValueError("Please upload a valid PDF file")
310
-
311
- if file_type == "image" and not str(file_path).lower().endswith(('.jpg', '.jpeg', '.png')):
312
- raise ValueError("Please upload a valid image file (JPG/JPEG/PNG)")
313
 
314
  # Read file content
315
  with open(file_path, "rb") as f:
@@ -319,14 +199,14 @@ def process_file(file_path, file_type):
319
  file_data = base64.b64encode(file_bytes).decode("ascii")
320
  headers = {
321
  "Authorization": f"token {TOKEN}",
322
- "Content-Type": "application/json"
323
  }
324
 
325
  response = requests.post(
326
  API_URL,
327
  json={"file": file_data, "fileType": 0 if file_type == "pdf" else 1},
328
  headers=headers,
329
- timeout=60
330
  )
331
  response.raise_for_status()
332
 
@@ -334,30 +214,38 @@ def process_file(file_path, file_type):
334
  result = response.json()
335
  layout_results = result.get("result", {}).get("layoutParsingResults", [])
336
 
337
- markdown_contents = []
338
- clean_markdown_contents = []
 
 
 
339
  for res in layout_results:
340
- markdown = res.get("markdown", {})
341
- original = markdown if isinstance(markdown, str) else markdown.get("text", "")
342
- markdown_contents.append(original)
343
- clean_markdown_contents.append(clean_markdown_text(original))
344
-
345
- # Generate preview content
346
- if file_type == "pdf":
347
- images = pdf_to_images(file_path)
348
- pdf_preview = create_pdf_preview(file_path)
349
- else:
350
- images = [Image.open(file_path)]
351
- pdf_preview = '<div class="no-preview-message">Image file preview</div>'
 
 
 
 
352
 
353
  return {
354
  "original_file": file_path,
355
  "file_type": file_type,
356
- "markdown_contents": markdown_contents,
357
- "clean_markdown_contents": clean_markdown_contents,
358
- "pdf_images": images,
359
- "pdf_preview": pdf_preview,
360
- "api_response": result
 
361
  }
362
 
363
  except requests.exceptions.RequestException as e:
@@ -365,148 +253,115 @@ def process_file(file_path, file_type):
365
  except Exception as e:
366
  raise gr.Error(f"Error processing file: {str(e)}")
367
 
368
- def create_zip_file(results):
 
369
  """Create ZIP file with all analysis results"""
370
  try:
371
  if not results:
372
  raise ValueError("No results to export")
373
-
374
- timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
375
- zip_filename = f"analysis_results_{timestamp}.zip"
376
-
377
- temp_dir = tempfile.mkdtemp()
378
- temp_dirs.append(temp_dir)
379
- zip_path = os.path.join(temp_dir, zip_filename)
380
-
381
- with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
382
- # Add original file
383
- original_path = results.get("original_file", "")
384
- if original_path and Path(original_path).exists():
385
- zipf.write(original_path, f"original/{Path(original_path).name}")
386
-
387
- # Add markdown content
388
- for i, (orig_md, clean_md) in enumerate(zip(
389
- results.get("markdown_contents", []),
390
- results.get("clean_markdown_contents", [])
391
- )):
392
- if orig_md:
393
- zipf.writestr(f"markdown/original/page_{i+1}.md", orig_md)
394
- if clean_md:
395
- zipf.writestr(f"markdown/clean/page_{i+1}.md", clean_md)
396
 
397
  # Add API response
398
  api_response = results.get("api_response", {})
399
- zipf.writestr("api_response.json", json.dumps(api_response, indent=2, ensure_ascii=False))
 
 
 
400
 
401
- # Add PDF images if available
402
- if results.get("file_type") == "pdf" and results.get("pdf_images"):
403
- for i, img in enumerate(results["pdf_images"]):
404
- img_path = os.path.join(temp_dir, f"page_{i+1}.jpg")
405
- img.save(img_path, "JPEG", quality=85)
406
- zipf.write(img_path, f"images/page_{i+1}.jpg")
407
 
408
- return zip_path
409
 
410
  except Exception as e:
411
  raise gr.Error(f"Error creating ZIP file: {str(e)}")
412
 
413
- def export_markdown(results):
414
- """Export markdown content to file"""
415
- try:
416
- if not results:
417
- raise ValueError("No results to export")
418
-
419
- markdowns = results.get("markdown_contents", [])
420
- if not markdowns:
421
- raise gr.Error("No markdown content to export")
422
-
423
- timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
424
- filename = f"markdown_export_{timestamp}.md"
425
- content = "\n\n".join(markdowns)
426
-
427
- temp_dir = tempfile.mkdtemp()
428
- temp_dirs.append(temp_dir)
429
- file_path = os.path.join(temp_dir, filename)
430
-
431
- with open(file_path, 'w', encoding='utf-8') as f:
432
- f.write(content)
433
-
434
- return file_path
435
-
436
- except Exception as e:
437
- raise gr.Error(f"Error exporting markdown: {str(e)}")
438
 
439
  with gr.Blocks(css=CSS, title="Document Analysis System") as demo:
440
  results_state = gr.State()
441
 
442
  # Header with logo
443
  with gr.Column(elem_classes=["logo-container"]):
444
- gr.HTML(f'<img src="{logo_base64}" class="logo-img">')
445
 
446
  # Navigation bar
447
  with gr.Row(elem_classes=["nav-bar"]):
448
- gr.HTML("""
 
449
  <div class="nav-links">
450
  <a href="https://github.com/PaddlePaddle/PaddleOCR" class="nav-link" target="_blank">GitHub</a>
451
  <a href="https://paddleocr.ai" class="nav-link" target="_blank">paddleocr.ai</a>
452
  </div>
453
- """)
 
454
 
455
  # Upload section
456
  with gr.Column(elem_classes=["upload-section"]):
457
- file_type = gr.Radio(
458
- ["pdf", "image"],
459
- label="File Type",
460
- value="pdf",
461
- interactive=True
462
- )
463
  file_input = gr.File(
464
  label="Upload Document",
465
  file_types=[".pdf", ".jpg", ".jpeg", ".png"],
466
- type="filepath"
467
  )
468
  process_btn = gr.Button("Analyze Document", variant="primary")
469
-
470
- loading_spinner = gr.Column(
471
- visible=False,
472
- elem_classes=["loader-container"]
473
  )
 
 
474
  with loading_spinner:
475
- gr.HTML("""
 
476
  <div class="loader"></div>
477
  <p>Processing, please wait...</p>
478
- """)
479
-
480
- # Results display section
481
- with gr.Row(elem_classes=["result-container"]):
482
- with gr.Column(elem_classes=["pdf-preview"]):
483
- gr.Markdown("### Original Document Preview")
484
- pdf_preview = gr.HTML(label="PDF Preview")
485
- pdf_gallery = gr.Gallery(
486
- label="PDF Pages",
487
- show_label=False,
488
- elem_classes=["gallery-container"],
489
- columns=[1],
490
- object_fit="contain",
491
- visible=False
492
  )
493
 
494
- with gr.Column(elem_classes=["markdown-result"]):
495
- with gr.Row(elem_classes=["radio-group"]):
496
- display_mode = gr.Radio(
497
- ["Original Markdown", "Cleaned Text"],
498
- label="Display Mode",
499
- value="Original Markdown",
500
- interactive=True
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
501
  )
502
- markdown_display = gr.Markdown(label="Analysis Results")
503
 
504
  # Download section
505
  with gr.Column(elem_classes=["download-section"]):
506
  gr.Markdown("### Result Export")
507
- with gr.Row():
508
- download_md_btn = gr.Button("Download Markdown", variant="secondary")
509
- download_all_btn = gr.Button("Download Full Results (ZIP)", variant="primary")
510
  download_file = gr.File(visible=False, label="Download File")
511
 
512
  # Interaction logic
@@ -516,81 +371,38 @@ with gr.Blocks(css=CSS, title="Document Analysis System") as demo:
516
  def hide_spinner():
517
  return gr.update(visible=False)
518
 
519
- # In update_display()
520
  def update_display(results):
521
- if not results:
522
- return [
523
- gr.update(value='<div class="no-preview-message">No file to display</div>'),
524
- gr.update(visible=False),
525
- gr.update(value="No content"),
526
- gr.update(value=[])
527
- ]
528
-
529
- images = results.get("pdf_images", [])
530
- show_gallery = bool(images)
531
- display_content = results["markdown_contents"][0] if results.get("markdown_contents") else "No content"
532
-
533
- return [
534
- gr.update(value='<div class="no-preview-message">Preview rendered as images</div>'),
535
- gr.update(visible=show_gallery),
536
- gr.update(value=display_content),
537
- gr.update(value=images if show_gallery else [])
538
- ]
539
-
540
-
541
- process_btn.click(
542
- toggle_spinner,
543
- outputs=[loading_spinner]
544
- ).then(
545
- process_file,
546
- inputs=[file_input, file_type],
547
- outputs=[results_state]
548
- ).then(
549
- hide_spinner,
550
- outputs=[loading_spinner]
551
- ).then(
552
  update_display,
553
  inputs=[results_state],
554
- outputs=[pdf_preview, pdf_gallery, markdown_display, pdf_gallery]
555
- )
556
-
557
- display_mode.change(
558
- lambda mode, res: (
559
- res["markdown_contents"][0] if mode == "Original Markdown"
560
- else res["clean_markdown_contents"][0]
561
- ) if res and res.get("markdown_contents") else "No content",
562
- inputs=[display_mode, results_state],
563
- outputs=[markdown_display]
564
- )
565
-
566
- download_md_btn.click(
567
- export_markdown,
568
- inputs=[results_state],
569
- outputs=[download_file]
570
- ).then(
571
- lambda: gr.update(visible=True),
572
- outputs=[download_file]
573
  )
574
 
575
  download_all_btn.click(
576
- create_zip_file,
577
- inputs=[results_state],
578
- outputs=[download_file]
579
- ).then(
580
- lambda: gr.update(visible=True),
581
- outputs=[download_file]
582
- )
583
 
584
- if __name__ == "__main__":
585
- # Check dependencies
586
- if not PDF2IMAGE_AVAILABLE:
587
- print("Warning: pdf2image not available, PDF to image conversion limited")
588
- if not PYGMUPDF_AVAILABLE:
589
- print("Warning: PyMuPDF not available, PDF fallback conversion disabled")
590
 
 
591
  demo.launch(
592
  server_name="0.0.0.0",
593
  server_port=7860,
594
  share=True,
595
- favicon_path=str(logo_path) if logo_path.exists() else None
596
- )
 
1
+ import atexit
2
  import base64
3
  import io
4
  import json
5
  import os
 
6
  import tempfile
7
+ import uuid
8
  import zipfile
 
 
 
9
  from pathlib import Path
10
 
11
  import gradio as gr
12
  import requests
13
  from PIL import Image
14
 
 
 
 
 
 
 
 
 
 
 
 
 
15
  # API Configuration
16
+ API_URL = "https://cf38vaydqdl2l4p2.aistudio-hub.baidu.com/layout-parsing"
17
  TOKEN = os.getenv("API_TOKEN")
18
 
19
+ LOGO_PATH = Path(__file__).parent / "pp-structurev3.png"
20
+ with open(LOGO_PATH, "rb") as image_file:
21
+ LOGO_BASE64 = (
22
+ f"data:image/png;base64,{base64.b64encode(image_file.read()).decode('utf-8')}"
23
+ )
 
 
 
 
 
 
 
24
 
25
+ TEMP_DIR = tempfile.TemporaryDirectory()
26
+ atexit.register(TEMP_DIR.cleanup)
 
 
 
 
27
 
 
 
 
 
28
 
29
  CSS = """
30
  :root {
31
+ --sand-color: #FAF9F6;
32
+ --white: #ffffff;
33
+ --shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
34
+ --text-color: #F3F4F7;
35
  --black:#000000;
36
  --link-hover: #2b6cb0;
37
  --content-width: 1200px;
 
53
  background-color: var(--white);
54
  }
55
 
56
+ #component-0,
57
+ #tabs,
58
+ #settings {
59
  background-color: var(--white) !important;
60
  padding: 15px;
61
  }
 
123
  text-decoration: none;
124
  }
125
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
126
  button {
127
  background-color: var(--text-color) !important;
128
  color: var(--black) !important;
 
134
  opacity: 0.8 !important;
135
  }
136
 
 
 
 
 
137
  .file-download {
138
  margin-top: 15px !important;
139
  }
 
156
  text-align: center;
157
  margin: 20px 0;
158
  }
159
+ """
160
 
161
+ MAX_NUM_PAGES = 10
 
 
 
 
 
 
 
 
 
 
162
 
 
 
 
 
163
 
164
+ def url_to_bytes(url, *, timeout=10):
165
+ resp = requests.get(url, timeout=timeout)
166
+ resp.raise_for_status()
167
+ return resp.content
 
 
 
168
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
169
 
170
+ def bytes_to_image(image_bytes):
171
+ return Image.open(io.BytesIO(image_bytes))
172
+
173
+
174
+ def embed_images_into_markdown_text(markdown_text, markdown_images):
175
+ for img_path, img_url in markdown_images.items():
176
+ # HACK
177
+ markdown_text = markdown_text.replace(
178
+ f'<img src="{img_path}"', f'<img src="{img_url}"'
179
+ )
180
+ return markdown_text
181
+
182
+
183
+ def process_file(file_path):
184
  """Process uploaded file with API"""
185
  try:
186
  if not file_path:
187
  raise ValueError("Please upload a file first")
188
+
189
+ if Path(file_path).suffix == ".pdf":
190
+ file_type = "pdf"
191
+ else:
192
+ file_type = "image"
 
193
 
194
  # Read file content
195
  with open(file_path, "rb") as f:
 
199
  file_data = base64.b64encode(file_bytes).decode("ascii")
200
  headers = {
201
  "Authorization": f"token {TOKEN}",
202
+ "Content-Type": "application/json",
203
  }
204
 
205
  response = requests.post(
206
  API_URL,
207
  json={"file": file_data, "fileType": 0 if file_type == "pdf" else 1},
208
  headers=headers,
209
+ timeout=1000,
210
  )
211
  response.raise_for_status()
212
 
 
214
  result = response.json()
215
  layout_results = result.get("result", {}).get("layoutParsingResults", [])
216
 
217
+ layout_ordering_images = []
218
+ markdown_texts = []
219
+ markdown_images = []
220
+ markdown_content_list = []
221
+ input_images = []
222
  for res in layout_results:
223
+ layout_ordering_images.append(
224
+ url_to_bytes(res["outputImages"]["layout_order_res"])
225
+ )
226
+ markdown = res["markdown"]
227
+ markdown_text = markdown["text"]
228
+ markdown_texts.append(markdown_text)
229
+ img_path_to_url = markdown["images"]
230
+ img_path_to_bytes = {}
231
+ for path, url in img_path_to_url.items():
232
+ img_path_to_bytes[path] = url_to_bytes(url)
233
+ markdown_images.append(img_path_to_bytes)
234
+ input_images.append(url_to_bytes(res["inputImage"]))
235
+ markdown_content = embed_images_into_markdown_text(
236
+ markdown_text, img_path_to_url
237
+ )
238
+ markdown_content_list.append(markdown_content)
239
 
240
  return {
241
  "original_file": file_path,
242
  "file_type": file_type,
243
+ "layout_ordering_images": layout_ordering_images,
244
+ "markdown_texts": markdown_texts,
245
+ "markdown_images": markdown_images,
246
+ "markdown_content_list": markdown_content_list,
247
+ "input_images": input_images,
248
+ "api_response": result,
249
  }
250
 
251
  except requests.exceptions.RequestException as e:
 
253
  except Exception as e:
254
  raise gr.Error(f"Error processing file: {str(e)}")
255
 
256
+
257
+ def export_full_results(results):
258
  """Create ZIP file with all analysis results"""
259
  try:
260
  if not results:
261
  raise ValueError("No results to export")
262
+
263
+ filename = Path(results["original_file"]).stem + f"_{uuid.uuid4().hex}.zip"
264
+ zip_path = Path(TEMP_DIR.name, filename)
265
+
266
+ with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as zipf:
267
+ for i, img_bytes in enumerate(results["layout_ordering_images"]):
268
+ zipf.writestr(f"layout_ordering_images/page_{i+1}.jpg", img_bytes)
269
+
270
+ for i, (md_text, md_imgs) in enumerate(
271
+ zip(
272
+ results["markdown_texts"],
273
+ results["markdown_images"],
274
+ )
275
+ ):
276
+ zipf.writestr(f"markdown/page_{i+1}.md", md_text)
277
+ for img_path, img_bytes in md_imgs.items():
278
+ zipf.writestr(f"markdown/{img_path}", img_bytes)
 
 
 
 
 
 
279
 
280
  # Add API response
281
  api_response = results.get("api_response", {})
282
+ zipf.writestr(
283
+ "api_response.json",
284
+ json.dumps(api_response, indent=2, ensure_ascii=False),
285
+ )
286
 
287
+ for i, img_bytes in enumerate(results["input_images"]):
288
+ zipf.writestr(f"input_images/page_{i+1}.jpg", img_bytes)
 
 
 
 
289
 
290
+ return str(zip_path)
291
 
292
  except Exception as e:
293
  raise gr.Error(f"Error creating ZIP file: {str(e)}")
294
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
295
 
296
  with gr.Blocks(css=CSS, title="Document Analysis System") as demo:
297
  results_state = gr.State()
298
 
299
  # Header with logo
300
  with gr.Column(elem_classes=["logo-container"]):
301
+ gr.HTML(f'<img src="{LOGO_BASE64}" class="logo-img">')
302
 
303
  # Navigation bar
304
  with gr.Row(elem_classes=["nav-bar"]):
305
+ gr.HTML(
306
+ """
307
  <div class="nav-links">
308
  <a href="https://github.com/PaddlePaddle/PaddleOCR" class="nav-link" target="_blank">GitHub</a>
309
  <a href="https://paddleocr.ai" class="nav-link" target="_blank">paddleocr.ai</a>
310
  </div>
311
+ """
312
+ )
313
 
314
  # Upload section
315
  with gr.Column(elem_classes=["upload-section"]):
 
 
 
 
 
 
316
  file_input = gr.File(
317
  label="Upload Document",
318
  file_types=[".pdf", ".jpg", ".jpeg", ".png"],
319
+ type="filepath",
320
  )
321
  process_btn = gr.Button("Analyze Document", variant="primary")
322
+ gr.Markdown(
323
+ f"*Please note that only the first {MAX_NUM_PAGES} pages will be processed.*"
 
 
324
  )
325
+
326
+ loading_spinner = gr.Column(visible=False, elem_classes=["loader-container"])
327
  with loading_spinner:
328
+ gr.HTML(
329
+ """
330
  <div class="loader"></div>
331
  <p>Processing, please wait...</p>
332
+ """
 
 
 
 
 
 
 
 
 
 
 
 
 
333
  )
334
 
335
+ # Results display section
336
+ with gr.Column():
337
+ gr.Markdown("### Results")
338
+ layout_ordering_images = []
339
+ markdown_display_list = []
340
+ for i in range(MAX_NUM_PAGES):
341
+ with gr.Row():
342
+ layout_ordering_images.append(
343
+ gr.Image(
344
+ label=f"Layout Ordering Image {i}",
345
+ show_label=True,
346
+ visible=False,
347
+ )
348
+ )
349
+ markdown_display_list.append(
350
+ gr.Markdown(
351
+ visible=False,
352
+ container=True,
353
+ show_copy_button=True,
354
+ latex_delimiters=[
355
+ {"left": "$$", "right": "$$", "display": True},
356
+ {"left": "$", "right": "$", "display": False},
357
+ ],
358
+ )
359
  )
 
360
 
361
  # Download section
362
  with gr.Column(elem_classes=["download-section"]):
363
  gr.Markdown("### Result Export")
364
+ download_all_btn = gr.Button("Download Full Results (ZIP)", variant="primary")
 
 
365
  download_file = gr.File(visible=False, label="Download File")
366
 
367
  # Interaction logic
 
371
  def hide_spinner():
372
  return gr.update(visible=False)
373
 
 
374
  def update_display(results):
375
+ ret_img = []
376
+ ret_cont = []
377
+ cnt = 0
378
+ for img, cont in zip(
379
+ results["layout_ordering_images"], results["markdown_content_list"]
380
+ ):
381
+ ret_img.append(gr.update(value=bytes_to_image(img), visible=True))
382
+ ret_cont.append(gr.update(value=cont, visible=True))
383
+ cnt += 1
384
+ for _ in range(cnt, MAX_NUM_PAGES):
385
+ ret_img.append(gr.update(visible=False))
386
+ ret_cont.append(gr.update(visible=False))
387
+ return ret_img + ret_cont
388
+
389
+ process_btn.click(toggle_spinner, outputs=[loading_spinner]).then(
390
+ process_file, inputs=[file_input], outputs=[results_state]
391
+ ).then(hide_spinner, outputs=[loading_spinner]).then(
 
 
 
 
 
 
 
 
 
 
 
 
 
 
392
  update_display,
393
  inputs=[results_state],
394
+ outputs=layout_ordering_images + markdown_display_list,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
395
  )
396
 
397
  download_all_btn.click(
398
+ export_full_results, inputs=[results_state], outputs=[download_file]
399
+ ).success(lambda: gr.update(visible=True), outputs=[download_file])
 
 
 
 
 
400
 
 
 
 
 
 
 
401
 
402
+ if __name__ == "__main__":
403
  demo.launch(
404
  server_name="0.0.0.0",
405
  server_port=7860,
406
  share=True,
407
+ favicon_path=LOGO_PATH,
408
+ )
requirements.txt CHANGED
@@ -1,6 +1,3 @@
1
- gradio>=3.0
2
  requests
3
  pillow
4
- pdf2image
5
- python-dotenv
6
- PyMuPDF
 
1
+ gradio>=5
2
  requests
3
  pillow