""" PDF Utilities -- AI Reel Creator Platform ========================================= Helpers for converting PDFs to images (for Gemini multimodal parsing) and extracting embedded images. Primary backend: pdf2image (poppler required) Fallback backend: PyMuPDF (pure Python, no external deps) """ import os import io from pathlib import Path from typing import List, Tuple, Optional from PIL import Image def _try_pdf2image( pdf_path: str, dpi: int = 200, first_page: int = 1, last_page: Optional[int] = None, ) -> Optional[Tuple[List[Image.Image], List[int]]]: """Try rendering PDF pages with pdf2image. Returns None on failure.""" try: from pdf2image import convert_from_path images = convert_from_path(pdf_path, dpi=dpi, first_page=first_page, last_page=last_page) page_numbers = list(range(first_page, first_page + len(images))) return images, page_numbers except Exception: return None def _try_pymupdf( pdf_path: str, dpi: int = 200, first_page: int = 1, last_page: Optional[int] = None, ) -> Tuple[List[Image.Image], List[int]]: """Render PDF pages with PyMuPDF (fitz). Raises on failure.""" import fitz doc = fitz.open(pdf_path) images = [] page_numbers = [] end = last_page or len(doc) start_idx = first_page - 1 for i in range(start_idx, end): page = doc.load_page(i) mat = fitz.Matrix(dpi / 72, dpi / 72) pix = page.get_pixmap(matrix=mat) img = Image.frombytes("RGB", [pix.width, pix.height], pix.samples) images.append(img) page_numbers.append(i + 1) doc.close() return images, page_numbers def pdf_pages_to_images( pdf_path: str, dpi: int = 200, first_page: int = 1, last_page: Optional[int] = None, ) -> Tuple[List[Image.Image], List[int]]: """Convert PDF pages to PIL Images. Tries pdf2image first, falls back to PyMuPDF.""" pdf_path = str(Path(pdf_path).resolve()) result = _try_pdf2image(pdf_path, dpi, first_page, last_page) if result is not None: return result try: return _try_pymupdf(pdf_path, dpi, first_page, last_page) except ImportError as exc: raise RuntimeError("No PDF-to-image library available. Install one of: pdf2image (+ poppler), PyMuPDF.") from exc def extract_pdf_embedded_images(pdf_path: str) -> List[Image.Image]: """Extract all embedded raster images from a PDF.""" try: import fitz except ImportError as exc: raise RuntimeError("PyMuPDF is required for embedded-image extraction.") from exc doc = fitz.open(pdf_path) images = [] for page in doc: for img_index, img in enumerate(page.get_images(full=True)): xref = img[0] base_image = doc.extract_image(xref) image_bytes = base_image["image"] try: pil_img = Image.open(io.BytesIO(image_bytes)) if pil_img.mode != "RGB": pil_img = pil_img.convert("RGB") images.append(pil_img) except Exception: continue doc.close() return images