--- name: processing-pdf description: Reads, creates, and modifies PDF files — extracts text and tables, builds new PDFs, merges, splits, rotates, watermarks, encrypts/decrypts, and fills PDF forms. Use when the user asks to read, extract, parse, create, generate, merge, combine, split, rotate, watermark, password-protect, decrypt, or fill a PDF, mentions a .pdf file, or asks to pull text or tables out of a PDF. Do not use for Word documents, images, or converting Office files to PDF (use the corresponding Office skill and export). --- # Processing PDF ## When to use / when NOT to use - **Use for:** any operation where a `.pdf` file is the input or the output — extraction, creation, page manipulation, forms, encryption. - **Do NOT use for:** Word documents (`.docx`), images, or converting Office files to PDF — use the corresponding Office skill and export instead. ## Quick reference — one default per operation **Extract text/tables — pdfplumber:** ```python import pdfplumber with pdfplumber.open("in.pdf") as pdf: text = "\n".join(p.extract_text() or "" for p in pdf.pages) tables = pdf.pages[0].extract_tables() ``` Escape hatch: if output is garbled or misordered, use `pdftotext -layout in.pdf out.txt` (poppler-utils). If pages yield no text at all, the PDF is scanned — OCR it with pytesseract + pdf2image (see recipes). **Page operations (merge/split/rotate/encrypt) and form filling — pypdf:** ```python from pypdf import PdfReader, PdfWriter writer = PdfWriter() for path in ["a.pdf", "b.pdf"]: writer.append(path) # merge writer.write("merged.pdf") ``` **Create new PDFs — reportlab (Platypus):** ```python from reportlab.lib.pagesizes import letter from reportlab.platypus import SimpleDocTemplate, Paragraph from reportlab.lib.styles import getSampleStyleSheet styles = getSampleStyleSheet() SimpleDocTemplate("out.pdf", pagesize=letter).build( [Paragraph("Title", styles["Title"]), Paragraph("Body text.", styles["Normal"])]) ``` ## Workflow 1. Identify the operation (extract / create / modify / form-fill) and pick the default tool above. 2. Run the operation with the minimal code needed; write output next to the input unless the user names a path. 3. **Validate:** re-open the output with `PdfReader("out.pdf")` and check `len(reader.pages)` matches expectations; for extraction, confirm the text/tables are non-empty before reporting success. 4. If validation fails, fix and repeat step 2 — never deliver an unverified file. 5. Report the output path and a one-line summary (page count, or rows/chars extracted). ## Edge cases & failure modes - **Missing dependency** → install exactly: `pip install pdfplumber pypdf reportlab`. - **Corrupt/malformed PDF** → report the parser's error message verbatim (it names the object/offset); do not guess at contents. - **Encrypted PDF** → `PdfReader(path)` raises or `reader.is_encrypted` is True; call `reader.decrypt(password)` — ask the user for the password, never brute-force. - **Scanned PDF (no text layer)** → `extract_text()` returns None/empty; switch to OCR (recipes) and tell the user accuracy depends on scan quality. - **Huge PDF** → process page-by-page (pdfplumber pages are lazy); never load all text into one string above ~1,000 pages. - **Empty file (0 bytes)** → report "file is empty, not a valid PDF" and stop. ## References Deeper copy-paste recipes (watermark, split, encrypt, forms, OCR, conversion): see [references/recipes.md](references/recipes.md).