CtrlK
BlogDocsLog inGet started
Tessl Logo

pdf-sign

This skill should be used when the user asks to "sign this PDF", "fill in this form", "add my signature to", or mentions signing letters, claim forms, or contracts. Overlays a signature image, ticks checkboxes, and fills date/name/phone fields on scanned or digital PDFs using pymupdf, with OCR fallback for scanned documents.

73

Quality

90%

Does it follow best practices?

Run evals on this skill

Adds up to 20 points to the overall score

View guide

SecuritybySnyk

Passed

No findings from the security scan

SKILL.md
Quality
Evals
Security

PDF Sign

Sign and fill PDF forms programmatically. Works on both digital PDFs (with a text layer) and scanned PDFs (via OCR).

Signature image

Always ask the user where their signature image is — never assume a location, and never commit a signature image to a repository. A scanned signature (dark ink on white) works best.

If the image has a white background (typical JPG scan), convert it to a transparent PNG first — otherwise the overlay blanks out any text or lines beneath it:

from PIL import Image
img = Image.open(SRC_JPG).convert("RGBA")
img.putdata([(0, 0, 0, 0 if (r+g+b)//3 > 200 else 255-(r+g+b)//3)
             for r, g, b, a in img.getdata()])
img.save(OUT_PNG)

Save the transparent version next to the original so it can be reused.

Setup

pymupdf in a venv (system Python is often externally managed):

python3 -m venv ./scratch/venv   # or /tmp outside a repo
./scratch/venv/bin/pip install -q pymupdf pillow

OCR (scanned PDFs) needs tesseract: brew install tesseract, and set TESSDATA_PREFIX (Homebrew: /opt/homebrew/share/tessdata).

Approach

  1. Try text search first. page.search_for("SIGNATURE:") — a digital PDF locates fields directly.
  2. Scanned PDF → page.get_text() returns empty. Fall back to an OCR textpage:
    tp = page.get_textpage_ocr(full=True, dpi=200)
    hits = page.search_for("SIGNATURE:", textpage=tp)
  3. Place content relative to the found label rects (see recipe).
  4. Always render and visually verify each modified page before declaring done:
    page.get_pixmap(dpi=100).save("check.png")
    Read the PNG — misplaced marks are common on the first pass.

Recipe

import os
os.environ.setdefault("TESSDATA_PREFIX", "/opt/homebrew/share/tessdata")
import pymupdf

doc = pymupdf.open(SRC)
page = doc[0]
tp = page.get_textpage_ocr(full=True, dpi=200)  # omit for digital PDFs

def find(needle):
    hits = page.search_for(needle, textpage=tp)
    return hits[0] if hits else None

# Signature: transparent PNG spanning the line, right of the label
r = find("SIGNATURE:")
page.insert_image(pymupdf.Rect(r.x1 + 40, r.y0 - 32, r.x1 + 180, r.y1 + 4),
                  filename=SIGNATURE_PNG, keep_proportion=True)

# Text field: right of label, baseline-aligned
r = find("DATE:")
page.insert_text((r.x1 + 15, r.y1 - 2), "01/31/2026", fontsize=11, color=(0, 0, 0.55))

# Checkbox: box sits ~22pt left of its label text
r = find("Check here if your name")
page.insert_text((r.x0 - 22, r.y1 - 2), "X", fontsize=12, color=(0, 0, 0.55))

doc.save(OUT)  # never overwrite the source

Blue-ish ink (color=(0, 0, 0.55)) reads as pen on printed forms. Match the form's date format (US forms: MM/DD/YYYY).

Gotchas

ProblemFix
search_for finds nothing, no errorScanned page with no text layer — check len(page.get_text()) == 0, use OCR textpage
Label matches twice (e.g. DATE: also hits Check Date: header)Filter hits by y-position, e.g. same line as the SIGNATURE match: abs(h.y1 - rsig.y1) < 8
Partial-phrase match places mark mid-sentenceAnchor x on a reliably-found label (e.g. checkbox column from the first checkbox label), use the phrase match only for y
Checkbox X lands on textCheckbox is left of label: x0 - 22 works for typical forms; verify visually
Signature overlay blanks out text/lines beneath itWhite image background — convert to transparent PNG (see above)
Multi-copy documents (same letter × N pages)Loop the letter pages, skip FAQ/back pages
Draft email already carries the old attachment after re-signingMail clients attach a snapshot; discard the draft and recreate it with the new file
import fitz deprecation warningUse import pymupdf

Workflow

  1. Ask the user for the signature image location (and any fill values: date, printed name, phone)
  2. Save/locate the source PDF
  3. Sign to a new file <name> - Signed.pdf — never overwrite the source
  4. Render pages → view the PNGs → verify placement
  5. Iterate until correct, then hand back or attach to a draft email
Repository
dwmkerr/claude-toolkit
Last updated
First committed

Is this your skill?

If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.