CtrlK
BlogDocsLog inGet started
Tessl Logo

pdf-reader

Use this skill when the user wants to read, parse, or extract data from PDF files — text, tables, images, or metadata.

72

Quality

88%

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 Reader

Overview

Extracts text, tables, images, and metadata from PDF files. Handles text-based and scanned (OCR) PDFs. Uses pdfplumber for tables, pymupdf for speed, and pytesseract for scanned documents.

Quick Start — Extract All Text

import pdfplumber

def extract_text(pdf_path: str) -> str:
    with pdfplumber.open(pdf_path) as pdf:
        return "\n\n".join(
            page.extract_text() or "" for page in pdf.pages
        )

Extract Tables

import pdfplumber
import pandas as pd

def extract_tables(pdf_path: str, page_num: int | None = None) -> list[pd.DataFrame]:
    """Extract all tables from PDF as DataFrames."""
    dfs = []
    with pdfplumber.open(pdf_path) as pdf:
        pages = [pdf.pages[page_num]] if page_num is not None else pdf.pages
        for page in pages:
            for table in page.extract_tables():
                if table and table[0]:
                    df = pd.DataFrame(table[1:], columns=table[0])
                    dfs.append(df)
    return dfs

def tables_to_csv(pdf_path: str, output_dir: str = ".") -> list[str]:
    from pathlib import Path
    Path(output_dir).mkdir(exist_ok=True)
    paths = []
    for i, df in enumerate(extract_tables(pdf_path)):
        out = f"{output_dir}/table_{i+1}.csv"
        df.to_csv(out, index=False)
        paths.append(out)
    return paths

Fast Extraction with pymupdf

import fitz  # pip install pymupdf

def extract_text_fast(pdf_path: str) -> str:
    """10-50x faster than pdfplumber for text-only extraction."""
    doc = fitz.open(pdf_path)
    return "\n\n".join(page.get_text() for page in doc)

def extract_page_range(pdf_path: str, start: int, end: int) -> str:
    """Extract text from pages start..end (0-indexed)."""
    doc = fitz.open(pdf_path)
    return "\n\n".join(doc[i].get_text() for i in range(start, min(end, len(doc))))

def get_metadata(pdf_path: str) -> dict:
    doc = fitz.open(pdf_path)
    return {**doc.metadata, "pages": len(doc)}

Extract Images

import fitz
from pathlib import Path

def extract_images(pdf_path: str, output_dir: str = "extracted_images") -> list[str]:
    Path(output_dir).mkdir(exist_ok=True)
    doc = fitz.open(pdf_path)
    saved = []
    for page_num, page in enumerate(doc):
        for img_index, img in enumerate(page.get_images(full=True)):
            xref = img[0]
            base_image = doc.extract_image(xref)
            ext = base_image["ext"]
            out = f"{output_dir}/page{page_num+1}_img{img_index+1}.{ext}"
            Path(out).write_bytes(base_image["image"])
            saved.append(out)
    return saved

OCR for Scanned PDFs

from pdf2image import convert_from_path
import pytesseract

def ocr_pdf(pdf_path: str, lang: str = "eng") -> str:
    """Extract text from a scanned PDF using OCR."""
    images = convert_from_path(pdf_path, dpi=300)
    pages = []
    for i, img in enumerate(images):
        text = pytesseract.image_to_string(img, lang=lang)
        pages.append(f"--- Page {i+1} ---\n{text}")
    return "\n\n".join(pages)

def is_text_pdf(pdf_path: str) -> bool:
    """Returns True if PDF has selectable text (not scanned)."""
    with pdfplumber.open(pdf_path) as pdf:
        return any(page.extract_text() for page in pdf.pages[:3])

Multi-Column Layout

import pdfplumber

def extract_two_column(pdf_path: str) -> str:
    """Handle 2-column academic/newspaper layouts."""
    text_parts = []
    with pdfplumber.open(pdf_path) as pdf:
        for page in pdf.pages:
            w = page.width
            left = page.crop((0, 0, w/2, page.height))
            right = page.crop((w/2, 0, w, page.height))
            text_parts.append(left.extract_text() or "")
            text_parts.append(right.extract_text() or "")
    return "\n\n".join(t for t in text_parts if t.strip())

Quick Reference

TaskLibraryNotes
Extract textpdfplumber or pymupdfpymupdf faster
Extract tablespdfplumberReturns nested lists
Tables → DataFramepdfplumber + pandasThen .to_csv()
Extract imagespymupdf (fitz)Returns bytes
Scanned PDFpdf2image + pytesseractNeeds tesseract installed
Check if scannedpdfplumberCheck for empty text
Metadatapymupdf.metadata dict
Repository
ProwlrBot/prowlr-marketplace
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.