File size: 8,286 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
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
# PDF Accessibility Pipeline

Automatically analyses a PDF document and produces a fully tagged, screen-reader-accessible PDF conforming to the **PDF/UA-1** standard.

---

## How it works

```
data/input.pdf
      β”‚
      β–Ό  Part 1 β€” generate_metadata  (Python)
      β”‚
      β”œβ”€ YOLOv11 layout detection  ──► segmented regions
      β”œβ”€ Tesseract OCR             ──► extracted text
      β”œβ”€ LaTeX chunking            ──► document order + reading order
      └─ GPT-4o labeling + alt-text
      β”‚
      β–Ό
output/elements.txt
      β”‚
      β–Ό  Part 2 β€” embed_metadata  (Java / iText 9)
      β”‚
      └─ PDF structure tree (tags) ──► output/tagged_output.pdf  (PDF/UA-1)
```

---

## Prerequisites

### 1 Β· Python (β‰₯ 3.9)

```bash
pip install -r requirements.txt
```

> **System packages also needed:**
> | Tool | Purpose | macOS | Linux (apt) |
> |------|---------|-------|-------------|
> | **Tesseract** | OCR engine | `brew install tesseract` | `apt install tesseract-ocr` |
> | **Poppler** | PDF β†’ image | `brew install poppler` | `apt install poppler-utils` |

### 2 Β· Java (β‰₯ 11) + Maven (β‰₯ 3.6)

```bash
brew install openjdk@11 maven   # macOS
# or: apt install openjdk-11-jdk maven  (Linux)
```

### 3 Β· YOLOv11 model weights

Download the pre-trained document-layout model:

```
https://github.com/moured/YOLOv11-Document-Layout-Analysis/releases
   β†’ yolov11x_best.pt
```

Save it anywhere and set the path (see Configuration below).

### 4 Β· OpenAI API key

Required for LaTeX chunk mapping and figure alt-text generation.

---

## Configuration

Create a `.env` file in the project root (or export as environment variables):

```dotenv
# .env β€” copy this block and fill in your values

# REQUIRED: OpenAI API key
OPENAI_API_KEY=sk-...

# REQUIRED: Path to downloaded YOLOv11 weights
YOLO_MODEL_PATH=/absolute/path/to/yolov11x_best.pt
```

All other settings can be tweaked in [`generate_metadata/config.py`](generate_metadata/config.py).

---

## Input files

Place both inputs in the `data/` directory before running:

| What | Where to put it |
|------|----------------|
| Input PDF | `data/input.pdf` |
| LaTeX source | any subfolder inside `data/` e.g. `data/my-paper/` |

The pipeline auto-detects the LaTeX subfolder inside `data/`.
Everything else (`elements.txt`, tagged PDF, visualizations) is generated by the pipeline into `output/`.

---

## Quick start

```bash
# 1. Clone and enter the repo
git clone <repo-url>
cd pdf-script

# 2. Configure
cp .env.example .env        # then fill in OPENAI_API_KEY and YOLO_MODEL_PATH
# (or just export them in your shell)

# 3. Install Python dependencies
pip install -r requirements.txt

# 4. Build the Java tagger
mvn package -DskipTests     # creates pdftagger.jar

# 5. Place your inputs in data/
cp your-paper.pdf data/input.pdf
cp -r your-latex-project/ data/my-paper/   # any subfolder name works

# 6. Run the full pipeline
python pipeline.py
```

Outputs land in `output/`:

```
output/
β”œβ”€β”€ elements.txt            ← structured metadata (roles + bboxes)
β”œβ”€β”€ tagged_output.pdf       ← accessible PDF/UA-1 output
β”œβ”€β”€ latex_chunks.txt        ← debug: LaTeX semantic chunks
β”œβ”€β”€ segments2chunks_gpt.txt ← debug: OCR β†’ chunk mapping log
β”œβ”€β”€ pdf-imgs/               ← per-page PNG images
β”œβ”€β”€ segmentations-1/        ← YOLO detection visualisations
β”œβ”€β”€ segmentations-2/        ← refined segment visualisations
└── visualizations/
    β”œβ”€β”€ labels.pdf          ← annotated pages with element labels
    └── reading_order.pdf   ← annotated pages with reading-order numbers
```

---

## Usage reference

### Full pipeline
```bash
# Place data/input.pdf and data/<your-latex-folder>/ first, then:
python pipeline.py                              # auto-detects everything in data/
python pipeline.py --pdf path/to/paper.pdf     # custom PDF location
python pipeline.py --latex path/to/latex-dir/  # custom LaTeX parent directory
python pipeline.py --pages 3                   # first 3 pages only
```

### Part 1 only (analysis β†’ elements.txt)
```bash
python -m generate_metadata.main \
    --pdf  data/input.pdf \
    --output output/
```

### Part 2 only (tag an existing elements.txt)
```bash
python pipeline.py --skip-metadata

# or call the Java tagger directly:
java -jar pdftagger.jar data/input.pdf output/elements.txt output/tagged_output.pdf
```

### Rebuild JAR (after changing Java code)
```bash
python pipeline.py --rebuild-jar
# or:
mvn package -DskipTests
```

### CLI reference
```
python pipeline.py --help

  --pdf PATH          Input PDF (default: data/input.pdf)
  --latex DIR         Directory containing the LaTeX source subfolder (default: data/)
  --output DIR        Output directory (default: output/)
  --pages N           Process first N pages only (-1 = all)
  --skip-metadata     Skip Part 1, reuse existing output/elements.txt
  --skip-tagging      Skip Part 2 (Java tagger)
  --rebuild-jar       Force rebuild of pdftagger.jar
```

---

## Project structure

```
pdf-script/
β”‚
β”œβ”€β”€ pipeline.py                     ← Main entry point (runs both parts)
β”œβ”€β”€ requirements.txt                ← Python dependencies
β”œβ”€β”€ pom.xml                         ← Maven build (embed_metadata JAR)
β”œβ”€β”€ .env                            ← Your secrets (not committed)
β”‚
β”œβ”€β”€ data/
β”‚   └── input.pdf                   ← Place your PDF here
β”‚
β”œβ”€β”€ output/                         ← All generated files (gitignored)
β”‚
β”œβ”€β”€ generate_metadata/              ← Part 1: Python analysis pipeline
β”‚   β”œβ”€β”€ main.py                     ← Entry point for Part 1
β”‚   β”œβ”€β”€ config.py                   ← API keys, model paths, settings
β”‚   β”œβ”€β”€ pdf_loader.py               ── PDF β†’ page images (pdf2image)
β”‚   β”œβ”€β”€ segmentation.py             ── YOLOv11 layout detection + bbox utils
β”‚   β”œβ”€β”€ ocr.py                      ── Tesseract OCR
β”‚   β”œβ”€β”€ latex_processor.py          ── LaTeX flattening + semantic chunking
β”‚   β”œβ”€β”€ mapping.py                  ── OCR ↔ YOLO mapping + refinement
β”‚   β”œβ”€β”€ chunk_mapper.py             ── Segment β†’ LaTeX chunk matching (GPT-4o)
β”‚   β”œβ”€β”€ refinement.py               ── Semantic split / merge / clean
β”‚   β”œβ”€β”€ labeling.py                 ── Accessibility role labeling
β”‚   β”œβ”€β”€ reading_order.py            ── Reading order determination
β”‚   β”œβ”€β”€ alt_text.py                 ── Figure alt-text (\\Description or GPT-4o)
β”‚   β”œβ”€β”€ sanity_check.py             ── GPT-4o label + reading-order QA pass
β”‚   └── output.py                   ── Write elements.txt + visualisation PDFs
β”‚
└── embed_metadata/                 ← Part 2: Java PDF tagger (iText 9 / PDF/UA-1)
    └── src/main/java/com/pdftag/
        β”œβ”€β”€ itagpdf.java            ← CLI entry point
        └── TagPDF.java             ← Core tagging logic
```

---

## elements.txt format

Pipe-delimited, with leading `|` characters indicating nesting depth:

```
Sect|1|100|50|800|900|
|H1|1|120|60|780|120|Introduction
|P|1|120|130|780|200|This paper presents…
|FIGURE|1|120|210|500|450|Chart showing accuracy over time
|TABLE|1|120|460|780|650|
||TR|1|125|465|775|495|
|||TH|1|125|465|300|495|Method
|||TH|1|305|465|500|495|Accuracy
```

Columns: `[depth-pipes] role | page | x1 | y1 | x2 | y2 | text`

**Supported roles:** `H1` `H2` `H3` `P` `Sect` `FIGURE` `CAPTION` `TABLE` `TR` `TH` `TD` `L` `LI` `AUTHOR` `BIBENTRY` `FORMULA` `NOTE` `ARTIFACT`

---

## Troubleshooting

| Error | Fix |
|-------|-----|
| `OPENAI_API_KEY is not set` | Add key to `.env` or export in shell |
| `YOLO_MODEL_PATH` not found | Download weights and update `.env` |
| `tesseract: command not found` | `brew install tesseract` / `apt install tesseract-ocr` |
| `pdftoppm: command not found` | `brew install poppler` / `apt install poppler-utils` |
| `mvn: command not found` | `brew install maven` / `apt install maven` |
| JAR build fails | Check `java -version` β€” needs Java 11+. On macOS: `export JAVA_HOME=$(/usr/libexec/java_home -v 11)` |