Skip to main content
AIDiveForge AIDiveForge
📑

PDF Structure Extraction

Data Extraction & Parsing · by AIDiveForge · Apr 17, 2026 · Intermediate · ✓ 1 verified compat

Extract page-linked text from a text PDF and identify pages that need OCR.

Get JSON with page numbers, extracted text and explicit OCR-needed flags.

Free · 4 KB · SKILL.md, instructions and example files. Python 3.10+ and pypdf are required for the included extraction helper.

A worked example

A small example to try before using your own data.

Input

{
  "file": "examples/sample.pdf",
  "expected_pages": 2
}

Expected result

{
  "page_count": 2,
  "pages": [
    {
      "page_number": 1,
      "text_contains": "Quarterly report",
      "needs_ocr": false
    },
    {
      "page_number": 2,
      "text_contains": "Appendix",
      "needs_ocr": false
    }
  ]
}

What to check: Both fixture pages retain their numbers and expected text; a blank/scanned page is flagged rather than silently reported as extracted.

Limits: Text extraction cannot recover arbitrary table semantics or scanned text. Layout and OCR are separate checks; no 90% accuracy claim is made.

Worked example included. Model-specific performance and vendor compatibility have not been benchmarked.

Installation options and other formats

Install this skill

An agent skill starts with SKILL.md. The ZIP also includes its examples and any helper scripts. Drop the file into your tool of choice — or pick a different format if you use Cursor, Windsurf, Copilot, or something else.

Download SKILL.md
python3 - <<'ADF_SKILL_INSTALL'
import hashlib, io, os, stat, tempfile, urllib.request, zipfile
from pathlib import Path, PurePosixPath
folder = "pdf-structure-extraction"
root = Path(os.environ.get('ADF_SKILL_ROOT', str(Path.home() / '.claude' / 'skills'))).expanduser()
destination = root / folder
if destination.exists() or destination.is_symlink():
    raise SystemExit('An installation already exists. Back it up and merge the ZIP deliberately.')
root.mkdir(parents=True, exist_ok=True)
request = urllib.request.Request("https://aidiveforge.com/skill/pdf-structure-extraction.zip", headers={'User-Agent': 'AIDiveForge-Resource-Installer/1.0 (+https://aidiveforge.com/skills/)'})
with urllib.request.urlopen(request, timeout=30) as response:
    data = response.read(8 * 1024 * 1024 + 1)
if len(data) > 8 * 1024 * 1024 or hashlib.sha256(data).hexdigest() != "b163e9e8b53b018ce7fa8ba01cabd12885b64453bef751d547d9294dbbd8a25a":
    raise SystemExit('The download did not match this reviewed bundle. No files were installed.')
with zipfile.ZipFile(io.BytesIO(data)) as archive, tempfile.TemporaryDirectory(prefix='.adf-skill-', dir=root) as temporary:
    members = archive.infolist()
    if len(members) > 100 or sum(m.file_size for m in members) > 16 * 1024 * 1024:
        raise SystemExit('Unexpected archive size')
    for member in members:
        name = PurePosixPath(member.filename)
        if name.is_absolute() or '..' in name.parts or not name.parts or name.parts[0] != folder or '\\' in member.filename or stat.S_ISLNK(member.external_attr >> 16):
            raise SystemExit('Unsafe archive entry; nothing was installed')
        target = Path(temporary).joinpath(*name.parts)
        if member.is_dir():
            target.mkdir(parents=True, exist_ok=True)
        else:
            target.parent.mkdir(parents=True, exist_ok=True)
            with target.open('xb') as stream:
                stream.write(archive.read(member))
    staged = Path(temporary) / folder
    if not (staged / 'SKILL.md').is_file() or destination.exists() or destination.is_symlink():
        raise SystemExit('The skill could not be installed without replacing existing data')
    os.rename(staged, destination)
print('Installed the complete skill folder:', destination)
ADF_SKILL_INSTALL

Claude Code: ~/.claude/skills/pdf-structure-extraction/SKILL.md. Codex: ~/.codex/skills/pdf-structure-extraction/SKILL.md. Keep the complete ZIP folder when helper scripts are included.

🧠 Why it works

Both fixture pages retain their numbers and expected text; a blank/scanned page is flagged rather than silently reported as extracted.

⚙️ How it works

  1. Run scripts/extract_pdf.py input.pdf --output extracted.json in a Python environment with pypdf installed.
  1. Inspect pages[].text alongside the original PDF. Keep page_number on every passage you reuse.
  1. If a page is flagged needs_ocr, report that limitation; this helper does not install or run OCR.
  1. Group passages into sections only where headings are visible in the source. Keep uncertain layout and reading order explicit.
  1. For a table, compare row and column boundaries with the page image before treating extracted text as cells.

Description

Get JSON with page numbers, extracted text and explicit OCR-needed flags.

Python 3.10+ and pypdf are required for the included extraction helper.

Limits: Text extraction cannot recover arbitrary table semantics or scanned text. Layout and OCR are separate checks; no 90% accuracy claim is made.

Recommended Use

Tools and workflow packs this skill pairs well with. Forge picks are auto-generated from category + capability signals; Community picks are added by people who've used the pairing.

No matches yet. Be the first to suggest a pairing, or the Forge will populate suggestions as signals align.