#!/usr/bin/env python3 """ pipeline.py — Full PDF Accessibility Pipeline ============================================== Runs both parts end-to-end: Part 1 · generate_metadata (Python) PDF → YOLOv11 + Tesseract + GPT-4o → output/elements.txt Part 2 · embed_metadata (Java / iText) input.pdf + elements.txt → output/tagged_output.pdf (PDF/UA-1) Usage ----- python pipeline.py # uses data/input.pdf python pipeline.py --pdf path/to/file.pdf python pipeline.py --pdf file.pdf --latex path/to/latex-project/ python pipeline.py --skip-metadata # only run Java tagger python pipeline.py --skip-tagging # only run Python analysis """ import argparse import os import subprocess import sys ROOT = os.path.dirname(os.path.abspath(__file__)) DATA_DIR = os.path.join(ROOT, "data") OUTPUT_DIR = os.path.join(ROOT, "output") JAR_PATH = os.path.join(ROOT, "pdftagger.jar") def run(cmd: list, **kwargs): print(f"\n>>> {' '.join(str(c) for c in cmd)}") result = subprocess.run(cmd, **kwargs) if result.returncode != 0: sys.exit(result.returncode) return result def build_jar(force: bool = False): if os.path.exists(JAR_PATH) and not force: print(f"[pipeline] JAR already built: {JAR_PATH}") return print("[pipeline] Building Java tagger JAR (maven package)…") run(["mvn", "-f", os.path.join(ROOT, "pom.xml"), "package", "-q", "-DskipTests"]) if not os.path.exists(JAR_PATH): print("[pipeline] ERROR: pdftagger.jar not found after build.", file=sys.stderr) sys.exit(1) print(f"[pipeline] JAR built: {JAR_PATH}") def main(): parser = argparse.ArgumentParser( description="PDF Accessibility Pipeline — analyse then tag.", formatter_class=argparse.RawDescriptionHelpFormatter, ) parser.add_argument( "--pdf", default=os.path.join(DATA_DIR, "input.pdf"), help="Input PDF (default: data/input.pdf)", ) parser.add_argument( "--latex", default=DATA_DIR, metavar="DIR", help="Directory containing the LaTeX source subfolder (default: data/). " "Place your .tex project as a subfolder inside data/ and this is detected automatically.", ) parser.add_argument( "--output", default=OUTPUT_DIR, help="Output directory (default: output/)", ) parser.add_argument( "--pages", type=int, default=-1, metavar="N", help="Limit to first N pages (-1 = all)", ) parser.add_argument( "--skip-metadata", action="store_true", help="Skip Part 1 (use existing output/elements.txt)", ) parser.add_argument( "--skip-tagging", action="store_true", help="Skip Part 2 (Java tagger)", ) parser.add_argument( "--rebuild-jar", action="store_true", help="Force rebuild of pdftagger.jar before tagging", ) parser.add_argument( "--no-preflight", action="store_true", help="Skip prerequisite checks (for CI/CD environments).", ) args = parser.parse_args() from preflight import run_preflight_checks run_preflight_checks( skip=args.no_preflight, need_python_part=not args.skip_metadata, need_java_part=not args.skip_tagging, ) pdf_path = os.path.abspath(args.pdf) output_dir = os.path.abspath(args.output) elements_out = os.path.join(output_dir, "elements.txt") tagged_out = os.path.join(output_dir, "tagged_output.pdf") os.makedirs(output_dir, exist_ok=True) os.makedirs(os.path.join(output_dir, "visualizations"), exist_ok=True) if not os.path.exists(pdf_path): print(f"[pipeline] ERROR: PDF not found: {pdf_path}", file=sys.stderr) sys.exit(1) # ---------------------------------------------------------------- # Part 1 — generate_metadata # ---------------------------------------------------------------- if not args.skip_metadata: print("\n[pipeline] ═══ Part 1: Generate Metadata ═══") cmd = [ sys.executable, "-m", "generate_metadata.main", "--pdf", pdf_path, "--output", output_dir, "--pages", str(args.pages), ] cmd += ["--latex", os.path.abspath(args.latex)] run(cmd, cwd=ROOT) if not os.path.exists(elements_out): print(f"[pipeline] ERROR: elements.txt not produced at {elements_out}", file=sys.stderr) sys.exit(1) else: print(f"[pipeline] Skipping Part 1 — using: {elements_out}") if not os.path.exists(elements_out): print( "[pipeline] ERROR: elements.txt not found. Run without --skip-metadata first.", file=sys.stderr, ) sys.exit(1) # ---------------------------------------------------------------- # Part 2 — embed_metadata (Java tagger) # ---------------------------------------------------------------- if not args.skip_tagging: print("\n[pipeline] ═══ Part 2: Embed Metadata ═══") build_jar(force=args.rebuild_jar) run(["java", "-jar", JAR_PATH, pdf_path, elements_out, tagged_out]) print(f"\n[pipeline] Tagged PDF: {tagged_out}") else: print("[pipeline] Skipping Part 2 (Java tagger).") print("\n[pipeline] Done.") if __name__ == "__main__": main()