BundledProductivityVersion 1.0.0

Hermes Agent PDF Skill: Merge, Split, Fill, Encrypt, and Extract PDFs

Create, merge, split, fill, and secure PDF files.

Written by Neura Market from the official Hermes Agent documentation for Pdf. Commands, paths, and version numbers are reproduced from the source unchanged.

Read the official documentation

The PDF skill bundled with Hermes Agent turns the agent into a capable PDF workstation. You can merge, split, rotate, watermark, encrypt, fill forms, extract text and tables, create new documents from scratch, and OCR scanned pages. Reach for this skill whenever a user mentions a .pdf file or asks to produce one. It is the default tool for anything PDF-related, with the exception of heavy scanned-document extraction (use the ocr-and-documents skill) and natural-language edits to existing PDF text (use the nano-pdf skill).

What it does

In practice, this skill gives the agent a set of Python libraries and command-line tools that cover the full PDF lifecycle:

  • Read and extract: pull text, tables, and images from existing PDFs.
  • Combine and split: merge multiple PDFs into one, or split a single PDF into separate pages or page ranges.
  • Transform: rotate pages, add watermarks, reorder pages.
  • Create: generate new PDFs with reportlab, including formatted text, headings, and page breaks.
  • Fill forms: handle both fillable AcroForm PDFs and flat scanned forms via helper scripts.
  • Secure: encrypt with password protection, decrypt password-protected files.
  • OCR: convert scanned image-only PDFs to searchable text.

Before you start

The skill requires several Python packages and system utilities. Install them with:

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

On macOS, replace the apt commands with brew install poppler qpdf. For OCR support, add:

pip install pytesseract pdf2image
sudo apt install -y tesseract-ocr

Script paths in the examples below are relative to the skill's directory at skills/productivity/pdf. The form-filling workflow has its own dedicated guide; read forms.md and follow it before attempting to fill forms. For advanced library usage (pypdfium2, pdf-lib) and troubleshooting, see reference.md.

Quick Reference

TaskBest ToolCommand/Code
Merge PDFspypdfwriter.add_page(page) per page
Split PDFspypdfOne page per file
Extract textpdfplumberpage.extract_text()
Extract tablespdfplumberpage.extract_tables()
Create PDFsreportlabCanvas or Platypus
Command-line merge/splitqpdfqpdf --empty --pages ...
OCR scanned PDFspytesseractConvert to images first (or use ocr-and-documents)
Fill PDF formssee forms.mdscripts/fill_fillable_fields.py etc.
Edit existing textnano-pdf skillnano-pdf edit file.pdf ""

Common operations

Merge / split / rotate (pypdf)

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)

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)

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 / markup inside Paragraph objects: Paragraph("H2O", styles['Normal']). For canvas-drawn text, adjust font size and position manually.

Command-line tools

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

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

writer.encrypt("userpassword", "ownerpassword")

OCR scanned PDFs

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 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.

When not to use it

If the user needs heavy text extraction from scanned documents, prefer the ocr-and-documents skill. For natural-language edits to existing PDF text (e.g., changing a sentence in place), use the nano-pdf skill instead.

Limits and gotchas

  • The built-in reportlab fonts do not support Unicode sub/superscript characters; use markup or manual positioning.
  • pypdf preserves encryption flags, so you must supply a password when reading encrypted files.
  • reportlab uses a bottom-left coordinate system with points (1/72 inch), not the top-left origin many expect.
  • Always verify annotation placement on flat forms with a validation image before delivering the result.

Related skills

ocr-and-documents (scanned-document text extraction), nano-pdf (NL text edits in place), docx (Word), xlsx (spreadsheets), powerpoint (decks).

Skills the docs pair this with

More Productivity skills