AgentStack
SKILL verified MIT Self-run

Pdf Handling

skill-furkangonel-cowrangler-pdf-handling · by furkangonel

PDF creation, text extraction, merging, splitting, and form-filling.

No reviews yet
0 installs
4 views
0.0% view→install

Install

$ agentstack add skill-furkangonel-cowrangler-pdf-handling

✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access Used
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

Are you the author of Pdf Handling? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

PDF Handling SOP

Select the right tool for each PDF job and execute it correctly. Covers text extraction, merging, splitting, HTML-to-PDF conversion, form filling, and scanned PDF (OCR) handling.

When to Use

  • User wants to extract text or tables from a PDF
  • User wants to merge multiple PDFs into one
  • User wants to split a PDF into separate files
  • User wants to create a PDF from HTML, Markdown, or data
  • User wants to fill fields in a PDF form
  • User wants to process a scanned (image-based) PDF

Part 1 — Tool Selection Guide

| Task | Best tool | Install | |------|-----------|---------| | Extract text (digital PDF) | pdfplumber | pip install pdfplumber | | Extract tables | pdfplumber | pip install pdfplumber | | Merge / split / rotate pages | pypdf | pip install pypdf | | Create PDF from scratch (programmatic) | reportlab | pip install reportlab | | Convert HTML / CSS to PDF | weasyprint | pip install weasyprint | | Convert Markdown to PDF | weasyprint + markdown | pip install weasyprint markdown | | Fill PDF forms | pypdf or pdfrw | pip install pypdf | | Scanned PDF — OCR | pytesseract + pdf2image | pip install pytesseract pdf2image + system tesseract | | Encrypt / decrypt | pypdf | pip install pypdf |


Part 2 — Text Extraction

Digital PDF — Plain Text

import pdfplumber

with pdfplumber.open("document.pdf") as pdf:
    for page_num, page in enumerate(pdf.pages, 1):
        text = page.extract_text()
        if text:
            print(f"=== Page {page_num} ===")
            print(text)

Extract Text from Specific Pages

with pdfplumber.open("report.pdf") as pdf:
    # Pages 2-5 (0-indexed: pages[1:5])
    for page in pdf.pages[1:5]:
        text = page.extract_text(x_tolerance=3, y_tolerance=3)
        print(text)

Extract Text with Coordinates (for layout-aware parsing)

with pdfplumber.open("form.pdf") as pdf:
    page = pdf.pages[0]
    words = page.extract_words()
    for word in words:
        print(f"  '{word['text']}' at x={word['x0']:.0f}, y={word['top']:.0f}")

Extract All Text to a File

import pdfplumber

def pdf_to_text(input_path, output_path):
    with pdfplumber.open(input_path) as pdf:
        with open(output_path, "w", encoding="utf-8") as out:
            for page in pdf.pages:
                text = page.extract_text() or ""
                out.write(text)
                out.write("\n\n")
    print(f"Extracted {len(pdf.pages)} pages to {output_path}")

pdf_to_text("input.pdf", "output.txt")

Part 3 — Table Extraction

import pdfplumber, csv

with pdfplumber.open("report.pdf") as pdf:
    all_tables = []
    for page in pdf.pages:
        tables = page.extract_tables()
        for table in tables:
            all_tables.extend(table)

# Write to CSV
with open("tables.csv", "w", newline="", encoding="utf-8") as f:
    writer = csv.writer(f)
    writer.writerows(all_tables)

print(f"Extracted {len(all_tables)} rows")

Fine-tuned Table Extraction

with pdfplumber.open("complex.pdf") as pdf:
    page = pdf.pages[0]
    table = page.extract_table({
        "vertical_strategy":   "lines",   # "lines", "text", "explicit"
        "horizontal_strategy": "lines",
        "snap_tolerance":      3,
        "join_tolerance":      3,
        "edge_min_length":     3,
        "min_words_vertical":  1,
    })
    for row in table:
        print(row)

Part 4 — Merge, Split, and Rotate

Merge Multiple PDFs

from pypdf import PdfWriter

writer = PdfWriter()
files = ["chapter1.pdf", "chapter2.pdf", "appendix.pdf"]

for path in files:
    writer.append(path)

with open("combined.pdf", "wb") as out:
    writer.write(out)

print(f"Merged {len(files)} files → combined.pdf")

Split — Extract a Range of Pages

from pypdf import PdfReader, PdfWriter

reader = PdfReader("large_report.pdf")
writer = PdfWriter()

# Extract pages 3–7 (0-indexed: 2–6)
for page_num in range(2, 7):
    writer.add_page(reader.pages[page_num])

with open("pages_3_to_7.pdf", "wb") as out:
    writer.write(out)

Split — One File Per Page

from pypdf import PdfReader, PdfWriter
import os

reader = PdfReader("document.pdf")
os.makedirs("pages", exist_ok=True)

for i, page in enumerate(reader.pages):
    writer = PdfWriter()
    writer.add_page(page)
    with open(f"pages/page_{i+1:03d}.pdf", "wb") as out:
        writer.write(out)

print(f"Split into {len(reader.pages)} files in pages/")

Rotate Pages

from pypdf import PdfReader, PdfWriter

reader = PdfReader("sideways.pdf")
writer = PdfWriter()

for page in reader.pages:
    page.rotate(90)  # 90, 180, or 270 degrees
    writer.add_page(page)

with open("rotated.pdf", "wb") as out:
    writer.write(out)

Part 5 — Create PDFs

HTML / CSS → PDF (weasyprint)

from weasyprint import HTML, CSS

html_content = """

  body { font-family: Arial, sans-serif; margin: 40px; }
  h1   { color: #2c3e50; border-bottom: 2px solid #3498db; padding-bottom: 8px; }
  table { width: 100%; border-collapse: collapse; margin-top: 20px; }
  th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
  th { background: #3498db; color: white; }
  tr:nth-child(even) { background: #f2f2f2; }
  @page { margin: 20mm; }

  Quarterly Report — Q2 2026
  Generated: May 18, 2026
  
    ItemRevenueGrowth
    Product A$120,000+12%
    Product B$85,000+5%
  

"""

HTML(string=html_content).write_pdf("report.pdf")
print("Created report.pdf")

Markdown → PDF

import markdown
from weasyprint import HTML

with open("README.md", "r") as f:
    md_content = f.read()

html_body = markdown.markdown(md_content, extensions=["tables", "fenced_code"])

full_html = f"""

  body {{ font-family: Georgia, serif; max-width: 800px; margin: 40px auto; }}
  code {{ background: #f4f4f4; padding: 2px 4px; border-radius: 3px; font-size: 0.9em; }}
  pre  {{ background: #f4f4f4; padding: 16px; border-radius: 6px; overflow-x: auto; }}
  @page {{ margin: 20mm; }}

{html_body}
"""

HTML(string=full_html).write_pdf("output.pdf")

Programmatic PDF with reportlab

from reportlab.lib.pagesizes import A4
from reportlab.lib.units import mm
from reportlab.pdfgen import canvas

def create_invoice(filename, data):
    c = canvas.Canvas(filename, pagesize=A4)
    width, height = A4

    # Header
    c.setFont("Helvetica-Bold", 24)
    c.drawString(20*mm, height - 30*mm, "INVOICE")
    c.setFont("Helvetica", 11)
    c.drawString(20*mm, height - 42*mm, f"Invoice #: {data['number']}")
    c.drawString(20*mm, height - 50*mm, f"Date: {data['date']}")

    # Line items
    y = height - 80*mm
    c.setFont("Helvetica-Bold", 10)
    c.drawString(20*mm, y, "Description")
    c.drawString(120*mm, y, "Amount")
    y -= 8*mm
    c.line(20*mm, y, 190*mm, y)
    y -= 6*mm

    c.setFont("Helvetica", 10)
    for item in data["items"]:
        c.drawString(20*mm, y, item["description"])
        c.drawRightString(190*mm, y, f"${item['amount']:,.2f}")
        y -= 7*mm

    # Total
    y -= 5*mm
    c.line(20*mm, y, 190*mm, y)
    y -= 8*mm
    c.setFont("Helvetica-Bold", 12)
    c.drawString(120*mm, y, "Total:")
    c.drawRightString(190*mm, y, f"${data['total']:,.2f}")

    c.save()

create_invoice("invoice.pdf", {
    "number": "INV-2026-042",
    "date": "May 18, 2026",
    "items": [
        {"description": "Consulting — 10 hours @ $150/hr", "amount": 1500},
        {"description": "Setup fee", "amount": 250},
    ],
    "total": 1750,
})

Part 6 — Scanned PDFs (OCR)

> Scanned PDFs are images. pdfplumber returns no text — you must OCR first.

# System requirements
brew install tesseract          # macOS
sudo apt-get install tesseract-ocr  # Ubuntu/Debian
pip install pytesseract pdf2image
import pytesseract
from pdf2image import convert_from_path

# Convert PDF pages to images, then OCR each
pages = convert_from_path("scanned.pdf", dpi=300)

full_text = []
for i, page_image in enumerate(pages, 1):
    text = pytesseract.image_to_string(page_image, lang="eng")
    print(f"=== Page {i} ===")
    print(text)
    full_text.append(text)

with open("scanned_output.txt", "w", encoding="utf-8") as f:
    f.write("\n\n".join(full_text))

Part 7 — Encrypt and Decrypt

from pypdf import PdfReader, PdfWriter

# Encrypt
reader = PdfReader("document.pdf")
writer = PdfWriter()
writer.append_pages_from_reader(reader)
writer.encrypt(user_password="view123", owner_password="admin456", use_128bit=True)
with open("protected.pdf", "wb") as f:
    writer.write(f)

# Decrypt
reader = PdfReader("protected.pdf")
if reader.is_encrypted:
    reader.decrypt("view123")
writer = PdfWriter()
writer.append_pages_from_reader(reader)
with open("unlocked.pdf", "wb") as f:
    writer.write(f)

Common Pitfalls

| Problem | Cause | Fix | |---------|-------|-----| | extract_text() returns empty string | Scanned PDF (image-based) | Use OCR (Part 6) | | Garbled text order | Complex multi-column layout | Try extract_words() with coordinate sorting | | weasyprint missing fonts | System fonts not found | Install system font packages | | Table extraction misses columns | Poor line detection | Adjust vertical_strategy to "text" | | Large PDF slow to process | All pages loaded at once | Process page-by-page in a loop |


Checklist

  • [ ] Identified whether PDF is digital (has selectable text) or scanned (image)
  • [ ] Chose correct tool for the task (extraction vs creation vs merge)
  • [ ] Scanned PDFs routed through OCR pipeline
  • [ ] For HTML→PDF, tested output at target paper size (A4 or Letter)
  • [ ] Merged output verified with page count check
  • [ ] Large PDFs processed page-by-page to avoid memory issues

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet — be the first.

Versions

  • v0.1.0 Imported from the upstream source.