---
name: pdf
description: Read, generate, and manipulate PDF files. Use to extract text/tables from a PDF, merge/split/rotate pages, read or fill form fields, or produce a polished PDF (from HTML/CSS or programmatically). Uses open-source libraries (pdfplumber, pypdf, reportlab, weasyprint). Not for editing Word/Excel/PowerPoint sources.
---

# PDF files

Pick the open-source library that fits the task; run through Bash on absolute paths. Install on demand (only what you need).

## Preflight — Python + the library (install on demand)
1. **Find Python.** `python3 --version`, else `python --version`. Use whichever works (`PY`).
2. **If neither exists, guide the user once** (in their language) — this needs a one-time Python install; then wait, don't fabricate:
   - macOS `brew install python` · Windows `winget install -e --id Python.Python.3.12` (or python.org, add to PATH) · Linux `sudo apt install -y python3 python3-pip`.
3. **Ensure only the library this task needs** (e.g. extraction → pdfplumber):
   ```bash
   PY -c "import pdfplumber" 2>/dev/null || PY -m pip install --quiet --user pdfplumber
   ```
   Do the same for `pypdf`, `reportlab`, or `weasyprint` as needed. If pip is offline/blocked, say so and stop.

## Read: extract text & tables — `pdfplumber`
```python
import pdfplumber
with pdfplumber.open("/abs/in.pdf") as pdf:
    for page in pdf.pages:
        text = page.extract_text() or ""
        for table in page.extract_tables():   # list of rows (lists of cells)
            ...
```
`pypdf` is fine for simple text + metadata; `pdfplumber` is better for layout/tables. **Scanned PDFs have no text layer** — detect empty `extract_text()` and, if OCR is available, run `ocrmypdf in.pdf out.pdf` (needs Tesseract) first, else tell the user it's an image-only PDF.

## Manipulate pages — `pypdf`
```python
from pypdf import PdfReader, PdfWriter
r = PdfReader("/abs/in.pdf"); w = PdfWriter()
for i in (0, 2, 3):            # keep/split specific pages
    w.add_page(r.pages[i])
# merge another file: for p in PdfReader("/abs/b.pdf").pages: w.add_page(p)
w.pages[0].rotate(90)
with open("/abs/out.pdf", "wb") as f:
    w.write(f)
```

## Forms — read & fill (AcroForm) — `pypdf`
```python
from pypdf import PdfReader, PdfWriter
r = PdfReader("/abs/form.pdf")
print(r.get_fields().keys())                 # discover field names
w = PdfWriter(clone_from=r)
w.update_page_form_field_values(w.pages[0], {"full_name": "Jane Doe", "agree": "/Yes"})
with open("/abs/filled.pdf", "wb") as f:
    w.write(f)
```
Checkboxes take their on-state name (often `/Yes` or `/On` — read it from the field). Set `NeedAppearances` if a viewer shows blanks.

## Generate a PDF
- **From formatted content → `weasyprint`** (write HTML/CSS, best for styled reports/letters):
  ```python
  from weasyprint import HTML
  HTML(string="<h1>Report</h1><p>…</p>").write_pdf("/abs/out.pdf")
  ```
- **Programmatic layout → `reportlab`** (precise positioning, invoices, labels).
- **From an Office source** you already made: convert with LibreOffice
  `soffice --headless --convert-to pdf --outdir /abs/dir /abs/src.docx`.

## Conventions
- Save to an absolute path and report it.
- When extracting, show the real extracted content — never fabricate text from an image-only PDF; say when OCR is required.
