Activate for any direct PDF action a user wants executed right now: generate a PDF from a document file (markdown, HTML, RST, Word), print a file to a printer, preview a document's formatted appearance, email a file as a PDF attachment, merge multiple PDFs, split a PDF into pages, extract specific pages, rotate pages, add watermarks, OCR a scanned PDF, extract text or tables, fill a form, or password-protect a file. Has the pandoc commands, Typst template, and Resend API integration needed — load before starting, not after. Skip for: reading or summarizing an existing PDF, writing PDF-generation code, setting up CI pipelines, or comparing PDF libraries.
PDF Skill
Two capabilities: generate PDFs from documents, and manipulate existing PDFs.
Part 1: PDF Generation & Distribution
Convert documents to PDFs and distribute them — save, preview, print, or email.
Prerequisites
pandocandtypstinstalled (brew install pandoc typst)- For email mode:
RESEND_API_KEYenvironment variable set
Modes
| Mode | Flag | What happens |
|---|---|---|
| save | (default) | PDF saved to ~/Desktop/<filename>.pdf |
| preview | --preview |
PDF opens in Preview.app (temp file) |
--print |
PDF sent to default printer, temp file cleaned up | |
--email <address> |
PDF emailed via Resend, temp file cleaned up |
Examples
/pdf docs/plans/phase-3/M10-pdf-email.md
/pdf README.md --preview
/pdf report.html --print
/pdf changelog.md --email ameet@rajababa.io
How to Generate the PDF
Step 1: Determine the input format.
Pandoc auto-detects from file extension. Common formats:
.md→gfm(GitHub-Flavored Markdown).html→html.docx→docx.rst→rst
Step 2: Run pandoc with the template.
SKILL_DIR="<framework-root>/.claude/skills/pdf"
TEMPLATE_PATH="$SKILL_DIR/templates/clean.typ"
FILTER_PATH="$SKILL_DIR/filters/github-alerts.lua"
# For save mode (to Desktop):
pandoc <input-file> -f gfm -o ~/Desktop/<output-name>.pdf \
--pdf-engine=typst \
--template="$TEMPLATE_PATH" \
--lua-filter="$FILTER_PATH" \
-V timestamp="$(date '+%b %d, %Y %I:%M %p')"
# For preview/print/email (use temp file):
pandoc <input-file> -f gfm -o /tmp/<output-name>.pdf \
--pdf-engine=typst \
--template="$TEMPLATE_PATH" \
--lua-filter="$FILTER_PATH" \
-V timestamp="$(date '+%b %d, %Y %I:%M %p')"
Adjust -f gfm to match the input format if not markdown.
Step 3: Distribute based on mode.
Save (default)
Output is already at ~/Desktop/<name>.pdf. Tell the user.
Preview
open /tmp/<name>.pdf
lpr /tmp/<name>.pdf && rm /tmp/<name>.pdf
Resend requires attachments as Base64-encoded content in JSON:
B64=$(base64 -i /tmp/<name>.pdf)
curl -s -X POST 'https://api.resend.com/emails' \
-H "Authorization: Bearer $RESEND_API_KEY" \
-H "Content-Type: application/json" \
-d "{
\"from\": \"Rajababa <noreply@rajababa.io>\",
\"to\": [\"<recipient>\"],
\"subject\": \"<document title>\",
\"text\": \"PDF attached.\",
\"attachments\": [{
\"filename\": \"<name>.pdf\",
\"content\": \"$B64\"
}]
}"
rm /tmp/<name>.pdf
Important: The to field must be an array: ["email@example.com"], not a string.
Template
The Typst template lives at templates/clean.typ in this skill directory. It uses:
- Font: Helvetica Neue (body), Menlo (code) — macOS system fonts, zero install
- Colors: Neutral palette — charcoal text, gray headings, light rules
- Layout: US Letter, justified body text, 0.9em leading, rules under H1/H2
- Admonitions:
#admonition("NOTE")[...]with colored left borders (NOTE=blue, TIP=green, IMPORTANT=purple, WARNING=amber, CAUTION=red) - Tables: Light gray header row fill for clear data separation
- Footer: Page numbers (left), timestamp (right)
The Lua filter at filters/github-alerts.lua converts GitHub alert syntax (> [!NOTE], etc.) into Typst admonition blocks automatically during PDF generation.
Pandoc Metadata
Pass metadata to customize the title page:
pandoc input.md -o output.pdf \
--pdf-engine=typst \
--template="$TEMPLATE_PATH" \
--lua-filter="$FILTER_PATH" \
-V title="Document Title" \
-V author="Ameet" \
-V date="March 2026" \
-V timestamp="$(date '+%b %d, %Y %I:%M %p')"
If the markdown has YAML frontmatter with title, author, or date, Pandoc picks those up automatically.
Part 2: PDF Manipulation
For working with existing PDF files — merging, splitting, extracting, transforming. Uses Python libraries.
For detailed code examples, patterns, and advanced usage, read these reference files:
references/reference.md— Full manipulation guide (pypdf, pdfplumber, reportlab, CLI tools)references/forms.md— PDF form filling guide
Quick Reference
| Task | Best Tool | Example |
|---|---|---|
| Merge PDFs | pypdf | writer.add_page(page) in a loop |
| Split PDFs | pypdf | One page per output file |
| Extract text | pdfplumber | page.extract_text() |
| Extract tables | pdfplumber | page.extract_tables() |
| Create PDFs programmatically | reportlab | Canvas or Platypus |
| Rotate pages | pypdf | page.rotate(90) |
| Add watermark | pypdf | page.merge_page(watermark) |
| Password protect | pypdf | writer.encrypt("password") |
| OCR scanned PDFs | pytesseract + pdf2image | Convert to image, then OCR |
| CLI merge | qpdf | qpdf --empty --pages f1.pdf f2.pdf -- out.pdf |
Prerequisites
Install Python libraries as needed:
pip install pypdf pdfplumber reportlab
# For OCR:
pip install pytesseract pdf2image
brew install tesseract poppler
# For CLI tools:
brew install qpdf poppler
Important Notes
- Never use Unicode subscript/superscript characters in reportlab — they render as black boxes. Use
<sub>and<super>tags in Paragraph objects instead. - For detailed examples of each operation, read
reference.md. - For PDF form filling, read
forms.mdfirst — it has specific instructions.
Troubleshooting
| Problem | Fix |
|---|---|
| Font rendering issues | Template uses macOS system fonts (Helvetica Neue, Menlo) — should work on any Mac |
| "unknown variable: horizontalrule" | Make sure you're using the template from this skill |
| Resend email fails | Check $RESEND_API_KEY is set: echo $RESEND_API_KEY |
| No default printer | lpstat -p to list printers, or set one in System Settings → Printers |
| pypdf/pdfplumber not found | pip install pypdf pdfplumber |