euler314 commited on
Commit
b82bb65
ยท
verified ยท
1 Parent(s): 51a1606

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +718 -41
app.py CHANGED
@@ -1,47 +1,724 @@
 
 
1
  import os
 
 
2
  import tempfile
 
 
 
 
3
 
4
- import gradio as gr
5
- from magic_pdf.data.data_reader_writer import (
6
- FileBasedDataReader,
7
- FileBasedDataWriter
8
- )
9
- from magic_pdf.data.dataset import PymuDocDataset
10
- from magic_pdf.model.doc_analyze_by_custom_model import doc_analyze
11
- from magic_pdf.config.enums import SupportedPdfParseMethod
12
-
13
- def pdf_to_markdown(uploaded_pdf):
14
- # Save the uploaded file to a temp location
15
- pdf_path = uploaded_pdf.name
16
- # Prepare output dirs
17
- img_dir = os.path.join(tempfile.gettempdir(), "magic_pdf_images")
18
- os.makedirs(img_dir, exist_ok=True)
19
-
20
- # Read bytes
21
- reader = FileBasedDataReader("")
22
- pdf_bytes = reader.read(pdf_path)
23
-
24
- # Build dataset & run inference
25
- ds = PymuDocDataset(pdf_bytes)
26
- if ds.classify() == SupportedPdfParseMethod.OCR:
27
- infer = ds.apply(doc_analyze, ocr=True)
28
- pipe = infer.pipe_ocr_mode(FileBasedDataWriter(img_dir))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
  else:
30
- infer = ds.apply(doc_analyze, ocr=False)
31
- pipe = infer.pipe_txt_mode(FileBasedDataWriter(img_dir))
32
-
33
- # Get Markdown string
34
- md = pipe.get_markdown(os.path.basename(img_dir))
35
- return md
36
-
37
- # Gradio UI
38
- iface = gr.Interface(
39
- fn=pdf_to_markdown,
40
- inputs=gr.File(file_types=[".pdf"], label="Upload PDF"),
41
- outputs=gr.Textbox(lines=20, label="Markdown"),
42
- title="PDF โ†’ Markdown Converter",
43
- description="Powered by Magic-PDF"
44
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
45
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46
  if __name__ == "__main__":
47
- iface.launch()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import gradio as gr
3
  import os
4
+ import sys
5
+ import subprocess
6
  import tempfile
7
+ from pathlib import Path
8
+ import json
9
+ from loguru import logger
10
+ import shutil
11
 
12
+ # ============================================================================
13
+ # AUTOMATIC SETUP: GPU Support, MinerU & Model Downloads
14
+ # ============================================================================
15
+
16
+ def run_command(cmd, description="", show_output=False):
17
+ """Run a shell command"""
18
+ try:
19
+ if description:
20
+ print(f"[SETUP] {description}...")
21
+
22
+ if show_output:
23
+ result = subprocess.run(cmd, shell=True, text=True)
24
+ else:
25
+ result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
26
+
27
+ if result.returncode == 0:
28
+ if description:
29
+ print(f"[SETUP] โœ… {description} completed")
30
+ return True, result.stdout if hasattr(result, 'stdout') else ""
31
+ else:
32
+ if hasattr(result, 'stderr'):
33
+ print(f"[SETUP] โš ๏ธ {result.stderr}")
34
+ return False, result.stderr if hasattr(result, 'stderr') else ""
35
+ except Exception as e:
36
+ print(f"[SETUP] โŒ Error: {e}")
37
+ return False, str(e)
38
+
39
+ def setup_environment():
40
+ """Setup MinerU environment with GPU support"""
41
+ print("=" * 70)
42
+ print("๐Ÿš€ MINERU OCR TOOL - SETUP WITH GPU & DOCX/PDF EXPORT")
43
+ print("=" * 70)
44
+
45
+ # Check GPU first
46
+ try:
47
+ import torch
48
+ if torch.cuda.is_available():
49
+ print(f"[SETUP] โœ… GPU Detected: {torch.cuda.get_device_name(0)}")
50
+ print(f"[SETUP] โœ… CUDA Version: {torch.version.cuda}")
51
+ print(f"[SETUP] โœ… GPU Memory: {torch.cuda.get_device_properties(0).total_memory / 1e9:.2f} GB")
52
+ gpu_available = True
53
+ else:
54
+ print("[SETUP] โš ๏ธ No GPU detected, will use CPU")
55
+ gpu_available = False
56
+ except ImportError:
57
+ print("[SETUP] Installing PyTorch...")
58
+ run_command(f"{sys.executable} -m pip install torch torchvision --upgrade", "Installing PyTorch")
59
+ import torch
60
+ gpu_available = torch.cuda.is_available()
61
+
62
+ # Install MinerU from GitHub dev branch (better quality than PyPI)
63
+ print("\n" + "=" * 70)
64
+ print("[SETUP] Installing MinerU from GitHub dev branch...")
65
+ print("[SETUP] (Better OCR quality than PyPI version)")
66
+ print("=" * 70)
67
+
68
+ # Uninstall old version
69
+ print("[SETUP] Removing old MinerU installation...")
70
+ os.system('pip uninstall -y mineru')
71
+
72
+ # Install from GitHub dev branch
73
+ print("[SETUP] Installing from GitHub (this may take a few minutes)...")
74
+ os.system('pip install git+https://github.com/myhloli/Magic-PDF.git@dev')
75
+
76
+ # Install additional packages
77
+ print("\n" + "=" * 70)
78
+ print("[SETUP] Installing additional packages...")
79
+ print("=" * 70)
80
+
81
+ packages = [
82
+ "mineru-vl-utils",
83
+ "gradio-pdf",
84
+ "loguru",
85
+ "pypandoc",
86
+ ]
87
+
88
+ # Add VLLM for GPU acceleration if GPU is available
89
+ if gpu_available:
90
+ print("[SETUP] Installing VLLM for GPU acceleration...")
91
+ packages.append("vllm==0.10.1.1")
92
+
93
+ for package in packages:
94
+ run_command(
95
+ f"{sys.executable} -m pip install '{package}' --upgrade",
96
+ f"Installing {package}"
97
+ )
98
+
99
+ # Install pandoc system dependency
100
+ print("\n" + "=" * 70)
101
+ print("[SETUP] Installing Pandoc for document conversion...")
102
+ print("=" * 70)
103
+ try:
104
+ import pypandoc
105
+ # Download pandoc if not installed
106
+ pypandoc.ensure_pandoc_installed()
107
+ print("[SETUP] โœ… Pandoc installed successfully")
108
+ except Exception as e:
109
+ print(f"[SETUP] โš ๏ธ Pandoc installation warning: {e}")
110
+ print("[SETUP] Document conversion may not work without pandoc")
111
+
112
+ # Download models
113
+ print("\n" + "=" * 70)
114
+ print("[SETUP] Downloading MinerU models...")
115
+ print("=" * 70)
116
+
117
+ # Check if models already exist
118
+ model_dir = Path.home() / ".cache" / "mineru"
119
+ if model_dir.exists() and any(model_dir.rglob("*")):
120
+ print("[SETUP] โœ… Models already downloaded")
121
  else:
122
+ print("[SETUP] Downloading models (this may take 5-10 minutes)...")
123
+ success, output = run_command(
124
+ "mineru-models-download -s huggingface -m all",
125
+ "Downloading models",
126
+ show_output=True
127
+ )
128
+ if success:
129
+ print("[SETUP] โœ… Models downloaded successfully")
130
+ else:
131
+ print("[SETUP] โš ๏ธ Models will be downloaded on first use")
132
+
133
+ # Configure MinerU for GPU
134
+ print("\n" + "=" * 70)
135
+ print("[SETUP] Configuring MinerU...")
136
+ print("=" * 70)
137
+
138
+ config_path = Path.home() / "mineru.json"
139
+ if config_path.exists():
140
+ try:
141
+ with open(config_path, 'r+') as file:
142
+ config = json.load(file)
143
+
144
+ # Set LaTeX delimiters
145
+ delimiters = {
146
+ 'display': {'left': '\\[', 'right': '\\]'},
147
+ 'inline': {'left': '\\(', 'right': '\\)'}
148
+ }
149
+ config['latex-delimiter-config'] = delimiters
150
+
151
+ # Enable GPU if available
152
+ if gpu_available:
153
+ if 'device-mode' in config:
154
+ config['device-mode'] = 'cuda'
155
+ print("[SETUP] โœ… GPU mode enabled in config")
156
+
157
+ file.seek(0)
158
+ file.truncate()
159
+ json.dump(config, file, indent=4)
160
+ print("[SETUP] โœ… Configuration updated")
161
+ except Exception as e:
162
+ logger.warning(f"Could not update config: {e}")
163
+
164
+ print("\n" + "=" * 70)
165
+ print("โœ… SETUP COMPLETE!")
166
+ print("=" * 70 + "\n")
167
+
168
+ return gpu_available
169
+
170
+ # Run setup
171
+ gpu_available = setup_environment()
172
+
173
+ # Import required modules after installation
174
+ try:
175
+ import torch
176
+ from gradio_pdf import PDF
177
+
178
+ # GPU info for display
179
+ if torch.cuda.is_available():
180
+ gpu_info = f"๐Ÿš€ GPU: {torch.cuda.get_device_name(0)} ({torch.cuda.get_device_properties(0).total_memory / 1e9:.1f}GB VRAM)"
181
+ else:
182
+ gpu_info = "๐Ÿ’ป CPU Mode"
183
+ except ImportError as e:
184
+ print(f"[WARNING] Some imports failed: {e}")
185
+ gpu_info = "๐Ÿ’ป CPU Mode"
186
+ gpu_available = False
187
+ PDF = None
188
+
189
+ print(f"[STARTUP] Running with: {gpu_info}")
190
+
191
+ # Conversion functions with LaTeX support
192
+ def convert_markdown_to_docx(markdown_content, output_path):
193
+ """Convert markdown (with LaTeX) to DOCX using pypandoc"""
194
+ try:
195
+ import pypandoc
196
+
197
+ # Convert markdown with LaTeX formulas to DOCX
198
+ # Pandoc will convert LaTeX math to Word equations
199
+ pypandoc.convert_text(
200
+ markdown_content,
201
+ 'docx',
202
+ format='markdown+tex_math_dollars', # Support $...$ and $$...$$ LaTeX
203
+ outputfile=output_path,
204
+ extra_args=[
205
+ '--standalone',
206
+ '--mathml', # Convert LaTeX math to MathML for Word
207
+ ]
208
+ )
209
+ print(f"[CONVERT] โœ… DOCX with LaTeX formulas created")
210
+ return True, output_path
211
+ except Exception as e:
212
+ print(f"[CONVERT] Error converting to DOCX: {e}")
213
+ # Fallback: try without LaTeX support
214
+ try:
215
+ pypandoc.convert_text(
216
+ markdown_content,
217
+ 'docx',
218
+ format='md',
219
+ outputfile=output_path,
220
+ extra_args=['--standalone']
221
+ )
222
+ print(f"[CONVERT] โš ๏ธ DOCX created without LaTeX support")
223
+ return True, output_path
224
+ except Exception as e2:
225
+ print(f"[CONVERT] DOCX conversion failed: {e2}")
226
+ return False, str(e2)
227
+
228
+ def convert_markdown_to_pdf(markdown_content, output_path):
229
+ """Convert markdown (with LaTeX) to PDF using pypandoc"""
230
+ try:
231
+ import pypandoc
232
+
233
+ # Convert markdown with LaTeX formulas to PDF
234
+ pypandoc.convert_text(
235
+ markdown_content,
236
+ 'pdf',
237
+ format='markdown+tex_math_dollars', # Support $...$ and $$...$$ LaTeX
238
+ outputfile=output_path,
239
+ extra_args=[
240
+ '--pdf-engine=pdflatex',
241
+ '--standalone',
242
+ '-V', 'geometry:margin=1in', # Better margins
243
+ ]
244
+ )
245
+ print(f"[CONVERT] โœ… PDF with LaTeX formulas created")
246
+ return True, output_path
247
+ except Exception as e:
248
+ print(f"[CONVERT] Error with pdflatex: {e}")
249
+ # Fallback 1: Try xelatex
250
+ try:
251
+ pypandoc.convert_text(
252
+ markdown_content,
253
+ 'pdf',
254
+ format='markdown+tex_math_dollars',
255
+ outputfile=output_path,
256
+ extra_args=['--pdf-engine=xelatex', '--standalone']
257
+ )
258
+ print(f"[CONVERT] โœ… PDF created with xelatex")
259
+ return True, output_path
260
+ except Exception as e2:
261
+ print(f"[CONVERT] xelatex failed: {e2}")
262
+ # Fallback 2: Try without LaTeX engine (may not render formulas)
263
+ try:
264
+ pypandoc.convert_text(
265
+ markdown_content,
266
+ 'pdf',
267
+ format='md',
268
+ outputfile=output_path,
269
+ extra_args=['--standalone']
270
+ )
271
+ print(f"[CONVERT] โš ๏ธ PDF created without LaTeX formula support")
272
+ return True, output_path
273
+ except Exception as e3:
274
+ print(f"[CONVERT] PDF conversion failed completely: {e3}")
275
+ return False, str(e3)
276
+
277
+ def process_with_mineru(input_path, output_base_dir, use_vllm=True):
278
+ """Process file with MinerU CLI"""
279
+ try:
280
+ output_dir = Path(output_base_dir) / "output"
281
+ output_dir.mkdir(parents=True, exist_ok=True)
282
+
283
+ # Build command
284
+ cmd = f'mineru -p "{input_path}" -o "{output_dir}"'
285
+
286
+ print(f"[PROCESS] Running MinerU on: {Path(input_path).name}")
287
+ print(f"[PROCESS] Command: {cmd}")
288
+
289
+ result = subprocess.run(
290
+ cmd,
291
+ shell=True,
292
+ capture_output=True,
293
+ text=True,
294
+ timeout=600 # 10 minute timeout
295
+ )
296
+
297
+ if result.returncode == 0:
298
+ print("[PROCESS] โœ… Processing completed")
299
+
300
+ # Find output files
301
+ md_files = list(output_dir.glob("**/*.md"))
302
+ json_files = list(output_dir.glob("**/*.json"))
303
+
304
+ md_content = ""
305
+ json_content = ""
306
+
307
+ # Read markdown output
308
+ if md_files:
309
+ print(f"[PROCESS] Found {len(md_files)} markdown file(s)")
310
+ # Sort by size, get the largest (usually the main content)
311
+ md_files.sort(key=lambda x: x.stat().st_size, reverse=True)
312
+ with open(md_files[0], 'r', encoding='utf-8') as f:
313
+ md_content = f.read()
314
+
315
+ # Read JSON output
316
+ if json_files:
317
+ print(f"[PROCESS] Found {len(json_files)} JSON file(s)")
318
+ # Find the content.json or result.json file
319
+ for jf in json_files:
320
+ if 'content' in jf.name.lower() or 'result' in jf.name.lower():
321
+ with open(jf, 'r', encoding='utf-8') as f:
322
+ json_content = f.read()
323
+ break
324
+
325
+ # If no specific file found, use the largest one
326
+ if not json_content and json_files:
327
+ json_files.sort(key=lambda x: x.stat().st_size, reverse=True)
328
+ with open(json_files[0], 'r', encoding='utf-8') as f:
329
+ json_content = f.read()
330
+
331
+ if not md_content and not json_content:
332
+ all_files = list(output_dir.glob("**/*"))
333
+ print(f"[PROCESS] Found {len(all_files)} total files in output")
334
+ return False, "No markdown or JSON output found", "", "", output_dir
335
+
336
+ return True, "Success", md_content, json_content, output_dir
337
+
338
+ else:
339
+ error_msg = result.stderr if result.stderr else result.stdout
340
+ print(f"[PROCESS] โŒ Error: {error_msg}")
341
+ return False, error_msg, "", "", None
342
+
343
+ except subprocess.TimeoutExpired:
344
+ return False, "Processing timeout (>10 minutes)", "", "", None
345
+ except Exception as e:
346
+ import traceback
347
+ error_details = traceback.format_exc()
348
+ print(f"[ERROR] {error_details}")
349
+ return False, str(e), "", "", None
350
+
351
+ def download_as_docx(markdown_content, original_filename="document"):
352
+ """Convert markdown to DOCX and return file path for download"""
353
+ if not markdown_content or markdown_content.strip() == "":
354
+ return None
355
+
356
+ try:
357
+ # Create temp file
358
+ temp_dir = Path(tempfile.gettempdir()) / "mineru_gradio"
359
+ temp_dir.mkdir(exist_ok=True)
360
+
361
+ base_name = Path(original_filename).stem if original_filename else "document"
362
+ output_path = temp_dir / f"{base_name}_extracted.docx"
363
 
364
+ success, result = convert_markdown_to_docx(markdown_content, str(output_path))
365
+
366
+ if success:
367
+ print(f"[DOWNLOAD] DOCX created: {output_path}")
368
+ return str(output_path)
369
+ else:
370
+ print(f"[DOWNLOAD] DOCX conversion failed: {result}")
371
+ return None
372
+ except Exception as e:
373
+ print(f"[DOWNLOAD] Error creating DOCX: {e}")
374
+ return None
375
+
376
+ def download_as_pdf(markdown_content, original_filename="document"):
377
+ """Convert markdown to PDF and return file path for download"""
378
+ if not markdown_content or markdown_content.strip() == "":
379
+ return None
380
+
381
+ try:
382
+ # Create temp file
383
+ temp_dir = Path(tempfile.gettempdir()) / "mineru_gradio"
384
+ temp_dir.mkdir(exist_ok=True)
385
+
386
+ base_name = Path(original_filename).stem if original_filename else "document"
387
+ output_path = temp_dir / f"{base_name}_extracted.pdf"
388
+
389
+ success, result = convert_markdown_to_pdf(markdown_content, str(output_path))
390
+
391
+ if success:
392
+ print(f"[DOWNLOAD] PDF created: {output_path}")
393
+ return str(output_path)
394
+ else:
395
+ print(f"[DOWNLOAD] PDF conversion failed: {result}")
396
+ return None
397
+ except Exception as e:
398
+ print(f"[DOWNLOAD] Error creating PDF: {e}")
399
+ return None
400
+
401
+ # Store current filename for download functions
402
+ current_filename = {"name": "document"}
403
+
404
+ def process_file(file, use_gpu=True):
405
+ """Process uploaded file"""
406
+ if file is None:
407
+ return (
408
+ None, # PDF preview
409
+ "โŒ No file uploaded. Please upload a PDF or image file.",
410
+ "", # Markdown
411
+ "", # JSON
412
+ None, # DOCX download
413
+ None # PDF download
414
+ )
415
+
416
+ try:
417
+ file_path = Path(file.name)
418
+ file_ext = file_path.suffix.lower()
419
+
420
+ print(f"\n[PROCESS] ========================================")
421
+ print(f"[PROCESS] Processing: {file_path.name}")
422
+ print(f"[PROCESS] Type: {file_ext}")
423
+ print(f"[PROCESS] Size: {file_path.stat().st_size / 1024:.1f} KB")
424
+ print(f"[PROCESS] ========================================")
425
+
426
+ if file_ext not in ['.pdf', '.png', '.jpg', '.jpeg', '.bmp', '.tiff', '.tif']:
427
+ return (
428
+ None,
429
+ f"โŒ Unsupported file type: {file_ext}\n\nSupported formats: PDF, PNG, JPG, JPEG, BMP, TIFF",
430
+ "",
431
+ "",
432
+ None,
433
+ None
434
+ )
435
+
436
+ # Store filename for download functions
437
+ current_filename["name"] = file_path.name
438
+
439
+ # Create persistent temp directory for this session
440
+ temp_base = Path(tempfile.gettempdir()) / "mineru_gradio"
441
+ temp_base.mkdir(exist_ok=True)
442
+
443
+ # Convert images to PDF
444
+ input_file = file_path
445
+ pdf_preview_path = file_path if file_ext == '.pdf' else None
446
+
447
+ if file_ext in ['.png', '.jpg', '.jpeg', '.bmp', '.tiff', '.tif']:
448
+ print("[PROCESS] Converting image to PDF...")
449
+ from PIL import Image
450
+
451
+ img = Image.open(file_path)
452
+ if img.mode in ('RGBA', 'LA', 'P'):
453
+ img = img.convert('RGB')
454
+
455
+ # Create temp PDF
456
+ temp_pdf = temp_base / f"{file_path.stem}.pdf"
457
+ img.save(temp_pdf, "PDF", resolution=100.0)
458
+ input_file = temp_pdf
459
+ pdf_preview_path = temp_pdf
460
+ print(f"[PROCESS] โœ… Converted to: {temp_pdf}")
461
+
462
+ # Process with MinerU
463
+ success, message, md_content, json_content, output_dir = process_with_mineru(
464
+ str(input_file),
465
+ str(temp_base),
466
+ use_vllm=use_gpu and gpu_available
467
+ )
468
+
469
+ if success:
470
+ # Count extracted elements
471
+ pages = md_content.count('\n## ') if md_content else 0
472
+ tables = md_content.count('|') // 4 if md_content else 0
473
+
474
+ status = f"""โœ… **Processing Complete!**
475
+
476
+ ๐Ÿ“„ **File:** {file_path.name}
477
+ ๐Ÿ“‘ **Type:** {file_ext.upper()}
478
+ โšก **Device:** {gpu_info}
479
+ ๐Ÿ“Š **Pages:** {pages if pages > 0 else 'N/A'}
480
+ ๐Ÿ“‹ **Tables:** ~{tables} detected
481
+ โฑ๏ธ **Status:** Successfully extracted and parsed
482
+
483
+ ---
484
+ **Ready!** View the extracted content in the tabs below or download as DOCX/PDF.
485
+ """
486
+ # Generate download files
487
+ docx_file = download_as_docx(md_content, file_path.name) if md_content else None
488
+ pdf_file = download_as_pdf(md_content, file_path.name) if md_content else None
489
+
490
+ return (
491
+ str(pdf_preview_path) if pdf_preview_path else None,
492
+ status,
493
+ md_content if md_content else "No markdown content generated",
494
+ json_content if json_content else json.dumps({"status": "no content"}, indent=2),
495
+ docx_file,
496
+ pdf_file
497
+ )
498
+ else:
499
+ error_status = f"""โŒ **Processing Failed**
500
+
501
+ ๐Ÿ“„ **File:** {file_path.name}
502
+ โšก **Device:** {gpu_info}
503
+
504
+ **Error Details:**
505
+ ```
506
+ {message}
507
+ ```
508
+
509
+ **Troubleshooting:**
510
+ - Ensure the file is not corrupted
511
+ - Try a smaller file first
512
+ - Check console logs for details
513
+ - For images, ensure they contain readable text
514
+ """
515
+ return (
516
+ str(pdf_preview_path) if pdf_preview_path else None,
517
+ error_status,
518
+ "",
519
+ "",
520
+ None,
521
+ None
522
+ )
523
+
524
+ except Exception as e:
525
+ import traceback
526
+ error_details = traceback.format_exc()
527
+ print(f"[ERROR] {error_details}")
528
+ return (
529
+ None,
530
+ f"โŒ **Unexpected Error**\n\n```\n{str(e)}\n```\n\nSee console for full traceback.",
531
+ "",
532
+ "",
533
+ None,
534
+ None
535
+ )
536
+
537
+ # Create Gradio Interface with improved layout
538
+ with gr.Blocks(
539
+ title="MinerU OCR Tool",
540
+ theme=gr.themes.Soft(
541
+ primary_hue="blue",
542
+ secondary_hue="cyan",
543
+ ),
544
+ css="""
545
+ .gradio-container {
546
+ max-width: 1400px !important;
547
+ }
548
+ .pdf-preview {
549
+ height: 600px !important;
550
+ }
551
+ """
552
+ ) as demo:
553
+
554
+ # Header
555
+ gr.Markdown(
556
+ f"""
557
+ # ๐Ÿ”ฎ MinerU - Advanced OCR & Document Parser
558
+
559
+ Extract text, tables, and LaTeX formulas from PDFs and images with high precision.
560
+ **GitHub Dev Branch** โ€ข Better quality than PyPI โ€ข **Export to DOCX/PDF with LaTeX support**
561
+
562
+ <div style="padding: 10px; background: linear-gradient(90deg, #667eea 0%, #764ba2 100%); border-radius: 8px; color: white; text-align: center; margin: 10px 0;">
563
+ <strong>{gpu_info}</strong>
564
+ </div>
565
+ """
566
+ )
567
+
568
+ with gr.Row():
569
+ # Left column - Input and Preview
570
+ with gr.Column(scale=1):
571
+ gr.Markdown("### ๐Ÿ“ค Upload Document")
572
+
573
+ file_input = gr.File(
574
+ label="Select PDF or Image",
575
+ file_types=[".pdf", ".png", ".jpg", ".jpeg", ".bmp", ".tiff", ".tif"],
576
+ type="filepath",
577
+ file_count="single"
578
+ )
579
+
580
+ with gr.Row():
581
+ process_btn = gr.Button(
582
+ "๐Ÿš€ Process Document",
583
+ variant="primary",
584
+ size="lg",
585
+ scale=3
586
+ )
587
+
588
+ use_gpu_checkbox = gr.Checkbox(
589
+ label="GPU",
590
+ value=gpu_available,
591
+ interactive=gpu_available,
592
+ scale=1,
593
+ info="Use GPU acceleration" if gpu_available else "No GPU"
594
+ )
595
+
596
+ gr.Markdown("---")
597
+
598
+ gr.Markdown("### ๐Ÿ‘๏ธ Document Preview")
599
+
600
+ if PDF is not None:
601
+ pdf_preview = PDF(
602
+ label="PDF Preview",
603
+ height=600,
604
+ elem_classes=["pdf-preview"]
605
+ )
606
+ else:
607
+ pdf_preview = gr.File(label="File Path", visible=False)
608
+
609
+ gr.Markdown(
610
+ f"""
611
+ ---
612
+ ### ๐Ÿ“‹ Supported Formats
613
+ - **PDF**: Multi-page documents
614
+ - **Images**: PNG, JPG, JPEG, BMP, TIFF
615
+
616
+ ### โœจ Features
617
+ - ๐ŸŒ OCR for 84+ languages
618
+ - ๐Ÿ“Š Table extraction
619
+ - ๐Ÿ”ข Formula recognition
620
+ - ๐Ÿ“ Layout preservation
621
+ - {f"โšก GPU acceleration (3-10x faster)" if gpu_available else "๐Ÿ’ป CPU processing"}
622
+
623
+ ### โฑ๏ธ Processing Time
624
+ - **First document:** 5-10 min (model download)
625
+ - **Subsequent:** {f"~10-30s with GPU" if gpu_available else "~1-2min with CPU"}
626
+ """
627
+ )
628
+
629
+ # Right column - Results
630
+ with gr.Column(scale=1):
631
+ gr.Markdown("### ๐Ÿ“Š Processing Status")
632
+
633
+ status_output = gr.Textbox(
634
+ label="Status",
635
+ lines=8,
636
+ interactive=False,
637
+ show_copy_button=False,
638
+ placeholder="Upload a file and click 'Process Document' to begin..."
639
+ )
640
+
641
+ gr.Markdown("### ๐Ÿ“„ Extracted Content")
642
+
643
+ with gr.Tabs():
644
+ with gr.Tab("๐Ÿ“ Markdown"):
645
+ gr.Markdown("**Human-readable format** - Easy to read and edit")
646
+ markdown_output = gr.Textbox(
647
+ label="Markdown Output",
648
+ lines=18,
649
+ max_lines=40,
650
+ interactive=False,
651
+ show_copy_button=True,
652
+ placeholder="Markdown content will appear here after processing..."
653
+ )
654
+
655
+ # Download buttons for converted formats
656
+ gr.Markdown("### ๐Ÿ“ฅ Download Extracted Content")
657
+ gr.Markdown("**Includes LaTeX formulas** converted to Word equations (DOCX) or rendered math (PDF)")
658
+ with gr.Row():
659
+ docx_download = gr.File(
660
+ label="๐Ÿ“„ Download as DOCX (with LaTeX)",
661
+ interactive=False,
662
+ visible=True
663
+ )
664
+ pdf_download = gr.File(
665
+ label="๐Ÿ“• Download as PDF (with LaTeX)",
666
+ interactive=False,
667
+ visible=True
668
+ )
669
+
670
+ with gr.Tab("๐Ÿ“‹ JSON"):
671
+ gr.Markdown("**Structured data** - Machine-readable format with metadata")
672
+ json_output = gr.Textbox(
673
+ label="JSON Output",
674
+ lines=20,
675
+ max_lines=40,
676
+ interactive=False,
677
+ show_copy_button=True,
678
+ placeholder="JSON data will appear here after processing..."
679
+ )
680
+
681
+ # Connect button
682
+ process_btn.click(
683
+ fn=process_file,
684
+ inputs=[file_input, use_gpu_checkbox],
685
+ outputs=[pdf_preview, status_output, markdown_output, json_output, docx_download, pdf_download]
686
+ )
687
+
688
+ # Footer
689
+ gr.Markdown(
690
+ """
691
+ ---
692
+ <div style="text-align: center; padding: 20px; background: #f5f5f5; border-radius: 8px;">
693
+ <p style="margin: 5px 0;">
694
+ <strong>Powered by</strong>
695
+ <a href="https://github.com/opendatalab/MinerU" target="_blank">MinerU (Magic-PDF)</a>
696
+ </p>
697
+ <p style="margin: 5px 0; font-size: 0.9em;">
698
+ ๐Ÿ“š <a href="https://opendatalab.github.io/MinerU/" target="_blank">Documentation</a> |
699
+ ๐Ÿ’ฌ <a href="https://discord.gg/Tdedn9GTXq" target="_blank">Discord</a> |
700
+ ๐ŸŒ <a href="https://mineru.net" target="_blank">Official Demo</a>
701
+ </p>
702
+ <p style="margin: 5px 0; font-size: 0.8em; color: #666;">
703
+ MinerU converts PDFs to machine-readable formats โ€ข Supports 84+ languages โ€ข Open Source
704
+ </p>
705
+ </div>
706
+ """
707
+ )
708
+
709
+ # Launch
710
  if __name__ == "__main__":
711
+ print("\n" + "=" * 70)
712
+ print("๐Ÿš€ Starting MinerU OCR Gradio Interface")
713
+ print("=" * 70)
714
+ print(f"Device: {gpu_info}")
715
+ print(f"PDF Preview: {'Enabled' if PDF is not None else 'Disabled (install gradio-pdf)'}")
716
+ print("=" * 70 + "\n")
717
+
718
+ demo.launch(
719
+ share=True,
720
+ server_name="0.0.0.0",
721
+ server_port=7860,
722
+ show_error=True,
723
+ show_api=False
724
+ )