import fitz # PyMuPDF from PIL import Image import io import json def build_searchable_pdf(image_list, ocr_results_list, output_path="searchable.pdf"): doc = fitz.open() for i, (image, ocr_data) in enumerate(zip(image_list, ocr_results_list)): # Convert PIL Image to bytes for embedding img_byte_arr = io.BytesIO() image.save(img_byte_arr, format='PNG') # Use PNG for lossless quality img_bytes = img_byte_arr.getvalue() # Create a new page with the original image dimensions img_width, img_height = image.size page = doc.new_page(width=img_width, height=img_height) # Embed the original image as the background rect = fitz.Rect(0, 0, img_width, img_height) page.insert_image(rect, stream=img_bytes) # Directly use OCR results (now assumed to be a dictionary) if not isinstance(ocr_data, dict): print(f"Warning: OCR result for page {i} is not a dictionary. Skipping text layer.") continue # Place invisible text at exact coordinates for word_data in ocr_data.get('words', []): word_text = word_data.get('text') bbox = word_data.get('bbox') # [x0, y0, x1, y1] if word_text and bbox and len(bbox) == 4: # PyMuPDF uses (x0, y0, x1, y1) for rects text_rect = fitz.Rect(bbox[0], bbox[1], bbox[2], bbox[3]) # A simple approach to estimate font size based on bbox height font_size = max(1, bbox[3] - bbox[1]) # Insert text, making it invisible (render_mode=3) # You might need to adjust text_rect coordinates or font_size for better alignment page.insert_textbox(text_rect, word_text, fontsize=font_size, render_mode=3, # Invisible text fill=None) # No fill color needed for invisible text doc.save(output_path) doc.close() return output_path