Create, merge, split, fill, and secure PDF files.
Scanned 9/10/2026
Install to Claude Code
npx -y skills add LoopyLuci/Skills --skill pdf --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Pdf?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/loopyluci-pdf)More formats (shields.io, HTML) on the badges page.
---
name: pdf
description: "Create, merge, split, fill, and secure PDF files."
version: 1.0.0
author: Anthropic (adapted by Nous Research)
license: Proprietary. LICENSE.txt has complete terms
platforms: [linux, macos, windows]
metadata:
hermes:
tags: [PDF, Documents, Forms, Office, Productivity]
category: productivity
related_skills: [ocr-and-documents, nano-pdf, docx, xlsx]
---
# PDF Skill
Create, combine, split, transform, and secure PDF files — merging, page manipulation, form filling, watermarks, encryption, and text/table extraction. For heavy text extraction from scanned documents prefer the `ocr-and-documents` skill; for natural-language edits to existing PDF text prefer `nano-pdf`.
## When to Use
Use this skill whenever the user wants to do anything with PDF files: reading or extracting text/tables, combining or merging multiple PDFs, splitting PDFs apart, rotating pages, adding watermarks, creating new PDFs, filling PDF forms, encrypting/decrypting, extracting images, or OCR on scanned PDFs. If the user mentions a .pdf file or asks to produce one, use this skill.
## Prerequisites
```bash
pip install pypdf pdfplumber reportlab
which pdftotext || sudo apt install -y poppler-utils # pdftotext, pdftoppm, pdfimages
which qpdf || sudo apt install -y qpdf # CLI merge/split/decrypt
```
macOS: `brew install poppler qpdf`. OCR extras: `pip install pytesseract pdf2image` + `sudo apt install -y tesseract-ocr`.
> Script paths below are relative to this skill's directory. Form filling has its own workflow — read [forms.md](forms.md) and follow it. Advanced library usage (pypdfium2, pdf-lib) and troubleshooting: [reference.md](reference.md).
## Quick Reference
| Task | Best Tool | Command/Code |
|------|-----------|--------------|
| Merge PDFs | pypdf | `writer.add_page(page)` per page |
| Split PDFs | pypdf | One page per file |
| Extract text | pdfplumber | `page.extract_text()` |
| Extract tables | pdfplumber | `page.extract_tables()` |
| Create PDFs | reportlab | Canvas or Platypus |
| Command-line merge/split | qpdf | `qpdf --empty --pages ...` |
| OCR scanned PDFs | pytesseract | Convert to images first (or use `ocr-and-documents`) |
| Fill PDF forms | see [forms.md](forms.md) | `scripts/fill_fillable_fields.py` etc. |
| Edit existing text | `nano-pdf` skill | `nano-pdf edit file.pdf <page> "<instruction>"` |
## Common operations
### Merge / split / rotate (pypdf)
```python
from pypdf import PdfReader, PdfWriter
# Merge
writer = PdfWriter()
for pdf_file in ["doc1.pdf", "doc2.pdf"]:
for page in PdfReader(pdf_file).pages:
writer.add_page(page)
with open("merged.pdf", "wb") as f:
writer.write(f)
# Split: one file per page
reader = PdfReader("input.pdf")
for i, page in enumerate(reader.pages):
w = PdfWriter(); w.add_page(page)
with open(f"page_{i+1}.pdf", "wb") as f:
w.write(f)
# Rotate
page = reader.pages[0]
page.rotate(90) # clockwise
```
### Extract text and tables (pdfplumber)
```python
import pdfplumber, pandas as pd
with pdfplumber.open("document.pdf") as pdf:
text = "\n".join(page.extract_text() or "" for page in pdf.pages)
tables = [pd.DataFrame(t[1:], columns=t[0])
for page in pdf.pages
for t in page.extract_tables() if t]
```
### Create PDFs (reportlab)
```python
from reportlab.lib.pagesizes import letter
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, PageBreak
from reportlab.lib.styles import getSampleStyleSheet
doc = SimpleDocTemplate("report.pdf", pagesize=letter)
styles = getSampleStyleSheet()
story = [Paragraph("Report Title", styles["Title"]), Spacer(1, 12),
Paragraph("Body text...", styles["Normal"]), PageBreak(),
Paragraph("Page 2", styles["Heading1"])]
doc.build(story)
```
**Subscripts/superscripts:** never use Unicode sub/superscript characters (₀₁₂, ⁰¹²) — the built-in fonts lack the glyphs and render solid black boxes. Use `<sub>`/`<super>` markup inside `Paragraph` objects: `Paragraph("H<sub>2</sub>O", styles['Normal'])`. For canvas-drawn text, adjust font size and position manually.
### Command-line tools
```bash
pdftotext -layout input.pdf output.txt # text, layout preserved
pdftotext -f 1 -l 5 input.pdf output.txt # pages 1-5
qpdf --empty --pages file1.pdf file2.pdf -- merged.pdf # merge
qpdf input.pdf --pages . 1-5 -- pages1-5.pdf # split range
qpdf input.pdf output.pdf --rotate=+90:1 # rotate page 1
qpdf --password=pw --decrypt encrypted.pdf decrypted.pdf # remove password
pdfimages -j input.pdf img # extract images
```
### Watermark
```python
from pypdf import PdfReader, PdfWriter
watermark = PdfReader("watermark.pdf").pages[0]
reader, writer = PdfReader("document.pdf"), PdfWriter()
for page in reader.pages:
page.merge_page(watermark)
writer.add_page(page)
with open("watermarked.pdf", "wb") as f:
writer.write(f)
```
### Password protection
```python
writer.encrypt("userpassword", "ownerpassword")
```
### OCR scanned PDFs
```python
import pytesseract
from pdf2image import convert_from_path
pages = convert_from_path("scanned.pdf")
text = "\n\n".join(pytesseract.image_to_string(img) for img in pages)
```
For batch/structured extraction from scans, the `ocr-and-documents` skill (pymupdf, marker-pdf) is the better path.
## Form filling
Read [forms.md](forms.md) first — it distinguishes fillable (AcroForm) PDFs from flat scanned forms and walks through the helper scripts:
- `scripts/check_fillable_fields.py` — does the PDF have AcroForm fields?
- `scripts/extract_form_field_info.py` / `scripts/extract_form_structure.py` — enumerate fields
- `scripts/fill_fillable_fields.py` — fill AcroForm fields
- `scripts/fill_pdf_form_with_annotations.py` — overlay text on flat forms
- `scripts/check_bounding_boxes.py`, `scripts/create_validation_image.py` — verify placement visually
## Pitfalls
- `page.extract_text()` returns `None` on image-only pages — guard with `or ""` and fall back to OCR.
- pypdf preserves encryption flags: reading an encrypted PDF requires `PdfReader(path, password=...)` before pages are accessible.
- reportlab coordinates are bottom-left origin, points (1/72″) — not top-left.
- When filling flat forms by annotation overlay, always render a validation image and check the placement before delivering.
## Verification
1. Open the output with `PdfReader` and assert the expected page count.
2. Re-extract text from the output (`pdftotext` or pdfplumber) and confirm the content you added is present.
3. For anything visual (watermarks, filled forms, created reports): `pdftoppm -jpeg -r 100 output.pdf page` and inspect the images with `vision_analyze`.
## Related skills
`ocr-and-documents` (scanned-document text extraction), `nano-pdf` (NL text edits in place), `docx` (Word), `xlsx` (spreadsheets), `powerpoint` (decks).
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!