import re import gradio as gr import os import tempfile import logging import json from pathlib import Path from datetime import datetime import cv2 import numpy as np from PIL import Image import fitz # PyMuPDF from typing import Dict, List, Tuple, Optional # Load environment variables from dotenv import load_dotenv load_dotenv() from backend import BackendManager from enhanced_indentation import EnhancedIndentationDetector, OpenCVTextAnalyzer # Configure logging logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) # Initialize backend manager backend_manager = BackendManager() # Initialize enhanced indentation detector with OpenCV indent_detector = EnhancedIndentationDetector() opencv_analyzer = OpenCVTextAnalyzer() # Check if python-docx is available try: from docx import Document from docx.shared import Inches, Pt from docx.enum.table import WD_TABLE_ALIGNMENT HAS_DOCX_SUPPORT = True logger.info("DOCX export available") except ImportError: HAS_DOCX_SUPPORT = False logger.info("DOCX export not available - install python-docx to enable") # Check OpenCV availability try: import cv2 HAS_OPENCV_SUPPORT = True logger.info("OpenCV text block analysis and bold detection available") except ImportError: HAS_OPENCV_SUPPORT = False logger.info("OpenCV not available - text block analysis and bold detection disabled") # Global variables for enhanced crop management with OpenCV current_pdf_data = { 'path': None, 'page_count': 0, 'page_images': {}, 'crop_settings': {}, 'default_crop_all': True, 'opencv_analysis': {} } class PDFPageManager: """Manages PDF page previews and crop settings with OpenCV-enhanced analysis""" def __init__(self): self.pdf_doc = None self.page_images = {} self.crop_settings = {} self.current_page = 0 self.high_res_scale = 2.0 # Optimized for OpenCV analysis self.opencv_analysis = {} def load_pdf(self, pdf_path: str) -> Dict: """Load PDF and generate high-resolution page previews with OpenCV analysis""" try: if self.pdf_doc: self.pdf_doc.close() self.pdf_doc = fitz.open(pdf_path) page_count = len(self.pdf_doc) # Generate high-resolution previews and OpenCV analysis for all pages self.page_images = {} self.opencv_analysis = {} for page_num in range(page_count): # Generate high-resolution preview img_array = self._generate_high_res_preview(page_num) self.page_images[page_num] = img_array # Perform OpenCV analysis if available if HAS_OPENCV_SUPPORT and img_array is not None: # Extract text lines for OpenCV correlation page = self.pdf_doc.load_page(page_num) text_content = page.get_text() text_lines = text_content.split('\n') # Perform OpenCV text block analysis opencv_result = opencv_analyzer.analyze_text_blocks(img_array, text_lines) self.opencv_analysis[page_num] = opencv_result logger.info(f"OpenCV analysis for page {page_num + 1}: {opencv_result.get('block_count', 0)} text blocks, bold detected: {opencv_result.get('bold_text_detected', False)}") # Initialize default crop settings for all pages self.crop_settings = { i: {'top': 0, 'bottom': 0, 'left': 0, 'right': 0, 'custom': False} for i in range(page_count) } logger.info(f"PDF loaded successfully with OpenCV enhancement: {page_count} pages") return { 'success': True, 'page_count': page_count, 'pages': list(range(page_count)), 'opencv_enhanced': HAS_OPENCV_SUPPORT, 'opencv_analysis_available': bool(self.opencv_analysis) } except Exception as e: logger.error(f"Error loading PDF: {e}") return {'success': False, 'error': str(e)} def _generate_high_res_preview(self, page_num: int) -> np.ndarray: """Generate high-resolution preview optimized for OpenCV analysis""" try: if not self.pdf_doc: return None page = self.pdf_doc.load_page(page_num) # Use high resolution matrix for better OpenCV analysis mat = fitz.Matrix(self.high_res_scale, self.high_res_scale) pix = page.get_pixmap(matrix=mat) img_data = pix.tobytes("png") # Convert to PIL Image and then to numpy array import io pil_image = Image.open(io.BytesIO(img_data)) img_array = np.array(pil_image) # Convert RGBA to RGB if needed, then to BGR for OpenCV if len(img_array.shape) == 3 and img_array.shape[2] == 4: img_array = img_array[:, :, :3] # Convert RGB to BGR for OpenCV compatibility if len(img_array.shape) == 3: img_array = cv2.cvtColor(img_array, cv2.COLOR_RGB2BGR) return img_array except Exception as e: logger.error(f"Error generating preview for page {page_num}: {e}") return None def update_crop_visualization(self, page_num: int, crop_coords: Dict) -> np.ndarray: """Update crop visualization with OpenCV-enhanced preview and text block overlay""" if page_num not in self.page_images or self.page_images[page_num] is None: logger.warning(f"No image available for page {page_num}") return None try: img_array = self.page_images[page_num].copy() height, width = img_array.shape[:2] # Convert coordinates from percentages to pixels x1 = int(crop_coords.get('left', 0) * width / 100) y1 = int(crop_coords.get('top', 0) * height / 100) x2 = width - int(crop_coords.get('right', 0) * width / 100) y2 = height - int(crop_coords.get('bottom', 0) * height / 100) # Ensure coordinates are valid x1 = max(0, min(x1, width)) x2 = max(0, min(x2, width)) y1 = max(0, min(y1, height)) y2 = max(0, min(y2, height)) # Create overlay overlay = img_array.copy() # Draw crop areas in semi-transparent red (areas to be removed) alpha = 0.3 if crop_coords.get('top', 0) > 0 and y1 > 0: cv2.rectangle(overlay, (0, 0), (width, y1), (0, 0, 255), -1) if crop_coords.get('bottom', 0) > 0 and y2 < height: cv2.rectangle(overlay, (0, y2), (width, height), (0, 0, 255), -1) if crop_coords.get('left', 0) > 0 and x1 > 0: cv2.rectangle(overlay, (0, 0), (x1, height), (0, 0, 255), -1) if crop_coords.get('right', 0) > 0 and x2 < width: cv2.rectangle(overlay, (x2, 0), (width, height), (0, 0, 255), -1) # Draw content area outline in green if x2 > x1 and y2 > y1: thickness = max(2, int(self.high_res_scale * 2)) cv2.rectangle(overlay, (x1, y1), (x2, y2), (0, 255, 0), thickness) # Add OpenCV text block visualization if available if HAS_OPENCV_SUPPORT and page_num in self.opencv_analysis: opencv_result = self.opencv_analysis[page_num] if opencv_result.get('success', False): # Draw text blocks in blue for block in opencv_result.get('text_blocks', []): block_x = block.get('x', 0) block_y = block.get('y', 0) block_w = block.get('width', 0) block_h = block.get('height', 0) cv2.rectangle(overlay, (block_x, block_y), (block_x + block_w, block_y + block_h), (255, 100, 0), 2) # Blue for text blocks # Draw bold regions in orange for bold_region in opencv_result.get('bold_regions', []): if bold_region.get('is_likely_header', False): bold_x = bold_region.get('x', 0) bold_y = bold_region.get('y', 0) bold_w = bold_region.get('width', 0) bold_h = bold_region.get('height', 0) cv2.rectangle(overlay, (bold_x, bold_y), (bold_x + bold_w, bold_y + bold_h), (0, 165, 255), 3) # Orange for bold headers # Blend overlay with original result = cv2.addWeighted(img_array, 1-alpha, overlay, alpha, 0) # Add informative text with OpenCV enhancement info font_scale = max(0.8, self.high_res_scale / 3) thickness = max(1, int(self.high_res_scale)) text_color = (255, 255, 255) background_color = (0, 0, 0) # Add text with background for better visibility texts = [ f"Page {page_num + 1}", "RED: Remove areas", "GREEN: Content area" ] # Add OpenCV-specific information if HAS_OPENCV_SUPPORT and page_num in self.opencv_analysis: opencv_result = self.opencv_analysis[page_num] if opencv_result.get('success', False): texts.extend([ "BLUE: Text blocks", "ORANGE: Bold headers", f"Blocks: {opencv_result.get('block_count', 0)}", f"Bold detected: {'Yes' if opencv_result.get('bold_text_detected', False) else 'No'}" ]) texts.append(f"Crop: T{crop_coords.get('top', 0):.1f}% B{crop_coords.get('bottom', 0):.1f}% L{crop_coords.get('left', 0):.1f}% R{crop_coords.get('right', 0):.1f}%") y_offset = 30 for i, text in enumerate(texts): y_pos = y_offset + (i * 30) # Add background rectangle for text (text_width, text_height), _ = cv2.getTextSize(text, cv2.FONT_HERSHEY_SIMPLEX, font_scale, thickness) cv2.rectangle(result, (10, y_pos - text_height - 5), (text_width + 20, y_pos + 5), background_color, -1) cv2.putText(result, text, (15, y_pos), cv2.FONT_HERSHEY_SIMPLEX, font_scale, text_color, thickness) # Convert back to RGB for display result_rgb = cv2.cvtColor(result, cv2.COLOR_BGR2RGB) return result_rgb except Exception as e: logger.error(f"Error updating crop visualization: {e}") if page_num in self.page_images and self.page_images[page_num] is not None: # Convert BGR to RGB for display return cv2.cvtColor(self.page_images[page_num], cv2.COLOR_BGR2RGB) return None def set_crop_for_page(self, page_num: int, crop_coords: Dict): """Set crop coordinates for specific page""" if page_num in self.crop_settings: self.crop_settings[page_num].update(crop_coords) self.crop_settings[page_num]['custom'] = True logger.info(f"Set crop for page {page_num}: {crop_coords}") def set_crop_for_all_pages(self, crop_coords: Dict): """Apply same crop settings to all pages""" for page_num in self.crop_settings: if not self.crop_settings[page_num].get('custom', False): self.crop_settings[page_num].update(crop_coords) logger.info(f"Applied crop to all non-custom pages: {crop_coords}") def get_crop_settings_for_processing(self) -> Dict: """Get crop settings in format expected by backend""" return { 'per_page_crops': self.crop_settings, 'has_custom_crops': any(page.get('custom', False) for page in self.crop_settings.values()), 'enhanced_resolution': True, 'resolution_scale': self.high_res_scale, 'opencv_enhanced': HAS_OPENCV_SUPPORT, 'opencv_analysis': self.opencv_analysis } def get_opencv_analysis(self, page_num: int = None) -> Dict: """Get OpenCV analysis for specific page or all pages""" if page_num is not None: return self.opencv_analysis.get(page_num, {}) return self.opencv_analysis def close(self): """Clean up resources""" if self.pdf_doc: self.pdf_doc.close() self.pdf_doc = None self.page_images.clear() self.crop_settings.clear() self.opencv_analysis.clear() # Global page manager instance pdf_manager = PDFPageManager() def load_pdf_for_preview(pdf_file): """Load PDF and return page thumbnails with OpenCV analysis""" if pdf_file is None: return None, gr.update(choices=[], value=None), gr.update(visible=False), "No PDF loaded" try: result = pdf_manager.load_pdf(pdf_file.name) if result['success']: # Create page choices for dropdown page_choices = [f"Page {i+1}" for i in range(result['page_count'])] # Get first page preview with default crop and OpenCV overlay first_page_preview = pdf_manager.update_crop_visualization(0, { 'top': 0, 'bottom': 0, 'left': 0, 'right': 0 }) if 0 in pdf_manager.page_images else None # Create status message with OpenCV information status_parts = [f"PDF loaded successfully: {result['page_count']} pages"] if result.get('opencv_enhanced'): status_parts.append("OpenCV text block analysis: Enabled") opencv_analysis = pdf_manager.get_opencv_analysis() if opencv_analysis: total_blocks = sum(analysis.get('block_count', 0) for analysis in opencv_analysis.values()) bold_pages = sum(1 for analysis in opencv_analysis.values() if analysis.get('bold_text_detected', False)) status_parts.append(f"Total text blocks detected: {total_blocks}") status_parts.append(f"Pages with bold text: {bold_pages}") else: status_parts.append("OpenCV analysis: Not available") status = " | ".join(status_parts) return (first_page_preview, gr.update(choices=page_choices, value=page_choices[0] if page_choices else None, visible=True), gr.update(visible=True), status) else: return None, gr.update(choices=[], value=None, visible=False), gr.update(visible=False), f"Error: {result['error']}" except Exception as e: logger.error(f"Error in load_pdf_for_preview: {e}") return None, gr.update(choices=[], value=None, visible=False), gr.update(visible=False), f"Error loading PDF: {str(e)}" def change_preview_page(page_selection, crop_top, crop_bottom, crop_left, crop_right): """Change preview to selected page with OpenCV-enhanced visualization""" if not page_selection: return None try: page_num = int(page_selection.split()[1]) - 1 # Extract page number # Get current crop settings for this page crop_coords = { 'top': crop_top, 'bottom': crop_bottom, 'left': crop_left, 'right': crop_right } # Update visualization with OpenCV enhancement preview_image = pdf_manager.update_crop_visualization(page_num, crop_coords) return preview_image except Exception as e: logger.error(f"Error changing preview page: {e}") return None def update_crop_preview_interactive(page_selection, crop_top, crop_bottom, crop_left, crop_right, apply_to_all): """Update crop preview with OpenCV-enhanced interactive feedback""" if not page_selection or not pdf_manager.pdf_doc: return None try: page_num = int(page_selection.split()[1]) - 1 crop_coords = { 'top': crop_top, 'bottom': crop_bottom, 'left': crop_left, 'right': crop_right } # Apply to current page or all pages based on setting if apply_to_all: pdf_manager.set_crop_for_all_pages(crop_coords) else: pdf_manager.set_crop_for_page(page_num, crop_coords) # Return updated preview with OpenCV enhancement return pdf_manager.update_crop_visualization(page_num, crop_coords) except Exception as e: logger.error(f"Error updating crop preview: {e}") return None def process_pdf_with_opencv_enhancement(pdf_file, ocr_method, enable_header_footer_removal, crop_top, crop_bottom, crop_left, crop_right, apply_to_all_pages, current_page_selection, progress=gr.Progress()): """Process PDF with OpenCV-enhanced text block analysis, bold detection, and comprehensive formatting""" if pdf_file is None: return "No file uploaded.", "", "", "Error: No file selected" try: progress(0.1, desc="Initializing OpenCV-enhanced processing with text block analysis and bold detection...") # Prepare enhanced preprocessing options with OpenCV data preprocessing_options = { 'enable_header_footer_removal': enable_header_footer_removal, 'enhanced_crop_processing': True, 'opencv_enhanced': HAS_OPENCV_SUPPORT, 'crop_settings': pdf_manager.get_crop_settings_for_processing() if enable_header_footer_removal else None } progress(0.3, desc="Processing with OpenCV text block analysis, bold detection, and comprehensive indentation...") # Process the PDF with OpenCV-enhanced analysis result = backend_manager.process_pdf_with_enhanced_resolution( pdf_file.name, ocr_method, preprocessing_options ) progress(0.9, desc="Finalizing OpenCV-enhanced processing...") progress(1.0, desc="Complete!") if result['success']: # Clean any remaining artifacts from text and HTML result['text'] = result['text'].replace(':unselected:', '').replace(':selected:', '') result['html'] = result['html'].replace(':unselected:', '').replace(':selected:', '') metadata_info = format_opencv_enhanced_metadata(result['metadata'], result['method_used']) status_parts = [f"Success: Processed using {result['method_used']}"] status_parts.append("OpenCV text block analysis: Enabled") status_parts.append("Bold text detection: Enabled") status_parts.append("Comprehensive indentation detection: Enabled") status_parts.append("Intelligent text classification: Enabled") status = " | ".join(status_parts) # Return text, HTML, metadata, and status return (result['text'], result.get('html', ''), metadata_info, status) else: error_msg = result.get('error', 'Unknown error occurred') return f"Error: {error_msg}", "", "", f"Processing failed: {error_msg}" except Exception as e: logger.error(f"OpenCV-enhanced processing error: {e}") return f"Error: {str(e)}", "", "", f"Unexpected error: {str(e)}" def format_opencv_enhanced_metadata(metadata, method_used): """Enhanced metadata formatting with OpenCV text block analysis and bold detection info""" if not metadata: return f"Method used: {method_used}" info_lines = [f"Method used: {method_used}"] if 'pages' in metadata: info_lines.append(f"Pages processed: {metadata['pages']}") if metadata.get('opencv_enhanced', False): info_lines.append("OpenCV enhancement: Enabled") if metadata.get('opencv_text_block_analysis', False): info_lines.append("OpenCV text block analysis: Enabled") if metadata.get('opencv_bold_detection', False): info_lines.append("OpenCV bold text detection: Enabled") if metadata.get('opencv_spacing_analysis', False): info_lines.append("OpenCV spacing analysis: Enabled") if metadata.get('header_indentation_suppression', False): info_lines.append("Header indentation suppression: Enabled") if metadata.get('enhanced_processing', False): info_lines.append("Enhanced processing: Enabled") if metadata.get('html_processing', False): info_lines.append("HTML generation: Enabled") if metadata.get('comprehensive_indentation', False): info_lines.append("Comprehensive indentation detection: Enabled") if metadata.get('intelligent_text_classification', False): info_lines.append("Intelligent text classification: Enabled") if metadata.get('parenthetical_patterns_supported', False): info_lines.append("Parenthetical patterns: Supported (Arabic, Thai, Letters, Roman)") if metadata.get('enhanced_resolution', False) and 'resolution_scale' in metadata: info_lines.append(f"Enhanced resolution: {metadata.get('resolution_scale', 'N/A')}x") if 'custom_crops_applied' in metadata: info_lines.append(f"Custom crops per page: {metadata['custom_crops_applied']}") if 'tables' in metadata: info_lines.append(f"Tables detected: {metadata['tables']}") # OpenCV-specific analysis information if 'opencv_global_analysis' in metadata: opencv_analysis = metadata['opencv_global_analysis'] if opencv_analysis.get('success', False): info_lines.append(f"OpenCV text blocks detected: {opencv_analysis.get('block_count', 0)}") info_lines.append(f"OpenCV paragraphs detected: {opencv_analysis.get('paragraph_count', 0)}") info_lines.append(f"OpenCV bold text detected: {'Yes' if opencv_analysis.get('bold_text_detected', False) else 'No'}") # Document structure analysis information if 'document_structure_analysis' in metadata: analysis = metadata['document_structure_analysis'] if not analysis.get('analysis_failed', False): info_lines.append(f"Patterned lines detected: {analysis.get('patterned_lines', 0)}") info_lines.append(f"Maximum indentation level: {analysis.get('max_level', 0)}") info_lines.append(f"Pattern coverage: {analysis.get('coverage_percentage', 0):.1f}%") # Text classification results if 'text_classification' in analysis: classification = analysis['text_classification'] info_lines.append(f"Headers detected: {analysis.get('header_count', 0)}") info_lines.append(f"Paragraphs detected: {analysis.get('paragraph_count', 0)}") info_lines.append(f"List items detected: {analysis.get('list_item_count', 0)}") if analysis.get('dominant_patterns'): dominant = analysis['dominant_patterns'][0][0] if analysis['dominant_patterns'] else 'None' info_lines.append(f"Dominant pattern: {dominant}") if 'processing_time_seconds' in metadata: info_lines.append(f"Processing time: {metadata['processing_time_seconds']:.2f} seconds") return "\n".join(info_lines) def prepare_opencv_enhanced_downloads(pdf_file, method, enable_header_footer_removal, crop_top, crop_bottom, crop_left, crop_right, apply_to_all_pages, current_page_selection): """Prepare OpenCV-enhanced downloads with text block analysis and bold detection""" text, html, metadata, status = process_pdf_with_opencv_enhancement( pdf_file, method, enable_header_footer_removal, crop_top, crop_bottom, crop_left, crop_right, apply_to_all_pages, current_page_selection ) # Prepare downloads if processing was successful if text and not text.startswith("Error:") and not text.startswith("No file"): try: # Create OpenCV-enhanced download files download_files = backend_manager.create_enhanced_downloads(text, html, metadata) # Prepare gradio updates for download buttons updates = [ text, metadata, status, # Display outputs gr.update(visible=True, value=download_files.get('txt')) if 'txt' in download_files else gr.update(visible=False), gr.update(visible=True, value=download_files.get('docx')) if 'docx' in download_files else gr.update(visible=False), gr.update(visible=True, value=download_files.get('html')) if 'html' in download_files else gr.update(visible=False) ] return tuple(updates) except Exception as file_error: logger.error(f"OpenCV-enhanced file creation error: {file_error}") return (text, metadata, status, gr.update(visible=False), gr.update(visible=False), gr.update(visible=False)) else: return (text, metadata, status, gr.update(visible=False), gr.update(visible=False), gr.update(visible=False)) def get_opencv_enhanced_method_info(method): """Get information about selected OCR method with OpenCV enhancements""" opencv_status = "with OpenCV Text Block Analysis & Bold Detection" if HAS_OPENCV_SUPPORT else "(OpenCV not available)" method_descriptions = { "auto": f"**Auto Selection**: Automatically chooses the best available method {opencv_status}, comprehensive indentation detection, intelligent text classification, HTML processing, enhanced pattern recognition for hierarchical numbering (including parenthetical patterns like (1), (๑), (a)), bullets, and multi-language support with header indentation suppression.", "azure": f"**Azure Document Intelligence**: Advanced cloud-based OCR {opencv_status}, comprehensive indentation detection, intelligent text classification, HTML generation, layout preservation, smart table detection, bold text recognition, and support for complex document structures including hierarchical numbering and parenthetical patterns with header detection.", "tesseract": f"**Tesseract OCR**: Open-source OCR enhanced {opencv_status}, comprehensive indentation detection, intelligent text classification, HTML output, advanced image preprocessing, resolution scaling, bold text detection, and pattern recognition for various numbering styles including parenthetical patterns and bullet points with header analysis.", "pymupdf": f"**PyMuPDF**: Fast extraction enhanced {opencv_status}, comprehensive indentation detection, intelligent text classification, HTML processing, improved formatting preservation, bold text recognition, and pattern recognition for maintaining document structure and hierarchy including parenthetical numbering with header detection." } return method_descriptions.get(method, "Select a method to see details.") def check_opencv_enhanced_service_status(): """Check and display OpenCV-enhanced service status with text block analysis and bold detection capabilities""" available_methods = backend_manager.get_available_methods() status_lines = ["**Available OCR Methods (Enhanced with OpenCV Text Block Analysis & Bold Detection):**"] opencv_status = " + OpenCV Enhanced" if HAS_OPENCV_SUPPORT else " (OpenCV not available)" if "azure" in available_methods: status_lines.append(f"✅ Azure Document Intelligence - Ready (HTML + Tables + Comprehensive Indentation + Text Classification{opencv_status})") else: status_lines.append("❌ Azure Document Intelligence - Not configured") if "tesseract" in available_methods: status_lines.append(f"✅ Tesseract OCR - Ready (HTML Enhanced + Comprehensive Indentation + Text Classification{opencv_status})") else: status_lines.append("❌ Tesseract OCR - Not available") if "pymupdf" in available_methods: status_lines.append(f"✅ PyMuPDF - Ready (HTML Enhanced + Comprehensive Indentation + Text Classification{opencv_status})") else: status_lines.append("❌ PyMuPDF - Not available") # Add OpenCV features status status_lines.append("") if HAS_OPENCV_SUPPORT: status_lines.append("**OpenCV Text Block Analysis & Bold Detection Features:**") status_lines.append("✅ Text Block Detection & Analysis") status_lines.append("✅ Bold Text Recognition for Headers") status_lines.append("✅ Automatic Spacing & Paragraph Detection") status_lines.append("✅ Visual Text Element Analysis") status_lines.append("✅ Header Indentation Suppression") status_lines.append("✅ Real-time Crop Preview with Text Overlay") status_lines.append("✅ Enhanced High-Resolution Processing") else: status_lines.append("**OpenCV Features:**") status_lines.append("❌ OpenCV not available - install opencv-python to enable") status_lines.append("❌ Text block analysis disabled") status_lines.append("❌ Bold detection disabled") # Add enhanced features status status_lines.append("") status_lines.append("**Comprehensive Indentation Detection Features:**") status_lines.append("✅ Hierarchical Decimal Numbering (1.1.1.1.1...)") status_lines.append("✅ Mixed Hierarchical Numbering (1.2.a.i.A...)") status_lines.append("✅ Legal Numbering (1.1.1(a)(i))") status_lines.append("✅ Outline Numbering (I.A.1.a.i.)") status_lines.append("✅ Section Numbering (§1.2.3, Article 1.1.1)") status_lines.append("✅ Parenthetical Arabic Numerals ((1), (2), (3))") status_lines.append("✅ Parenthetical Thai Numerals ((๑), (๒), (๓))") status_lines.append("✅ Parenthetical Letters ((a), (b), (A), (B))") status_lines.append("✅ Parenthetical Roman Numerals ((i), (ii), (I), (II))") status_lines.append("✅ Parenthetical Thai Letters ((ก), (ข), (ค))") status_lines.append("✅ Thai Script Support (มาตรา, ข้อ, ก.ข.ค.)") status_lines.append("✅ Multiple Bullet Styles (•◦▪→ and more)") status_lines.append("✅ Checkbox Items ([x], [ ], [✓])") status_lines.append("✅ Roman Numerals (I.II.III, i.ii.iii)") status_lines.append("✅ Letter Lists (A.B.C, a.b.c)") status_lines.append("✅ Space-based Indentation Detection") status_lines.append("✅ Priority-based Pattern Matching") status_lines.append("") status_lines.append("**Intelligent Text Classification Features:**") status_lines.append("✅ Header Detection (title case, all caps, short lines)") status_lines.append("✅ Paragraph Classification (long text, proper punctuation)") status_lines.append("✅ List Item Recognition (patterned content)") status_lines.append("✅ Context-aware Analysis (position, font size)") status_lines.append("✅ Confidence Scoring") status_lines.append("✅ Document Structure Analysis") if HAS_OPENCV_SUPPORT: status_lines.append("✅ OpenCV-Enhanced Bold Text Detection") status_lines.append("✅ Header Indentation Suppression") status_lines.append("") status_lines.append("**Enhanced Processing Features:**") status_lines.append("✅ HTML Processing - Available") status_lines.append("✅ Enhanced Table Handling - Available") status_lines.append("✅ Smart Text Preservation - Available") status_lines.append("✅ Multi-Page Crop Preview - Available") status_lines.append("✅ Per-Page Crop Customization - Available") status_lines.append("✅ Document Structure Analysis - Available") if HAS_OPENCV_SUPPORT: status_lines.append("✅ OpenCV Text Block Overlay - Available") status_lines.append("✅ Bold Text Visualization - Available") if HAS_DOCX_SUPPORT: status_lines.append("✅ Enhanced DOCX Export - Available (with OpenCV-enhanced indentation formatting)") else: status_lines.append("❌ Enhanced DOCX Export - Install python-docx to enable") status_lines.append("✅ HTML File Export - Available") status_lines.append("✅ Enhanced Text Export - Available") # Add pattern detection statistics pattern_count = len(indent_detector.patterns) status_lines.append(f"✅ Pattern Detection Engine - {pattern_count} patterns supported") return "\n".join(status_lines) def create_opencv_enhanced_interface(): """Create OpenCV-enhanced Gradio interface with text block analysis and bold detection""" with gr.Blocks( title="PDF OCR Service - Enhanced with OpenCV Text Block Analysis & Bold Detection", theme=gr.themes.Soft(), css=""" .main-header { text-align: center; margin-bottom: 2rem; } .config-panel { border: 2px solid #007bff; padding: 1.5rem; border-radius: 0.8rem; background-color: #f8f9fa; margin-bottom: 1rem; } .instructions-panel { border: 2px solid #28a745; padding: 1.5rem; border-radius: 0.8rem; background-color: #f0fff0; margin-bottom: 1rem; } .crop-controls { border: 2px solid #ffc107; padding: 1rem; border-radius: 0.5rem; background-color: #fffef7; } .page-preview { border: 2px solid #17a2b8; padding: 1rem; border-radius: 0.5rem; background-color: #f0f8ff; } .results-panel { border: 2px solid #6f42c1; padding: 1rem; border-radius: 0.5rem; background-color: #f8f5ff; } .status-box { border-left: 4px solid #007bff; padding: 1rem; background-color: #f8f9fa; } .opencv-panel { border: 2px solid #e74c3c; padding: 1rem; border-radius: 0.5rem; background-color: #fdf2f2; } """ ) as interface: gr.HTML(f"""

PDF OCR Service - Enhanced with OpenCV Text Block Analysis & Bold Detection

Convert PDF documents to text using OpenCV-enhanced OCR with text block detection, bold text recognition, HTML intermediate processing, smart table handling, comprehensive indentation pattern recognition including parenthetical patterns like (1), (๑), (a), and intelligent text classification for headers, paragraphs, and list items

OpenCV Status: {'✅ Available - Text block analysis and bold detection enabled' if HAS_OPENCV_SUPPORT else '❌ Not available - Install opencv-python for enhanced features'}

""") # Instructions at the top with gr.Group(elem_classes=["instructions-panel"]): gr.HTML("

Instructions & OpenCV-Enhanced Features

") gr.HTML(f"""

How to Use:

  1. Upload PDF: Select your PDF file in the configuration panel below
  2. Choose Method: Select OCR method (Auto recommended for best results)
  3. Configure Crop (Optional): Enable header/footer removal and adjust crop settings with OpenCV visualization
  4. Process: Click the process button to extract text with OpenCV text block analysis and bold detection
  5. Download: Get results in TXT, DOCX, or HTML format with preserved formatting and header detection

OpenCV Text Block Analysis & Bold Detection Features:

OpenCV Enhancements {'✅' if HAS_OPENCV_SUPPORT else '❌'}:
  • Text Block Detection & Analysis
  • Entire Line Bold Text Recognition for Headers
  • Automatic Spacing & Paragraph Detection
  • Visual Text Element Analysis
  • Header Indentation Suppression
  • Real-time Text Block Overlay
  • 4-Space Indentation System
Hierarchical Numbering:
  • Decimal: 1.1.1.1.1...
  • Mixed: 1.2.a.i.A...
  • Legal: 1.1.1(a)(i)
  • Outline: I.A.1.a.i.
  • Section: §1.2.3, Article 1.1.1
Parenthetical Patterns:
  • Arabic: (1), (2), (3)
  • Thai Numerals: (๑), (๒), (๓)
  • Letters: (a), (b), (A), (B)
  • Roman: (i), (ii), (I), (II)
  • Thai Letters: (ก), (ข), (ค)
Multi-Language & Symbols:
  • Thai Script: มาตรา, ข้อ, ก.ข.ค.
  • Bullets: •◦▪→ and 20+ more
  • Roman: I.II.III, i.ii.iii
  • Letters: A.B.C, a.b.c
  • Checkboxes: [x], [ ], [✓]
Intelligent Text Classification:
  • Header Detection: Title case, all caps, short lines
  • Paragraph Recognition: Long text, proper punctuation
  • List Item Identification: Patterned content
  • Context Analysis: Position, font size, formatting
  • Confidence Scoring: Reliability assessment
  • OpenCV Bold Detection: {'Enabled' if HAS_OPENCV_SUPPORT else 'Disabled'}
Technical Enhancements:
  • OpenCV Text Block Analysis: {'Enabled' if HAS_OPENCV_SUPPORT else 'Disabled'}
  • Bold Text Recognition: {'Enabled' if HAS_OPENCV_SUPPORT else 'Disabled'}
  • Header Indentation Suppression: {'Enabled' if HAS_OPENCV_SUPPORT else 'Disabled'}
  • Smart Table Detection: 70% overlap threshold prevents text loss
  • HTML Processing: Better structure and formatting preservation
  • Multi-format Export: TXT, DOCX, and HTML downloads with preserved indentation
""") # Configuration Panel - Top Left with gr.Group(elem_classes=["config-panel"]): gr.HTML("

Configuration Panel

") with gr.Row(): with gr.Column(scale=1): # File upload pdf_input = gr.File( label="Upload PDF File", file_types=[".pdf"], file_count="single" ) # PDF loading status pdf_load_status = gr.Textbox( label="PDF Status", interactive=False, lines=1, value="No PDF loaded" ) with gr.Column(scale=1): # OCR method selection method_choice = gr.Dropdown( choices=["auto", "azure", "tesseract", "pymupdf"], value="auto", label="OCR Method", info=f"Choose OCR method (all enhanced with OpenCV {'✅' if HAS_OPENCV_SUPPORT else '❌'} + comprehensive indentation detection and text classification)" ) # Method information display method_info = gr.Markdown( value=get_opencv_enhanced_method_info("auto"), elem_classes=["method-info"] ) # Enhanced Header/Footer Removal Section with gr.Group(elem_classes=["crop-controls"]): gr.HTML("

Header/Footer Removal & Crop Settings with OpenCV Enhancement

") enable_header_footer_removal = gr.Checkbox( label="Enable Enhanced Header/Footer Removal with OpenCV Analysis", value=False, info=f"Remove headers and footers with high-resolution processing {'+ OpenCV text block analysis' if HAS_OPENCV_SUPPORT else ''}" ) # Multi-page controls with gr.Group(visible=False) as crop_controls: gr.HTML(f"
Multi-Page Crop Control {'with OpenCV Text Block Overlay' if HAS_OPENCV_SUPPORT else ''}
") with gr.Row(): # Page selection page_selector = gr.Dropdown( label="Select Page for Preview", choices=[], value=None, info=f"Choose page to preview and customize crop settings {'(with OpenCV overlay)' if HAS_OPENCV_SUPPORT else ''}", visible=False ) # Apply to all pages toggle apply_to_all_pages = gr.Checkbox( label="Apply crop settings to all pages", value=True, info="When enabled, changes apply to all pages" ) if HAS_OPENCV_SUPPORT: gr.HTML("
OpenCV Visual Indicators
") gr.HTML("""
🔴 RED: Crop areas
🟢 GREEN: Content area
🔵 BLUE: Text blocks
🟠 ORANGE: Bold headers
""") gr.HTML("
Crop Areas (% of page)
") with gr.Row(): crop_top = gr.Slider( minimum=0, maximum=40, value=8, step=0.5, label="Top Crop %" ) crop_bottom = gr.Slider( minimum=0, maximum=40, value=8, step=0.5, label="Bottom Crop %" ) with gr.Row(): crop_left = gr.Slider( minimum=0, maximum=30, value=3, step=0.5, label="Left Crop %" ) crop_right = gr.Slider( minimum=0, maximum=30, value=3, step=0.5, label="Right Crop %" ) # Quick preset buttons with gr.Row(): preset_light = gr.Button("Light Crop (5%)", size="sm") preset_medium = gr.Button("Medium Crop (10%)", size="sm") preset_heavy = gr.Button("Heavy Crop (15%)", size="sm") preset_reset = gr.Button("Reset", size="sm") # Process button process_btn = gr.Button( f"Process PDF with OpenCV {'✅' if HAS_OPENCV_SUPPORT else '❌'} + Comprehensive Indentation Detection & Text Classification", variant="primary", size="lg" ) # Results and Preview Section with gr.Row(): with gr.Column(scale=1): # Enhanced crop preview with OpenCV overlay with gr.Group(visible=False, elem_classes=["page-preview"]) as preview_group: gr.HTML(f"

Page Preview with OpenCV-Enhanced Crop Visualization {'& Text Block Overlay' if HAS_OPENCV_SUPPORT else ''}

") crop_preview = gr.Image( label="High-Resolution Page Preview with OpenCV Enhancement", interactive=False, height=500, show_label=False ) if HAS_OPENCV_SUPPORT: gr.HTML("""

Red areas: Will be removed | Green outline: Content area | Blue rectangles: OpenCV text blocks | Orange rectangles: Bold headers | Enhanced: 2x resolution processing with OpenCV analysis

""") else: gr.HTML("""

Red areas: Will be removed | Green outline: Content area | Enhanced: 2x resolution processing

""") with gr.Column(scale=2): with gr.Group(elem_classes=["results-panel"]): gr.HTML("

Results & Downloads

") # Processing status processing_status = gr.Textbox( label="Processing Status", interactive=False, lines=1 ) # Extracted text output text_output = gr.Textbox( label=f"Extracted Text (OpenCV {'✅' if HAS_OPENCV_SUPPORT else '❌'} Enhanced with Comprehensive Indentation Detection & Text Classification)", placeholder=f"Processed text with {'OpenCV text block analysis, bold detection, ' if HAS_OPENCV_SUPPORT else ''}comprehensive indentation detection, intelligent text classification, HTML enhancement, and preserved formatting will appear here...", lines=20, max_lines=30, interactive=False, show_copy_button=True ) # Metadata information metadata_output = gr.Textbox( label="Processing Information & Document Analysis", interactive=False, lines=10 ) # Enhanced download buttons with gr.Row(): download_txt_btn = gr.DownloadButton( "Download Enhanced TXT", visible=False, variant="secondary" ) download_docx_btn = gr.DownloadButton( f"Download Enhanced DOCX (with OpenCV {'✅' if HAS_OPENCV_SUPPORT else '❌'} + Indentation & Classification)", visible=False, variant="secondary" ) download_html_btn = gr.DownloadButton( "Download HTML File", visible=False, variant="secondary" ) # Service Status at the bottom with gr.Group(elem_classes=["status-box"]): gr.HTML("

Service Status & OpenCV-Enhanced Capabilities

") service_status = gr.Markdown( value=check_opencv_enhanced_service_status() ) # Refresh status button refresh_btn = gr.Button("Refresh Status", size="sm") # Event handlers with OpenCV enhancement # PDF upload handler pdf_input.change( fn=load_pdf_for_preview, inputs=[pdf_input], outputs=[crop_preview, page_selector, crop_controls, pdf_load_status] ) # Method info handler method_choice.change( fn=get_opencv_enhanced_method_info, inputs=[method_choice], outputs=[method_info] ) # Header/footer removal handler enable_header_footer_removal.change( fn=lambda enabled: [ gr.update(visible=enabled), gr.update(visible=enabled) ], inputs=[enable_header_footer_removal], outputs=[crop_controls, preview_group] ) # Page selection handler page_selector.change( fn=change_preview_page, inputs=[page_selector, crop_top, crop_bottom, crop_left, crop_right], outputs=[crop_preview] ) # Crop parameter handlers - update preview in real-time with OpenCV enhancement for crop_input in [crop_top, crop_bottom, crop_left, crop_right, apply_to_all_pages]: crop_input.change( fn=update_crop_preview_interactive, inputs=[page_selector, crop_top, crop_bottom, crop_left, crop_right, apply_to_all_pages], outputs=[crop_preview] ) # Preset button handlers def apply_preset(top, bottom, left, right): return top, bottom, left, right preset_light.click( fn=lambda: apply_preset(5, 5, 2, 2), outputs=[crop_top, crop_bottom, crop_left, crop_right] ) preset_medium.click( fn=lambda: apply_preset(10, 10, 5, 5), outputs=[crop_top, crop_bottom, crop_left, crop_right] ) preset_heavy.click( fn=lambda: apply_preset(15, 15, 8, 8), outputs=[crop_top, crop_bottom, crop_left, crop_right] ) preset_reset.click( fn=lambda: apply_preset(0, 0, 0, 0), outputs=[crop_top, crop_bottom, crop_left, crop_right] ) # Status refresh handler refresh_btn.click( fn=check_opencv_enhanced_service_status, outputs=[service_status] ) # Main processing handler with OpenCV enhancement process_btn.click( fn=prepare_opencv_enhanced_downloads, inputs=[pdf_input, method_choice, enable_header_footer_removal, crop_top, crop_bottom, crop_left, crop_right, apply_to_all_pages, page_selector], outputs=[text_output, metadata_output, processing_status, download_txt_btn, download_docx_btn, download_html_btn] ) return interface def launch_opencv_enhanced_ui(): """Launch the OpenCV-enhanced Gradio interface with text block analysis and bold detection""" try: interface = create_opencv_enhanced_interface() interface.launch( server_name="0.0.0.0", server_port=7860, share=False, show_error=True ) finally: # Clean up resources pdf_manager.close() if __name__ == "__main__": launch_opencv_enhanced_ui()