# PDF

> Pdf

- Skill: `jmech-pepa/pdf` (Agent Skill)
- Install (CLI): `npx skillmds@latest add jmech-pepa/pdf`
- Raw SKILL.md: https://api.skillmd.com/api/skills/jmech-pepa/pdf/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Docs & Writing
- Author: jmech-pepa (https://skillmd.com/u/jmech-pepa)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/jmech-pepa/pdf

---

# /pdf - Extract Structured Data from PDFs

Universal skill for batch-processing PDF documents into structured JSON and a merged Excel file. Works from any project directory.

Do NOT ask for confirmation — just execute and report results at the end.

## Step 0: Detect input

1. **Find PDF source folder:** Check for a `pdf/` directory in the current working directory. If it doesn't exist, check for `pdfs/`, `documents/`, or `docs/` (in that order). If none found, ask the user to specify the path.
2. **List all `.pdf` files** in the source folder (non-recursive by default). Print the count: `Found N PDFs in <folder>/`.
3. **Create output directory:** `mkdir -p ./output` in the current working directory.
4. If no PDFs are found, STOP and report: `No PDF files found in <folder>/`.

## Step 1: Parallel extraction — spawn sub-agents

For EACH PDF file, launch an **independent sub-agent** (using the Agent tool). Launch ALL sub-agents in a SINGLE message so they run in parallel — do NOT process sequentially.

### Each sub-agent receives this task:

```
Read the PDF file at: <absolute_path_to_pdf>

Extract ALL structured data and write a JSON file to: <absolute_path_to_output>/<filename_without_ext>.json

The JSON schema MUST be exactly:
{
  "source_file": "<original filename>",
  "title": "<document title — from metadata, first heading, or first prominent text>",
  "sections": [
    {
      "heading": "<section heading or 'Introduction' if none>",
      "content": "<full text content of this section>",
      "page_numbers": [1, 2]
    }
  ],
  "key_entities": [
    {
      "name": "<entity name>",
      "type": "<person|organization|date|location|amount|product|other>",
      "context": "<short sentence where entity appears>"
    }
  ],
  "metadata": {
    "page_count": <number>,
    "language": "<detected language code, e.g. cs, en>",
    "extracted_at": "<ISO 8601 timestamp>"
  },
  "raw_text": "<complete raw text of the entire document>"
}

Rules:
- Read the PDF using the Read tool (it supports PDFs). For large PDFs (>10 pages), read in chunks of 20 pages at a time.
- Extract entities: names of people, organizations, dates, monetary amounts, locations, products — anything notable.
- Split into sections based on headings, page breaks, or logical content boundaries.
- If the PDF has no clear sections, create a single section with heading "Content".
- Write the JSON to disk IMMEDIATELY after extraction — do not hold it in memory.
- Use JSON.stringify with 2-space indentation.
- Report back: filename, title found, number of sections, number of entities, page count.
```

**Important:** If there are more than 10 PDFs, batch them into groups of 10 sub-agents at a time. Wait for each batch to complete before launching the next. This prevents overwhelming the system.

## Step 2: Verify extraction

After all sub-agents complete:

1. List all `.json` files in `./output/`.
2. Compare against the original PDF list — report any missing extractions.
3. For each JSON, verify it parses correctly and has the required schema fields.
4. Report: `Successfully extracted N/M PDFs`.

If any extractions failed, note them but continue with the successful ones.

## Step 3: Merge into Excel

**Before generating the Excel file, present the proposed structure to the user and wait for approval.**

Show the user a preview like this:

```
Navrhovaná struktura výstupního Excel souboru:

📊 final_output.xlsx
├── Sheet 1: "Summary"
│   └── Columns: File | Title | Language | Pages | Sections | Entities | Extracted At
│   └── (jeden řádek per dokument)
│
├── Sheet 2: "<název souboru 1>"
│   ├── Tabulka "Sections": Section Heading | Content | Pages
│   └── Tabulka "Entities": Entity | Type | Context
│
├── Sheet 3: "<název souboru 2>"
│   └── (stejná struktura)
└── ...

Celkem: N listů (1 summary + N dokumentů)
Výstup: ./final_output.xlsx
```

Wait for the user to confirm or request changes (e.g., different columns, different sheet layout, additional sheets). Adjust the structure accordingly before proceeding.

Once approved, create `./final_output.xlsx` using a Python script:

1. Check if `openpyxl` is available: `python3 -c "import openpyxl"`. If not, install it: `pip3 install openpyxl`.
2. Generate and execute a Python script that:
   - Reads all JSON files from `./output/`
   - Creates a workbook with the approved structure (default below if user confirms without changes):
     - **Summary sheet** (first sheet): columns = `File | Title | Language | Pages | Sections | Entities | Extracted At`. One row per document.
     - **One sheet per document** (sheet name = first 31 chars of filename): columns = `Section Heading | Content | Pages`. Below the sections table, add an "Entities" table: `Entity | Type | Context`.
   - Sheet names must be truncated to 31 characters (Excel limit) and deduplicated if needed.
   - Saves to `./final_output.xlsx`.

## Step 4: Report results

Print a single summary:

```
## 📄 PDF Processing Report

### Source: <folder> (N files)
### Extraction: ✅ N/N successful / ⚠️ N failed
### Output JSON: ./output/ (N files)
### Excel: ./final_output.xlsx

### Per-file summary:
| File | Title | Pages | Sections | Entities |
|------|-------|-------|----------|----------|
| doc1.pdf | ... | 12 | 5 | 23 |
| doc2.pdf | ... | 3 | 2 | 8 |

### Failures (if any):
- doc3.pdf: <error reason>
```

## Notes

- This skill works with any PDF content: contracts, reports, invoices, research papers, etc.
- Entity extraction is best-effort — it identifies prominent named entities, not an exhaustive NER.
- The raw_text field preserves the complete document text for downstream use.
- All intermediate JSON files are kept in `./output/` for inspection or re-processing.

