---
name: pdf-processing
description: Processes, extracts and verifies existing PDF files. Use whenever the user wants to read text or tables from a PDF, OCR a scanned PDF, merge, split, rotate or compress PDFs, inspect metadata, or check that extracted data matches the source, even if they only say "get the data out of this PDF". Every pipeline ends with a verification step. For creating new styled PDFs use pdf-atelier instead.
---

# pdf-processing

A repeatable workflow for reading, transforming and verifying existing PDF files. Extraction is never trusted blindly: every pipeline ends with a verification step.

## When to use

- Extract text or tables from digital PDFs
- OCR scanned PDFs
- Merge, split, rotate or compress PDFs
- Validate that extracted data matches the source document
- Inspect metadata, fonts and structure

To create a new styled PDF, use `pdf-atelier`. To process or verify an existing one, use this skill.

## Tool selection

| Task | Tool |
|------|------|
| Fast text extraction | `pdftotext` (poppler-utils) |
| Tables and layout | `pdfplumber` |
| Merge, split, rotate | `qpdf`, `pypdf` |
| OCR | `ocrmypdf`, `tesseract` |
| Integrity check | `qpdf --check` |

## Workflow

1. **Inspect**: page count, encryption, text layer present or not
2. **Choose strategy**: text layer exists means direct extraction, otherwise OCR
3. **Extract**: keep the page number with every extracted chunk
4. **Normalize**: fix hyphenation, whitespace, encoding and RTL ordering
5. **Verify**: compare against the source (see checklist below)
6. **Report**: return the data with a confidence note and known gaps

## Example commands

```bash
# Inspect
pdfinfo input.pdf
qpdf --check input.pdf

# Text extraction with layout preserved
pdftotext -layout input.pdf output.txt

# Detect a missing text layer (near-empty output means scanned)
pdftotext input.pdf - | wc -w

# OCR a scanned PDF (English + Persian)
ocrmypdf -l eng+fas --deskew input.pdf output_ocr.pdf

# Merge and split
qpdf --empty --pages a.pdf b.pdf -- merged.pdf
qpdf input.pdf --pages . 1-5 -- first5.pdf
```

```python
import pdfplumber

with pdfplumber.open("input.pdf") as pdf:
    for i, page in enumerate(pdf.pages, start=1):
        for table in page.extract_tables():
            print(f"page {i}:", table)
```

## Verification checklist

- Output page count equals source page count
- Word count per page is in a sane range (no accidentally empty pages)
- Numbers, dates and totals are cross-checked by a second method (for example `pdftotext` vs `pdfplumber`)
- Sample 3 random pages and compare against the rendered image
- Table row and column counts match the source
- Record a SHA-256 checksum of input and output files

```bash
sha256sum input.pdf output.pdf
pdftoppm -r 80 -f 3 -l 3 -png input.pdf check_page
```

## Common pitfalls

- Scanned PDFs return empty text, so run OCR first
- Multi-column layouts scramble reading order, so use `-layout` or crop regions
- RTL text (Persian, Arabic, Hebrew) may extract reversed, so verify against the render
- Ligatures and hyphenation break search, so normalize before indexing

## Related skills

- `pdf-atelier`: creates styled PDFs; this skill processes and verifies existing ones
- `systematic-debugging`: apply when an extraction pipeline gives wrong output
