# ------------------- OCR (optional) ------------------- # def run_ocr_if_needed(pdf_path: Path, out_dir: Path, force: bool = False): """ If the PDF appears to have no extractable text (e.g. scanned), run OCR. Uses ocrmypdf which adds a text layer while preserving the original appearance. """ try: import ocrmypdf except ImportError: print("⚠️ ocrmypdf not installed – OCR step skipped.") return
import argparse import json import os import re import sys from pathlib import Path from typing import List, Dict
def clean_filename(s: str) -> str: """Make a filesystem‑safe name.""" return re.sub(r"[^\w\-_. ]", "_", s) agnibina filetype.pdf
img_counter = 0 for page_num in tqdm(range(len(doc)), desc="Pages (images)"): page = doc[page_num] img_list = page.get_images(full=True) for img_index, img in enumerate(img_list, start=1): xref = img[0] base_image = doc.extract_image(xref) img_bytes = base_image["image"] img_ext = base_image["ext"] img_name = f"pagepage_num+1:03d_imgimg_index:03d.img_ext" (img_dir / img_name).write_bytes(img_bytes) img_counter += 1 doc.close() print(f"✅ Extracted img_counter images to img_dir")
Requirements (install via pip): pip install pdfplumber pymupdf tqdm tabula-py ocrmypdf # tabula-py needs Java; ocrmypdf needs Tesseract + poppler Dict def clean_filename(s: str) ->
import pdfplumber import fitz # pymupdf from tqdm import tqdm
# ------------------- Text + Layout ------------------- # def extract_text_and_layout(pdf_path: Path, out_dir: Path) -> List[Dict]: """ Returns a list (one dict per page) with: - page_number - plain_text - list of text elements text, x0, y0, x1, y1, fontname, size """ pages_info = [] with pdfplumber.open(str(pdf_path)) as pdf: for page_num, page in enumerate(tqdm(pdf.pages, desc="Pages (text/layout)")): plain = page.extract_text() # layout objects (characters) – useful for heading detection chars = page.chars # each char already has x0, y0, x1, y1, fontname, size # Group chars into words/lines if you like, but we keep raw for flexibility pages_info.append( "page_number": page_num + 1, "text": plain, "characters": chars, ) # Save raw JSON for later inspection (out_dir / "text_layout.json").write_text(json.dumps(pages_info, indent=2, ensure_ascii=False)) return pages_info img in enumerate(img_list
ocr_output = out_dir / "ocr_layered.pdf" print("🖼️ Running OCR (this may take a while)…") ocrmypdf.ocr(str(pdf_path), str(ocr_output), force_ocr=True, deskew=True, language="eng") print(f"🆗 OCR complete → ocr_output")
# ------------------- Metadata ------------------- # def extract_metadata(pdf_path: Path) -> Dict: """Return a dict with PDF metadata (title, author, dates, etc.).""" doc = fitz.open(str(pdf_path)) meta = doc.metadata # Normalize keys normalized = "title": meta.get("title"), "author": meta.get("author"), "creator": meta.get("creator"), "producer": meta.get("producer"), "subject": meta.get("subject"), "keywords": meta.get("keywords"), "creationDate": meta.get("creationDate"), "modDate": meta.get("modDate"), "pdf_version": doc.pdf_version, "page_count": doc.page_count, doc.close() return normalized
count = 0 for i in range(doc.embfile_count()): info = doc.embfile_info(i) fname = clean_filename(info["filename"]) data = doc.embfile_get(i) (att_dir / fname).write_bytes(data) count += 1 doc.close() print(f"📦 Extracted count embedded file(s).")
#!/usr/bin/env python3 # -*- coding: utf-8 -*-