File size: 5,553 Bytes
c6c9042
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
#!/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()