""" PDF-family extractor. Text per page via pypdf, plus the document info dict (title/author/dates). When options['render'] is set and pdf2image (+ system poppler) is available, page 1 is rendered to thumb.jpg. """ import io from pathlib import Path from .base import ExtractionResult EXTRACTOR_ID = "pdf/pypdf@1" def _info_metadata(reader) -> dict: out = {} info = getattr(reader, "metadata", None) if not info: return out for src, dst in (("title", "title"), ("author", "author"), ("creation_date", "created"), ("modification_date", "modified_doc")): try: val = getattr(info, src, None) except Exception: val = None if val: out[dst] = str(val) return out def _render_first_page(source: Path) -> bytes: from pdf2image import convert_from_path # needs system poppler images = convert_from_path(str(source), first_page=1, last_page=1, dpi=100) if not images: return b"" im = images[0] im.thumbnail((640, 640)) buf = io.BytesIO() im.convert("RGB").save(buf, format="JPEG", quality=80) return buf.getvalue() def extract(source: Path, options: dict) -> ExtractionResult: from pypdf import PdfReader # lazy warnings = [] reader = PdfReader(str(source)) pages = reader.pages blocks = [] for i, page in enumerate(pages, 1): try: text = page.extract_text() or "" except Exception as e: text = "" warnings.append(f"page {i} text extraction failed: {e}") blocks.append(f"## Page {i}\n\n{text.strip()}") content = "\n\n".join(blocks) + "\n" metadata = _info_metadata(reader) metadata["pages"] = len(pages) thumb = None if options.get("render"): try: thumb = _render_first_page(source) or None except ImportError: warnings.append("--render requested but pdf2image/poppler not available") except Exception as e: warnings.append(f"page render failed: {e}") return ExtractionResult(content=content, metadata=metadata, thumb=thumb, warnings=warnings, extractor=EXTRACTOR_ID)