pdf-harvester
Extract text and data from PDF documents
What this skill does
# PDF Harvester Skill
> Extract and ingest PDF documents into RAG with proper text extraction, table handling, and metadata.
## Overview
PDFs are common for research papers, reports, manuals, and ebooks. This skill covers:
- Text extraction with layout preservation
- Table extraction and conversion to markdown
- Academic paper patterns (abstract, sections, citations)
- OCR for scanned documents
- Multi-page chunking strategies
## Prerequisites
```bash
# Core extraction
pip install pdfplumber pymupdf
# For OCR (scanned documents)
pip install pytesseract pdf2image
# Also need: brew install tesseract poppler (macOS)
# For academic papers
pip install arxiv # If fetching from arXiv
```
## Extraction Methods
### Method 1: pdfplumber (Recommended)
Best for structured PDFs with tables.
```python
#!/usr/bin/env python3
"""PDF extraction using pdfplumber."""
import pdfplumber
from pathlib import Path
from typing import Dict, List, Optional
import re
def extract_pdf_text(
pdf_path: str,
extract_tables: bool = True
) -> Dict:
"""
Extract text and tables from PDF.
Args:
pdf_path: Path to PDF file
extract_tables: Whether to extract tables separately
Returns:
Dict with pages, tables, and metadata
"""
result = {
"pages": [],
"tables": [],
"metadata": {},
"total_pages": 0
}
with pdfplumber.open(pdf_path) as pdf:
result["total_pages"] = len(pdf.pages)
result["metadata"] = pdf.metadata or {}
for page_num, page in enumerate(pdf.pages, 1):
# Extract text
text = page.extract_text() or ""
result["pages"].append({
"page_number": page_num,
"text": text,
"width": page.width,
"height": page.height
})
# Extract tables
if extract_tables:
tables = page.extract_tables()
for table_num, table in enumerate(tables, 1):
if table and len(table) > 0:
result["tables"].append({
"page_number": page_num,
"table_number": table_num,
"data": table,
"markdown": table_to_markdown(table)
})
return result
def table_to_markdown(table: List[List]) -> str:
"""Convert table data to markdown format."""
if not table or len(table) == 0:
return ""
# Clean cells
def clean_cell(cell):
if cell is None:
return ""
return str(cell).replace("
", " ").strip()
# Header row
headers = [clean_cell(c) for c in table[0]]
md = "| " + " | ".join(headers) + " |
"
md += "| " + " | ".join(["---"] * len(headers)) + " |
"
# Data rows
for row in table[1:]:
cells = [clean_cell(c) for c in row]
# Pad if necessary
while len(cells) < len(headers):
cells.append("")
md += "| " + " | ".join(cells[:len(headers)]) + " |
"
return md
```
### Method 2: PyMuPDF (fitz)
Faster, better for large PDFs.
```python
#!/usr/bin/env python3
"""PDF extraction using PyMuPDF."""
import fitz # PyMuPDF
from typing import Dict, List
def extract_with_pymupdf(pdf_path: str) -> Dict:
"""
Extract text using PyMuPDF.
Faster than pdfplumber, good for large documents.
"""
doc = fitz.open(pdf_path)
result = {
"pages": [],
"metadata": doc.metadata,
"total_pages": len(doc)
}
for page_num, page in enumerate(doc, 1):
# Get text with layout preservation
text = page.get_text("text")
# Get text blocks for better structure
blocks = page.get_text("dict")["blocks"]
result["pages"].append({
"page_number": page_num,
"text": text,
"blocks": len(blocks)
})
doc.close()
return result
def extract_with_structure(pdf_path: str) -> Dict:
"""Extract with heading detection."""
doc = fitz.open(pdf_path)
pages = []
for page_num, page in enumerate(doc, 1):
blocks = page.get_text("dict")["blocks"]
structured_content = []
for block in blocks:
if block["type"] == 0: # Text block
for line in block.get("lines", []):
for span in line.get("spans", []):
text = span["text"].strip()
font_size = span["size"]
is_bold = "bold" in span["font"].lower()
# Detect headings by font size
if font_size > 14 or is_bold:
structured_content.append({
"type": "heading",
"text": text,
"size": font_size
})
else:
structured_content.append({
"type": "paragraph",
"text": text
})
pages.append({
"page_number": page_num,
"content": structured_content
})
doc.close()
return {"pages": pages, "total_pages": len(pages)}
```
### Method 3: OCR for Scanned PDFs
```python
#!/usr/bin/env python3
"""OCR extraction for scanned PDFs."""
import pytesseract
from pdf2image import convert_from_path
from typing import Dict, List
def extract_with_ocr(
pdf_path: str,
language: str = "eng",
dpi: int = 300
) -> Dict:
"""
Extract text from scanned PDF using OCR.
Args:
pdf_path: Path to PDF
language: Tesseract language code
dpi: Resolution for conversion
"""
# Convert PDF pages to images
images = convert_from_path(pdf_path, dpi=dpi)
pages = []
for page_num, image in enumerate(images, 1):
# Run OCR
text = pytesseract.image_to_string(image, lang=language)
pages.append({
"page_number": page_num,
"text": text,
"ocr": True
})
return {
"pages": pages,
"total_pages": len(pages),
"ocr_used": True
}
def is_scanned_pdf(pdf_path: str) -> bool:
"""Detect if PDF is scanned (image-based)."""
import fitz
doc = fitz.open(pdf_path)
# Check first few pages
for page in doc[:min(3, len(doc))]:
text = page.get_text().strip()
if len(text) > 100: # Has extractable text
doc.close()
return False
doc.close()
return True
```
## Chunking Strategies
### Strategy 1: Page-Based
Simple chunking by page boundaries.
```python
def chunk_by_pages(
extracted: Dict,
pages_per_chunk: int = 1
) -> List[Dict]:
"""Chunk PDF by page boundaries."""
chunks = []
pages = extracted["pages"]
for i in range(0, len(pages), pages_per_chunk):
page_group = pages[i:i + pages_per_chunk]
text = "
".join(p["text"] for p in page_group)
chunks.append({
"content": text,
"page_start": page_group[0]["page_number"],
"page_end": page_group[-1]["page_number"],
"chunk_index": len(chunks)
})
return chunks
```
### Strategy 2: Section-Based
Chunk by document sections/headings.
```python
def chunk_by_sections(
extracted: Dict,
heading_patterns: List[str] = None
) -> List[Dict]:
"""Chunk PDF by section headings."""
if heading_patterns is None:
heading_patterns = [
r'^#+\s', # Markdown headings
r'^\d+\.\s+[A-Z]', # Numbered sections
r'^[A-Z][A-Z\s]+$', # ALL CAPS headings
r'^(Abstract|Introduction|Conclusion|References)',
]
full_text = "
".join(p["text"] for p in extracted["pages"])
# Find section boundariesRelated in Writing & Docs
jax-development
IncludedUse this skill when the user is writing, debugging, profiling, refactoring, reviewing, benchmarking, parallelising, exporting, or explaining JAX code, or when they mention JAX, jax.numpy, jit, grad, value_and_grad, vmap, scan, lax, random keys, pytrees, jax.Array, sharding, Mesh, PartitionSpec, NamedSharding, pmap, shard_map, Pallas, XLA, StableHLO, checkify, profiler, or the JAX repo. It helps turn NumPy or PyTorch-style code into pure functional JAX, fix tracer/control-flow/shape/PRNG bugs, remove recompiles and host-device syncs, choose transforms and sharding strategies, inspect jaxpr/lowering/IR, and benchmark compiled code correctly.
nature-article-writer
IncludedDrafts, rewrites, diagnostically critiques, and style-calibrates primary research manuscripts for Nature and Nature Portfolio journals. Use when the user wants a Nature-style title, summary paragraph or abstract, introduction, results, discussion, methods, figure legends, presubmission enquiry, cover letter, reviewer response, or when a scientific draft sounds generic, jargon-heavy, structurally weak, or AI-ish and needs precise, broad-reader-friendly prose without inventing data, analyses, or references. Best for primary research articles and letters rather than reviews or press releases unless explicitly adapting one.
deckrd
IncludedDocument-driven framework that derives requirements, specifications, implementation plans, and executable tasks from goals through structured AI dialogue. Use when user says "write requirements", "create spec", "plan implementation", "derive tasks", "structure this feature", "break down into tasks", or "document this module". Also use for reverse engineering existing code into docs (/deckrd rev). Do NOT use for direct code writing — use /deckrd-coder after tasks are generated. Do NOT use when the user only wants to run or fix existing code without planning.
clinical-decision-support
IncludedGenerate professional clinical decision support (CDS) documents for pharmaceutical and clinical research settings, including patient cohort analyses (biomarker-stratified with outcomes) and treatment recommendation reports (evidence-based guidelines with decision algorithms). Supports GRADE evidence grading, statistical analysis (hazard ratios, survival curves, waterfall plots), biomarker integration, and regulatory compliance. Outputs publication-ready LaTeX/PDF format optimized for drug development, clinical research, and evidence synthesis.
handling-sf-data
IncludedSalesforce data operations with 130-point scoring. Use this skill to create, update, delete, bulk import/export, generate test data, and clean up org records using sf CLI and anonymous Apex. TRIGGER when: user creates test data, performs bulk import/export, uses sf data CLI commands, needs data factory patterns for Apex tests, or needs to seed/clean records in a Salesforce org. DO NOT TRIGGER when: SOQL query writing only (use querying-soql), Apex test execution (use running-apex-tests), or metadata deployment (use deploying-metadata).
accelint-ac-to-playwright
IncludedConvert and validate acceptance criteria for Playwright test automation. Use when user asks to (1) review/evaluate/check if AC are ready for automation, (2) assess if AC can be converted as-is, (3) validate AC quality for Playwright, (4) turn AC into tests, (5) generate tests from acceptance criteria, (6) convert .md bullets or .feature Gherkin files to Playwright specs, (7) create test automation from requirements. Handles both bullet-style markdown and Gherkin syntax with JSON test plan generation and validation.