Spaces:
Sleeping
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)
pip install -r requirements.txt
System packages also needed:
Tool Purpose macOS Linux (apt) Tesseract OCR engine brew install tesseractapt install tesseract-ocrPoppler PDF β image brew install popplerapt install poppler-utils
2 Β· Java (β₯ 11) + Maven (β₯ 3.6)
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):
# .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.
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
# 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
# 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)
python -m generate_metadata.main \
--pdf data/input.pdf \
--output output/
Part 2 only (tag an existing elements.txt)
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)
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) |