"""Text extraction from PDF (poppler pdftotext).""" import os import subprocess FFFD = "\ufffd" def extract(pdf_path: str, out_path: str) -> dict: """Run pdftotext and report basic text-layer statistics. Returns a dict with path, chars, words, fffd_count, and a short quality verdict (useful for deciding how much cleanup a book needs). """ if not os.path.exists(pdf_path): raise FileNotFoundError(pdf_path) if not os.path.exists(out_path): subprocess.run( ["pdftotext", pdf_path, out_path], check=True, capture_output=True, text=True, ) text = open(out_path, encoding="utf-8").read() words = len(text.split()) fffd = text.count(FFFD) if words == 0: verdict = "NO TEXT LAYER: the PDF appears to be a pure image scan. " \ "OCR it first (e.g. ocrmypdf) or the pipeline has nothing to read." elif fffd > words * 0.01: verdict = "HEAVY OCR DAMAGE: many unreadable glyphs (U+FFFD). " \ "Run `scan` and build a fix config before synthesizing." elif fffd > 0: verdict = "MINOR OCR DAMAGE: some unreadable glyphs. " \ "Run `scan` to see them; a fix config is recommended." else: verdict = "CLEAN TEXT LAYER: no replacement characters found." return { "path": out_path, "chars": len(text), "words": words, "fffd": fffd, "verdict": verdict, }