#!/usr/bin/env python3 """ DocTags Picture Extractor - Extract elements from DocTags. """ import argparse import os import re from pathlib import Path from PIL import Image # Add parent directory to path for imports import sys sys.path.append(str(Path(__file__).parent.parent.parent)) from backend.utils import (ensure_results_folder, load_pdf_page, normalize_coordinates, auto_adjust_coordinates, validate_coordinates) from backend.config import DEFAULT_DPI, MAX_IMAGE_WIDTH, DEFAULT_GRID_SIZE # Regular expression to extract picture location data PICTURE_PATTERN = r'.*?(.*?)' def parse_arguments(): """Parse command line arguments.""" results_dir = ensure_results_folder() parser = argparse.ArgumentParser(description='Extract pictures from DocTags format') parser.add_argument('--doctags', '-d', type=str, required=True, help='Path to DocTags file') parser.add_argument('--pdf', '-p', type=str, required=True, help='Path to original PDF file') parser.add_argument('--page', type=int, default=1, help='Page number in PDF (starts at 1)') parser.add_argument('--output', '-o', type=str, default=str(results_dir / "pictures"), help='Output directory for extracted pictures') parser.add_argument('--dpi', type=int, default=DEFAULT_DPI, help='DPI for PDF rendering') parser.add_argument('--max-width', type=int, default=MAX_IMAGE_WIDTH, help='Maximum width of output images in pixels') parser.add_argument('--adjust', action='store_true', help='Try to automatically adjust scaling') parser.add_argument('--margin', type=int, default=0, help='Add margin around extracted pictures in pixels') return parser.parse_args() def extract_pictures_from_doctags(doctags_path): """Parse DocTags file and extract picture elements with their coordinates.""" if not os.path.exists(doctags_path): raise FileNotFoundError(f"DocTags file not found: {doctags_path}") with open(doctags_path, 'r', encoding='utf-8') as f: doctags_content = f.read() pictures = [] picture_matches = re.finditer(PICTURE_PATTERN, doctags_content, re.DOTALL) for i, match in enumerate(picture_matches): x1, y1, x2, y2, caption = match.groups() # Clean caption clean_caption = re.sub(r'', '', caption).strip() pictures.append({ 'id': i + 1, 'x1': int(x1), 'y1': int(y1), 'x2': int(x2), 'y2': int(y2), 'caption': clean_caption }) return pictures def extract_and_save_pictures(image, pictures, output_dir, max_width, margin): """Extract picture regions from the image and save them as separate files.""" output_path = ensure_results_folder(output_dir) saved_files = [] for picture in pictures: try: # Add margin to coordinates x1 = max(0, picture['x1'] - margin) y1 = max(0, picture['y1'] - margin) x2 = min(image.width, picture['x2'] + margin) y2 = min(image.height, picture['y2'] + margin) # Validate coordinates if not validate_coordinates(x1, y1, x2, y2, image.width, image.height): print(f"Warning: Invalid coordinates for picture {picture['id']}") continue # Crop the image cropped_img = image.crop((x1, y1, x2, y2)) # Resize if necessary if cropped_img.width > max_width: ratio = max_width / cropped_img.width new_height = int(cropped_img.height * ratio) cropped_img = cropped_img.resize((max_width, new_height), Image.LANCZOS) # Generate filename if picture['caption']: safe_caption = re.sub(r'[^\w\s-]', '', picture['caption'])[:30].strip().replace(' ', '_').lower() filename = f"picture_{picture['id']}_{safe_caption}.png" else: filename = f"picture_{picture['id']}.png" # Save the image output_file = output_path / filename cropped_img.save(output_file, format="PNG") # Save caption if available if picture['caption']: caption_file = output_path / f"{output_file.stem}.txt" with open(caption_file, 'w', encoding='utf-8') as f: f.write(picture['caption']) print(f"Saved picture {picture['id']} to {output_file}") saved_files.append(output_file) except Exception as e: print(f"Error processing picture {picture['id']}: {e}") return saved_files def create_html_index(pictures, saved_files, pdf_name, page_num, output_dir): """Create an HTML index file of all extracted pictures.""" output_path = Path(output_dir) index_file = output_path / "index.html" html = f""" Extracted Pictures from {pdf_name} - Page {page_num}

Extracted Pictures from {pdf_name} - Page {page_num}

Total pictures found: {len(pictures)}

""" if pictures: html += ' \n' else: html += '
\n

No pictures found on this page

\n
\n' html += '\n\n' with open(index_file, 'w', encoding='utf-8') as f: f.write(html) print(f"Created index file: {index_file}") return index_file def main(): args = parse_arguments() output_dir = ensure_results_folder(args.output) try: # Extract pictures from DocTags print(f"Extracting pictures from {args.doctags}...") pictures = extract_pictures_from_doctags(args.doctags) if not pictures: print("No picture elements found in the DocTags file.") return print(f"Found {len(pictures)} picture elements.") # Load the image from PDF page_image = load_pdf_page(args.pdf, args.page, args.dpi) print(f"Loaded page {args.page} image: {page_image.size[0]}x{page_image.size[1]}") # Adjust coordinates if needed if args.adjust: # Check if coordinates need normalization max_x = max([p['x2'] for p in pictures]) max_y = max([p['y2'] for p in pictures]) if max_x <= DEFAULT_GRID_SIZE and max_y <= DEFAULT_GRID_SIZE: print(f"Detected normalized coordinates (0-{DEFAULT_GRID_SIZE} grid)") pictures = normalize_coordinates(pictures, page_image.width, page_image.height) else: pictures = auto_adjust_coordinates(pictures, page_image.width, page_image.height) # Extract and save pictures saved_files = extract_and_save_pictures( page_image, pictures, output_dir, args.max_width, args.margin ) # Create HTML index pdf_name = Path(args.pdf).stem create_html_index(pictures, saved_files, pdf_name, args.page, output_dir) except Exception as e: print(f"Error: {e}") import traceback traceback.print_exc() if __name__ == "__main__": main()