Skill: wancy86/pdf-reader-cn (version 1.0.0)

--- name: openclaw-pdf-reader version: 1.0.0 description: Read, extract, and analyze PDF files. Use when the user needs to: (1) Extract text from PDF documents, (2) Analyze PDF content, (3) Summarize PDF documents, (4) Search for specific information in PDFs, (5) Extract tables from PDFs --- # PDF Reader Skill Extract and analyze content from PDF files using pdfplumber and PyMuPDF. ## Quick Start For basic text extraction, use the provided script: ```bash python scripts/extract_pdf.py <path-to-pdf> ``` ## Capabilities - **Text extraction** - Extract all text from PDF pages - **Table extraction** - Extract tables as structured data - **Metadata** - Get PDF metadata (author, pages, etc.) - **Page-specific** - Extract from specific pages or ranges - **Search** - Find specific text within PDF ## Scripts | Script | Purpose | |--------|---------| | `scripts/extract_pdf.py` | Extract text from PDF | | `scripts/analyze_pdf.py` | Analyze and summarize PDF content | ## References - `references/pdf-libraries.md` - Python PDF library documentation ## Usage Examples **Extract all text:** ``` python scripts/extract_pdf.py document.pdf ``` **Extract specific page:** ``` python scripts/extract_pdf.py document.pdf --page 5 ``` **Analyze and summarize:** ``` python scripts/analyze_pdf.py document.pdf ```

path: references/pdf-libraries.md
content: # PDF Python Libraries Reference

## pdfplumber

**Best for:** Text and table extraction with precise positioning

```python
import pdfplumber

with pdfplumber.open("file.pdf") as pdf:
    for page in pdf.pages:
        text = page.extract_text()
        tables = page.extract_tables()
```

**Features:**
- Extract text with coordinates
- Extract tables as lists
- Get words/lines/chars with position
- Crop pages

## PyMuPDF (fitz)

**Best for:** High-performance operations, rendering

```python
import fitz  # pymupdf

doc = fitz.open("file.pdf")
for page in doc:
    text = page.get_text()
```

**Features:**
- Fast text extraction
- Render pages to images
- Extract fonts/images
- Modify PDFs

## When to Use Which

| Task | Recommended |
|------|-------------|
| Text extraction | pdfplumber |
| Table extraction | pdfplumber |
| Render to image | PyMuPDF |
| Fast bulk processing | PyMuPDF |
| Precise positioning | pdfplumber |


path: scripts/analyze_pdf.py
content: #!/usr/bin/env python
"""Analyze and summarize PDF content."""

import argparse
import pdfplumber


def analyze_pdf(path: str) -> dict:
    """Analyze PDF structure and content.
    
    Args:
        path: Path to PDF file
    
    Returns:
        Analysis results
    """
    result = {
        "path": path,
        "total_pages": 0,
        "total_chars": 0,
        "total_words": 0,
        "tables_count": 0,
        "page_stats": []
    }
    
    all_text = []
    
    with pdfplumber.open(path) as pdf:
        result["total_pages"] = len(pdf.pages)
        result["metadata"] = {
            "author": pdf.metadata.get("Author", ""),
            "title": pdf.metadata.get("Title", ""),
        }
        
        for i, page in enumerate(pdf.pages):
            text = page.extract_text() or ""
            tables = page.extract_tables()
            
            chars = len(text)
            words = len(text.split())
            
            result["total_chars"] += chars
            result["total_words"] += words
            result["tables_count"] += len(tables) if tables else 0
            
            result["page_stats"].append({
                "page": i + 1,
                "chars": chars,
                "words": words,
                "tables": len(tables) if tables else 0
            })
            
            all_text.append(text)
    
    # Generate summary
    result["summary"] = {
        "avg_words_per_page": result["total_words"] / max(result["total_pages"], 1),
        "text_density": "high" if result["total_words"] > 5000 else "medium" if result["total_words"] > 1000 else "low"
    }
    
    # First and last page previews
    result["preview"] = {
        "first_page": all_text[0][:500] + "..." if all_text and len(all_text[0]) > 500 else all_text[0] if all_text else "",
        "last_page": all_text[-1][:500] + "..." if all_text and len(all_text[-1]) > 500 else all_text[-1] if len(all_text) > 1 else ""
    }
    
    return result


def main():
    parser = argparse.ArgumentParser(description="Analyze PDF content")
    parser.add_argument("path", help="Path to PDF file")
    parser.add_argument("--json", action="store_true", help="Output as JSON")
    args = parser.parse_args()
    
    result = analyze_pdf(args.path)
    
    if args.json:
        import json
        print(json.dumps(result, indent=2, ensure_ascii=False))
    else:
        print(f"\n📄 PDF Analysis: {result['path']}")
        print("=" * 50)
        print(f"Total Pages: {result['total_pages']}")
        print(f"Total Words: {result['total_words']:,}")
        print(f"Total Characters: {result['total_chars']:,}")
        print(f"Tables Found: {result['tables_count']}")
        if result['metadata']['title']:
            print(f"Title: {result['metadata']['title']}")
        if result['metadata']['author']:
            print(f"Author: {result['metadata']['author']}")
        print(f"\nAvg Words/Page: {result['summary']['avg_words_per_page']:.0f}")
        print(f"Text Density: {result['summary']['text_density']}")
        print("\n--- Page Breakdown ---")
        for stat in result["page_stats"][:10]:  # Show first 10 pages
            print(f"  Page {stat['page']}: {stat['words']:,} words, {stat['tables']} tables")
        if len(result["page_stats"]) > 10:
            print(f"  ... and {len(result['page_stats']) - 10} more pages")
        
        print("\n--- Preview (First Page) ---")
        print(result["preview"]["first_page"])


if __name__ == "__main__":
    main()


path: scripts/extract_pdf.py
content: #!/usr/bin/env python
"""Extract text from PDF files using pdfplumber."""

import argparse
import json
import pdfplumber


def extract_pdf(path: str, page_num: int = None) -> dict:
    """Extract text from PDF.
    
    Args:
        path: Path to PDF file
        page_num: Optional specific page number (1-indexed)
    
    Returns:
        Dict with pages and extracted text
    """
    result = {"path": path, "pages": []}
    
    with pdfplumber.open(path) as pdf:
        result["total_pages"] = len(pdf.pages)
        result["metadata"] = {
            "author": pdf.metadata.get("Author", ""),
            "title": pdf.metadata.get("Title", ""),
            "subject": pdf.metadata.get("Subject", ""),
        }
        
        pages_to_process = [page_num - 1] if page_num else range(len(pdf.pages))
        
        for i in pages_to_process:
            if 0 <= i < len(pdf.pages):
                page = pdf.pages[i]
                text = page.extract_text() or ""
                tables = page.extract_tables()
                result["pages"].append({
                    "page": i + 1,
                    "text": text,
                    "tables": tables if tables else []
                })
    
    return result


def main():
    parser = argparse.ArgumentParser(description="Extract text from PDF")
    parser.add_argument("path", help="Path to PDF file")
    parser.add_argument("--page", type=int, help="Specific page number (1-indexed)")
    parser.add_argument("--json", action="store_true", help="Output as JSON")
    args = parser.parse_args()
    
    result = extract_pdf(args.path, args.page)
    
    if args.json:
        print(json.dumps(result, indent=2, ensure_ascii=False))
    else:
        print(f"PDF: {result['path']}")
        print(f"Pages: {result['total_pages']}")
        if result['metadata']['title']:
            print(f"Title: {result['metadata']['title']}")
        print("-" * 50)
        for page in result["pages"]:
            print(f"\n--- Page {page['page']} ---\n")
            print(page["text"])
            if page["tables"]:
                print(f"\n[Found {len(page['tables'])} table(s)]")


if __name__ == "__main__":
    main()


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: clean (LOW)