Skill: pengsc1994/free-pdf-processor (version 1.0.0)

--- name: pdf-processor description: | 一站式 PDF 处理技能。支持 PDF 文本/图片/表格提取、格式转换（PDF↔Word/Excel）、合并拆分、OCR 识别、批量处理、水印添加、加密解密、压缩等。使用场景： (1) 从 PDF 提取文本内容进行数据分析 (2) 将 PDF 转换为 Word/Excel 方便编辑 (3) 合并或拆分 PDF 文件 (4) 对扫描件进行 OCR 识别提取文字 (5) 批量处理多个 PDF 文件 (6) 添加水印或加密保护 PDF (7) 压缩 PDF 减小文件体积 --- # PDF 处理技能 ## 快速开始 ### 安装依赖 ```bash cd D:\PDF.skill\pdf-processor pip install -r requirements.txt ``` ### 核心功能 | 功能 | 命令 | 说明 | |------|------|------| | 提取文本 | `python scripts/extract_text.py <pdf_path>` | 提取 PDF 文本内容 | | 提取图片 | `python scripts/extract_images.py <pdf_path> <output_dir>` | 提取 PDF 中的图片 | | 提取表格 | `python scripts/extract_tables.py <pdf_path>` | 提取 PDF 中的表格 | | PDF 转 Word | `python scripts/pdf_to_word.py <pdf_path> <output_path>` | 转换为可编辑 Word | | PDF 转 Excel | `python scripts/pdf_to_excel.py <pdf_path> <output_path>` | 提取表格到 Excel | | 合并 PDF | `python scripts/merge_pdfs.py <output_path> <file1> <file2> ...` | 合并多个 PDF | | 拆分 PDF | `python scripts/split_pdf.py <pdf_path> <output_dir>` | 按页拆分 PDF | | 添加水印 | `python scripts/add_watermark.py <pdf_path> <output_path> <text>` | 添加文字水印 | | OCR 识别 | `python scripts/ocr_pdf.py <pdf_path> <output_path>` | OCR 识别扫描件 | | 加密 PDF | `python scripts/encrypt_pdf.py <input> <output> <password>` | AES-256 加密 | | 解密 PDF | `python scripts/decrypt_pdf.py <input> <output> <password>` | 解密 PDF | | 压缩 PDF | `python scripts/compress_pdf.py <input> <output>` | 压缩 PDF 文件 | | 批量处理 | `python scripts/batch_process.py <input_dir> <output_dir> --operation <op>` | 批量处理 | ## 功能详情 ### extract_text.py 提取 PDF 文本内容，支持： - 纯文本提取 - 保留段落结构 - 提取元数据（标题、作者、创建时间） ```bash python scripts/extract_text.py input.pdf -o output.txt --metadata ``` ### extract_tables.py 提取 PDF 表格数据： - 自动检测表格边框 - 支持合并单元格 - 输出为 Excel 文件 ### pdf_to_word.py PDF 转 Word 转换： - 保留原始格式 - 提取图片到 Word - 表格转换为 Word 表格 ### pdf_to_excel.py PDF 转 Excel： - 提取表格到不同 Sheet - 保留文本内容 ### add_watermark.py 水印功能： - 支持文字水印 - 可设置透明度、旋转角度、字体大小 - 支持批量添加 ### ocr_pdf.py OCR 识别（需要安装 Tesseract）： - 使用 Tesseract 进行中文识别 - 支持多种语言混合识别 - 保留原有 PDF 格式 ### encrypt_pdf.py / decrypt_pdf.py 加密解密： - AES-256 加密 - 支持用户密码和所有者密码 ### compress_pdf.py 压缩功能： - 清理未使用对象 - 压缩图片 - 5 个压缩级别可选 ### batch_process.py 批量处理： - 支持所有单文件操作 - 自动处理目录中所有 PDF - 生成处理报告 ## 使用示例 ### 从 PDF 提取文本 ``` 用户: 帮我提取这个合同的文本内容 AI: 使用 extract_text.py 脚本提取文本 ``` ### PDF 转 Word ``` 用户: 把这个 PDF 转成 Word 文档 AI: 使用 pdf_to_word.py 进行转换 ``` ### 批量加水印 ``` 用户: 给这个文件夹里所有 PDF 添加"内部资料"水印 AI: 使用 batch_process.py 批量处理 ``` ### 加密 PDF ``` 用户: 这个文件需要加密 AI: 使用 encrypt_pdf.py 进行 AES-256 加密 ``` ## 依赖安装 ### 基础依赖 ```bash pip install pymupdf pdfplumber python-docx openpyxl pillow ``` ### OCR 支持（可选） ```bash # 安装 Tesseract OCR # Windows: https://github.com/UB-Mannheim/tesseract/wiki # macOS: brew install tesseract # Linux: sudo apt install tesseract-ocr pip install pytesseract ``` ## 注意事项 - 加密 PDF 需要提供密码 - OCR 需要安装 Tesseract 引擎 - 大文件处理可能需要较长时间 - 转换效果取决于 PDF 原始质量

path: requirements.txt
content: # PDF 处理技能依赖
# 安装: pip install -r requirements.txt

# 核心 PDF 处理
pymupdf>=1.23.0
pdfplumber>=0.10.0

# Word/Excel 转换
python-docx>=1.1.0
openpyxl>=3.1.0

# 图片处理
Pillow>=10.0.0

# 可选: OCR 支持
# pytesseract>=0.3.10
# tessdata>=4.1.0

# 测试
pytest>=7.4.0
pytest-cov>=4.1.0


path: scripts/add_watermark.py
content: #!/usr/bin/env python3
"""
PDF 水印添加脚本
用法: python add_watermark.py <input.pdf> <output.pdf> <watermark_text> 
      [--opacity 0.3] [--rotation -45] [--font_size 48]
"""
import sys
import argparse
from pathlib import Path

try:
    import fitz  # PyMuPDF
except ImportError:
    print("Error: PyMuPDF not installed. Run: pip install pymupdf")
    sys.exit(1)


def add_watermark(
    input_path: str,
    output_path: str,
    watermark_text: str,
    opacity: float = 0.3,
    rotation: int = -45,
    font_size: int = 48
) -> bool:
    """为 PDF 添加水印"""
    doc = fitz.open(input_path)
    
    for page_num, page in enumerate(doc, 1):
        # 获取页面尺寸
        rect = page.rect
        
        # 创建水印文本
        text = watermark_text
        
        # 计算文本宽度（近似）
        # 使用标准字体
        font_name = "helv"
        
        # 创建水印文本对象
        text_point = fitz.Point(rect.width / 2, rect.height / 2)
        
        # 添加水印（每页重复）
        page.insert_text(
            text_point,
            text,
            fontsize=font_size,
            fontname=font_name,
            color=(0.8, 0.8, 0.8),
            opacity=opacity,
            rotate=rotation,
            align=1  # 居中
        )
        
        print(f"已添加水印到第 {page_num} 页")
    
    # 保存
    doc.save(output_path)
    doc.close()
    
    print(f"\n水印添加完成: {output_path}")
    return True


def main():
    parser = argparse.ArgumentParser(description='为 PDF 添加水印')
    parser.add_argument('input_path', help='输入 PDF 文件路径')
    parser.add_argument('output_path', help='输出 PDF 文件路径')
    parser.add_argument('watermark_text', help='水印文字')
    parser.add_argument('--opacity', '-o', type=float, default=0.3, help='透明度 (0-1)')
    parser.add_argument('--rotation', '-r', type=int, default=-45, help='旋转角度')
    parser.add_argument('--font_size', '-s', type=int, default=48, help='字体大小')
    
    args = parser.parse_args()
    
    try:
        add_watermark(
            args.input_path,
            args.output_path,
            args.watermark_text,
            args.opacity,
            args.rotation,
            args.font_size
        )
    except Exception as e:
        print(f"Error: {e}")
        sys.exit(1)


if __name__ == '__main__':
    main()

path: scripts/batch_process.py
content: #!/usr/bin/env python3
"""
PDF 批量处理脚本
用法: python batch_process.py <input_dir> <output_dir> --operation <operation> [options]
支持的操作:
  - extract_text: 提取文本
  - extract_images: 提取图片  
  - add_watermark: 添加水印
  - compress: 压缩 PDF
示例: 
  python batch_process.py ./input ./output --operation extract_text
  python batch_process.py ./input ./output --operation add_watermark --text "机密"
"""
import sys
import os
import argparse
from pathlib import Path

# 导入各功能模块
from extract_text import extract_text
from extract_images import extract_images
from add_watermark import add_watermark


def batch_process(input_dir: str, output_dir: str, operation: str, **options) -> dict:
    """批量处理 PDF 文件"""
    input_path = Path(input_dir)
    output_path = Path(output_dir)
    output_path.mkdir(parents=True, exist_ok=True)
    
    # 查找所有 PDF 文件
    pdf_files = list(input_path.glob("*.pdf"))
    
    if not pdf_files:
        print(f"在 {input_dir} 中未找到 PDF 文件")
        return {'success': 0, 'failed': 0}
    
    results = {'success': 0, 'failed': 0, 'details': []}
    
    for pdf_file in pdf_files:
        try:
            print(f"\n处理: {pdf_file.name}")
            
            output_file = output_path / pdf_file.name
            
            if operation == 'extract_text':
                # 文本输出到同名 .txt 文件
                output_txt = output_path / f"{pdf_file.stem}.txt"
                extract_text(str(pdf_file), str(output_txt), options.get('metadata', False))
            
            elif operation == 'extract_images':
                # 图片输出到同名子目录
                img_dir = output_path / pdf_file.stem
                extract_images(str(pdf_file), str(img_dir))
            
            elif operation == 'add_watermark':
                watermark_text = options.get('text', 'WATERMARK')
                add_watermark(str(pdf_file), str(output_file), watermark_text,
                             options.get('opacity', 0.3),
                             options.get('rotation', -45),
                             options.get('font_size', 48))
            
            elif operation == 'compress':
                # 压缩 PDF
                import fitz
                doc = fitz.open(str(pdf_file))
                # 压缩图片
                for page in doc:
                    for img in page.get_images():
                        # 简单压缩
                        pass
                doc.save(str(output_file), garbage=4, deflate=True)
                doc.close()
            
            else:
                print(f"Unknown operation: {operation}")
                continue
            
            results['success'] += 1
            print(f"✓ 完成: {pdf_file.name}")
            
        except Exception as e:
            results['failed'] += 1
            results['details'].append({'file': pdf_file.name, 'error': str(e)})
            print(f"✗ 失败: {pdf_file.name} - {e}")
    
    print(f"\n=== 批量处理完成 ===")
    print(f"成功: {results['success']}")
    print(f"失败: {results['failed']}")
    
    return results


def main():
    parser = argparse.ArgumentParser(description='PDF 批量处理')
    parser.add_argument('input_dir', help='输入目录')
    parser.add_argument('output_dir', help='输出目录')
    parser.add_argument('--operation', '-op', required=True, 
                       choices=['extract_text', 'extract_images', 'add_watermark', 'compress'],
                       help='要执行的操作')
    parser.add_argument('--text', '-t', help='水印文字 (用于 add_watermark)')
    parser.add_argument('--metadata', '-m', action='store_true', help='包含元数据')
    parser.add_argument('--opacity', '-o', type=float, default=0.3, help='水印透明度')
    parser.add_argument('--rotation', '-r', type=int, default=-45, help='水印旋转角度')
    parser.add_argument('--font_size', '-s', type=int, default=48, help='水印字体大小')
    
    args = parser.parse_args()
    
    options = {
        'text': args.text,
        'metadata': args.metadata,
        'opacity': args.opacity,
        'rotation': args.rotation,
        'font_size': args.font_size,
    }
    
    try:
        batch_process(args.input_dir, args.output_dir, args.operation, **options)
    except Exception as e:
        print(f"Error: {e}")
        sys.exit(1)


if __name__ == '__main__':
    main()

path: scripts/compress_pdf.py
content: #!/usr/bin/env python3
"""
PDF 压缩脚本
用法: python compress_pdf.py <input.pdf> <output.pdf> [--level 1-5]
"""
import sys
import argparse

try:
    import fitz  # PyMuPDF
except ImportError:
    print("Error: PyMuPDF not installed. Run: pip install pymupdf")
    sys.exit(1)


def compress_pdf(input_path: str, output_path: str, level: int = 2) -> bool:
    """压缩 PDF 文件"""
    doc = fitz.open(input_path)
    original_size = Path(input_path).stat().st_size
    
    # 保存选项
    # garbage: 清理未使用对象 (0-4)
    # deflate: 压缩内容流
    # clean: 清理 graphics 状态
    
    save_options = {
        'garbage': min(level, 4),
        'deflate': True,
        'clean': True
    }
    
    if level >= 3:
        save_options['compress_images'] = True
    
    doc.save(output_path, **save_options)
    doc.close()
    
    compressed_size = Path(output_path).stat().st_size
    ratio = (1 - compressed_size / original_size) * 100
    
    print(f"压缩完成: {output_path}")
    print(f"原始大小: {original_size / 1024:.1f} KB")
    print(f"压缩后: {compressed_size / 1024:.1f} KB")
    print(f"压缩率: {ratio:.1f}%")
    
    return True


def main():
    parser = argparse.ArgumentParser(description='压缩 PDF')
    parser.add_argument('input_path', help='输入 PDF 文件路径')
    parser.add_argument('output_path', help='输出 PDF 文件路径')
    parser.add_argument('--level', '-l', type=int, default=2, 
                       choices=[1, 2, 3, 4, 5], help='压缩级别 (1=低, 5=高)')
    
    args = parser.parse_args()
    
    try:
        compress_pdf(args.input_path, args.output_path, args.level)
    except Exception as e:
        print(f"Error: {e}")
        sys.exit(1)


if __name__ == '__main__':
    main()


path: scripts/decrypt_pdf.py
content: #!/usr/bin/env python3
"""
PDF 解密脚本
用法: python decrypt_pdf.py <encrypted.pdf> <output.pdf> <password>
"""
import sys
import argparse

try:
    import fitz  # PyMuPDF
except ImportError:
    print("Error: PyMuPDF not installed. Run: pip install pymupdf")
    sys.exit(1)


def decrypt_pdf(input_path: str, output_path: str, password: str) -> bool:
    """解密 PDF 文件"""
    # 打开加密的 PDF（需要密码）
    doc = fitz.open(input_path)
    
    # 验证密码
    if doc.is_encrypted:
        if not doc.authenticate(password):
            doc.close()
            print("Error: 密码错误")
            return False
    
    # 移除加密并保存
    doc.save(output_path, encryption=0)
    doc.close()
    
    print(f"解密完成: {output_path}")
    return True


def main():
    parser = argparse.ArgumentParser(description='解密 PDF')
    parser.add_argument('input_path', help='输入加密的 PDF 文件路径')
    parser.add_argument('output_path', help='输出 PDF 文件路径')
    parser.add_argument('password', help='解密密码')
    
    args = parser.parse_args()
    
    try:
        decrypt_pdf(args.input_path, args.output_path, args.password)
    except Exception as e:
        print(f"Error: {e}")
        sys.exit(1)


if __name__ == '__main__':
    main()


path: scripts/encrypt_pdf.py
content: #!/usr/bin/env python3
"""
PDF 加密脚本
用法: python encrypt_pdf.py <pdf_path> <output.pdf> <password>
"""
import sys
import argparse

try:
    import fitz  # PyMuPDF
except ImportError:
    print("Error: PyMuPDF not installed. Run: pip install pymupdf")
    sys.exit(1)


def encrypt_pdf(input_path: str, output_path: str, password: str) -> bool:
    """加密 PDF 文件"""
    doc = fitz.open(input_path)
    
    # 加密
    doc.save(
        output_path,
        encryption=fitz.PDF_ENCRYPT_AES_256,
        user_pw=password,
        owner_pw=password
    )
    
    doc.close()
    
    print(f"加密完成: {output_path}")
    print(f"密码: {password}")
    return True


def main():
    parser = argparse.ArgumentParser(description='加密 PDF')
    parser.add_argument('input_path', help='输入 PDF 文件路径')
    parser.add_argument('output_path', help='输出 PDF 文件路径')
    parser.add_argument('password', help='加密密码')
    
    args = parser.parse_args()
    
    try:
        encrypt_pdf(args.input_path, args.output_path, args.password)
    except Exception as e:
        print(f"Error: {e}")
        sys.exit(1)


if __name__ == '__main__':
    main()


path: scripts/extract_images.py
content: #!/usr/bin/env python3
"""
PDF 图片提取脚本
用法: python extract_images.py <pdf_path> <output_dir>
"""
import sys
import argparse
from pathlib import Path

try:
    import fitz  # PyMuPDF
except ImportError:
    print("Error: PyMuPDF not installed. Run: pip install pymupdf")
    sys.exit(1)


def extract_images(pdf_path: str, output_dir: str) -> list:
    """提取 PDF 中的所有图片"""
    doc = fitz.open(pdf_path)
    output_path = Path(output_dir)
    output_path.mkdir(parents=True, exist_ok=True)
    
    extracted_images = []
    
    for page_num, page in enumerate(doc, 1):
        image_list = page.get_images()
        
        for img_index, img in enumerate(image_list):
            xref = img[0]
            base_image = doc.extract_image(xref)
            
            image_ext = base_image["ext"]
            image_data = base_image["image"]
            
            # 生成文件名
            filename = f"page{page_num}_img{img_index + 1}.{image_ext}"
            filepath = output_path / filename
            
            # 保存图片
            with open(filepath, "wb") as f:
                f.write(image_data)
            
            extracted_images.append({
                'page': page_num,
                'index': img_index + 1,
                'filename': filename,
                'path': str(filepath),
                'size': len(image_data),
                'ext': image_ext
            })
            
            print(f"提取: 第{page_num}页 - 图片{img_index + 1} -> {filename}")
    
    doc.close()
    
    # 保存图片索引文件
    index_file = output_path / "images_index.json"
    import json
    with open(index_file, 'w', encoding='utf-8') as f:
        json.dump(extracted_images, f, ensure_ascii=False, indent=2)
    
    print(f"\n共提取 {len(extracted_images)} 张图片")
    print(f"索引文件: {index_file}")
    
    return extracted_images


def main():
    parser = argparse.ArgumentParser(description='提取 PDF 图片')
    parser.add_argument('pdf_path', help='PDF 文件路径')
    parser.add_argument('output_dir', help='输出目录')
    
    args = parser.parse_args()
    
    try:
        extract_images(args.pdf_path, args.output_dir)
    except FileNotFoundError:
        print(f"Error: 文件不存在 - {args.pdf_path}")
        sys.exit(1)
    except Exception as e:
        print(f"Error: {e}")
        sys.exit(1)


if __name__ == '__main__':
    main()


path: scripts/extract_tables.py
content: #!/usr/bin/env python3
"""
PDF 表格提取脚本
用法: python extract_tables.py <pdf_path> [--output <output.xlsx>]
"""
import sys
import argparse
from pathlib import Path

try:
    import fitz
    import pdfplumber
except ImportError:
    print("Error: Missing dependencies. Run: pip install pymupdf pdfplumber")
    sys.exit(1)


def extract_tables(pdf_path: str, output_path: str = None) -> list:
    """提取 PDF 表格数据"""
    tables_data = []
    
    with pdfplumber.open(pdf_path) as pdf:
        for page_num, page in enumerate(pdf.pages, 1):
            tables = page.extract_tables()
            
            if tables:
                for table_idx, table in enumerate(tables):
                    if table:  # 过滤空表格
                        tables_data.append({
                            'page': page_num,
                            'table_index': table_idx + 1,
                            'data': table
                        })
                        print(f"第{page_num}页 - 表格{table_idx + 1}: {len(table)}行 x {len(table[0]) if table else 0}列")
    
    # 如果指定了输出路径，保存为 Excel
    if output_path:
        try:
            import pandas as pd
            
            with pd.ExcelWriter(output_path, engine='openpyxl') as writer:
                for idx, table_info in enumerate(tables_data):
                    sheet_name = f"Page{table_info['page']}_Table{table_info['table_index']}"
                    df = pd.DataFrame(table_info['data'][1:], columns=table_info['data'][0])
                    df.to_excel(writer, sheet_name=sheet_name[:31], index=False)
            
            print(f"\n表格已保存到: {output_path}")
        except ImportError:
            print("Warning: pandas 未安装，仅输出原始数据")
    
    return tables_data


def main():
    parser = argparse.ArgumentParser(description='提取 PDF 表格')
    parser.add_argument('pdf_path', help='PDF 文件路径')
    parser.add_argument('--output', '-o', help='输出 Excel 文件路径')
    
    args = parser.parse_args()
    
    try:
        tables = extract_tables(args.pdf_path, args.output)
        print(f"\n共提取 {len(tables)} 个表格")
    except Exception as e:
        print(f"Error: {e}")
        sys.exit(1)


if __name__ == '__main__':
    main()

path: scripts/extract_text.py
content: #!/usr/bin/env python3
"""
PDF 文本提取脚本
用法: python extract_text.py <pdf_path> [--output <output.txt>] [--metadata]
"""
import sys
import argparse
from pathlib import Path

try:
    import fitz  # PyMuPDF
except ImportError:
    print("Error: PyMuPDF not installed. Run: pip install pymupdf")
    sys.exit(1)


def extract_text(pdf_path: str, output_path: str = None, include_metadata: bool = False) -> str:
    """提取 PDF 文本内容"""
    doc = fitz.open(pdf_path)
    
    text_content = []
    metadata_info = {}
    
    # 提取元数据
    if include_metadata:
        metadata = doc.metadata
        metadata_info = {
            'title': metadata.get('title', ''),
            'author': metadata.get('author', ''),
            'subject': metadata.get('subject', ''),
            'creator': metadata.get('creator', ''),
            'producer': metadata.get('producer', ''),
            'creation_date': metadata.get('creationDate', ''),
            'mod_date': metadata.get('modDate', ''),
            'page_count': len(doc),
        }
    
    # 提取每页文本
    for page_num, page in enumerate(doc, 1):
        text = page.get_text()
        if text.strip():
            text_content.append(f"--- 第 {page_num} 页 ---\n{text}")
    
    doc.close()
    
    result = ""
    if metadata_info:
        result += "=== PDF 元数据 ===\n"
        for key, value in metadata_info.items():
            if value:
                result += f"{key}: {value}\n"
        result += "\n"
    
    result += "\n\n".join(text_content)
    
    # 保存到文件或返回
    if output_path:
        Path(output_path).write_text(result, encoding='utf-8')
        print(f"文本已保存到: {output_path}")
    
    return result


def main():
    parser = argparse.ArgumentParser(description='提取 PDF 文本内容')
    parser.add_argument('pdf_path', help='PDF 文件路径')
    parser.add_argument('--output', '-o', help='输出文件路径')
    parser.add_argument('--metadata', '-m', action='store_true', help='包含元数据')
    
    args = parser.parse_args()
    
    try:
        text = extract_text(args.pdf_path, args.output, args.metadata)
        
        if not args.output:
            print(text)
        
        print(f"\n提取完成! 总字符数: {len(text)}")
        
    except FileNotFoundError:
        print(f"Error: 文件不存在 - {args.pdf_path}")
        sys.exit(1)
    except Exception as e:
        print(f"Error: {e}")
        sys.exit(1)


if __name__ == '__main__':
    main()


path: scripts/merge_pdfs.py
content: #!/usr/bin/env python3
"""
PDF 合并脚本
用法: python merge_pdfs.py <output.pdf> <file1.pdf> <file2.pdf> ...
"""
import sys
import argparse
from pathlib import Path

try:
    import fitz  # PyMuPDF
except ImportError:
    print("Error: PyMuPDF not installed. Run: pip install pymupdf")
    sys.exit(1)


def merge_pdfs(output_path: str, *pdf_files) -> bool:
    """合并多个 PDF 文件"""
    if not pdf_files:
        print("Error: 请提供至少一个 PDF 文件")
        return False
    
    # 创建输出 PDF
    output_doc = fitz.open()
    
    for pdf_path in pdf_files:
        try:
            doc = fitz.open(pdf_path)
            output_doc.insert_pdf(doc)
            doc.close()
            print(f"已合并: {Path(pdf_path).name}")
        except Exception as e:
            print(f"Warning: 无法合并 {pdf_path}: {e}")
    
    # 保存
    output_doc.save(output_path)
    output_doc.close()
    
    print(f"\n合并完成: {output_path}")
    print(f"总页数: {fitz.open(output_path).page_count}")
    return True


def main():
    parser = argparse.ArgumentParser(description='合并 PDF 文件')
    parser.add_argument('output', help='输出 PDF 文件路径')
    parser.add_argument('files', nargs='+', help='要合并的 PDF 文件列表')
    
    args = parser.parse_args()
    
    try:
        merge_pdfs(args.output, *args.files)
    except Exception as e:
        print(f"Error: {e}")
        sys.exit(1)


if __name__ == '__main__':
    main()

path: scripts/ocr_pdf.py
content: #!/usr/bin/env python3
"""
PDF OCR 识别脚本
用法: python ocr_pdf.py <pdf_path> <output.pdf> [--lang chi_sim+eng]
"""
import sys
import argparse
from pathlib import Path

try:
    import fitz
    import pytesseract
    from PIL import Image
except ImportError as e:
    print(f"Error: Missing dependency. {e}")
    print("Run: pip install pytesseract pillow pymupdf")
    print("\n注意: 还需要安装 Tesseract OCR 引擎")
    print("Windows: https://github.com/UB-Mannheim/tesseract/wiki")
    sys.exit(1)


def ocr_pdf(pdf_path: str, output_path: str, lang: str = 'chi_sim+eng') -> bool:
    """对 PDF 进行 OCR 识别"""
    # 检查 Tesseract 路径（Windows 常见位置）
    import os
    tesseract_paths = [
        r'C:\Program Files\Tesseract-OCR\tesseract.exe',
        r'C:\Program Files (x86)\Tesseract-OCR\tesseract.exe',
    ]
    
    for path in tesseract_paths:
        if os.path.exists(path):
            pytesseract.pytesseract.tesseract_cmd = path
            break
    
    # 打开 PDF
    input_doc = fitz.open(pdf_path)
    output_doc = fitz.open()
    
    for page_num, page in enumerate(input_doc, 1):
        print(f"处理第 {page_num} 页...")
        
        # 获取页面尺寸
        rect = page.rect
        
        # 将页面转换为图片
        mat = fitz.Matrix(2.0, 2.0)  # 2x 提高分辨率
        pix = page.get_pixmap(matrix=mat)
        
        # 转换为 PIL Image
        img = Image.frombytes("RGB", [pix.width, pix.height], pix.samples)
        
        # OCR 识别
        try:
            text = pytesseract.image_to_string(img, lang=lang)
        except Exception as e:
            print(f"  OCR 失败: {e}")
            text = f"[OCR 识别失败，请检查 Tesseract 安装]"
        
        # 创建新页面
        new_page = output_doc.new_page(width=rect.width, height=rect.height)
        
        # 插入原文图片
        new_page.insert_image(rect, pixmap=pix)
        
        # 在图片下方添加识别的文本
        text_height = 500  # 文本区域高度
        if rect.height > text_height:
            # 调整页面大小以容纳文本
            new_page = output_doc.new_page(width=rect.width, height=rect.height + text_height)
            new_page.insert_image(fitz.Rect(0, 0, rect.width, rect.height), pixmap=pix)
            
            # 添加文本
            text_rect = fitz.Rect(0, rect.height, rect.width, rect.height + text_height)
            new_page.insert_textbox(
                text_rect,
                text,
                fontsize=10,
                align=0
            )
        else:
            new_page.insert_image(rect, pixmap=pix)
    
    # 保存
    output_doc.save(output_path)
    output_doc.close()
    input_doc.close()
    
    print(f"OCR 完成: {output_path}")
    return True


def main():
    parser = argparse.ArgumentParser(description='PDF OCR 识别')
    parser.add_argument('pdf_path', help='输入 PDF 文件路径')
    parser.add_argument('output_path', help='输出 PDF 文件路径')
    parser.add_argument('--lang', '-l', default='chi_sim+eng', 
                       help='识别语言 (默认: chi_sim+eng)')
    
    args = parser.parse_args()
    
    try:
        ocr_pdf(args.pdf_path, args.output_path, args.lang)
    except Exception as e:
        print(f"Error: {e}")
        sys.exit(1)


if __name__ == '__main__':
    main()


path: scripts/pdf_to_excel.py
content: #!/usr/bin/env python3
"""
PDF 转 Excel 脚本
用法: python pdf_to_excel.py <pdf_path> <output.xlsx>
"""
import sys
import argparse
from pathlib import Path

try:
    import fitz
    import pdfplumber
    import pandas as pd
except ImportError as e:
    print(f"Error: Missing dependency. {e}")
    print("Run: pip install pymupdf pdfplumber pandas openpyxl")
    sys.exit(1)


def pdf_to_excel(pdf_path: str, output_path: str) -> bool:
    """将 PDF 转换为 Excel 文件"""
    output_file = Path(output_path)
    output_file.parent.mkdir(parents=True, exist_ok=True)
    
    with pd.ExcelWriter(output_path, engine='openpyxl') as writer:
        # 第一页：提取所有文本作为备注
        doc = fitz.open(pdf_path)
        all_text = []
        
        for page_num, page in enumerate(doc, 1):
            text = page.get_text().strip()
            if text:
                all_text.append(f"=== 第 {page_num} 页 ===\n{text}")
        
        if all_text:
            df_text = pd.DataFrame({'PDF文本内容': all_text})
            df_text.to_excel(writer, sheet_name='文本内容', index=False)
        
        doc.close()
        
        # 其他页：提取表格
        sheet_index = 1
        with pdfplumber.open(pdf_path) as pdf:
            for page_num, page in enumerate(pdf.pages, 1):
                tables = page.extract_tables()
                
                if tables:
                    for table_idx, table in enumerate(tables):
                        if table and len(table) > 1:
                            sheet_name = f"P{page_num}T{table_idx + 1}"
                            
                            # 处理表格数据
                            headers = table[0] if table else []
                            data = table[1:] if len(table) > 1 else []
                            
                            df = pd.DataFrame(data, columns=headers)
                            df.to_excel(writer, sheet_name=sheet_name[:31], index=False)
                            sheet_index += 1
        
        # 如果没有提取到任何表格，添加提示
        if sheet_index == 1:
            df_note = pd.DataFrame({'提示': ['此PDF未检测到表格数据，仅提取了文本内容']})
            df_note.to_excel(writer, sheet_name='说明', index=False)
    
    print(f"转换完成: {output_path}")
    return True


def main():
    parser = argparse.ArgumentParser(description='PDF 转 Excel')
    parser.add_argument('pdf_path', help='输入 PDF 文件路径')
    parser.add_argument('output_path', help='输出 Excel 文件路径')
    
    args = parser.parse_args()
    
    try:
        pdf_to_excel(args.pdf_path, args.output_path)
    except Exception as e:
        print(f"Error: {e}")
        sys.exit(1)


if __name__ == '__main__':
    main()


path: scripts/pdf_to_word.py
content: #!/usr/bin/env python3
"""
PDF 转 Word 脚本
用法: python pdf_to_word.py <pdf_path> <output.docx>
"""
import sys
import argparse
from pathlib import Path

try:
    import fitz
    from docx import Document
    from docx.shared import Inches, Pt, RGBColor
    from docx.enum.text import WD_ALIGN_PARAGRAPH
except ImportError:
    print("Error: Missing dependencies. Run: pip install pymupdf python-docx")
    sys.exit(1)


def pdf_to_word(pdf_path: str, output_path: str) -> bool:
    """将 PDF 转换为 Word 文档"""
    doc = Document()
    
    # 添加标题
    title = doc.add_heading(Path(pdf_path).stem, 0)
    title.alignment = WD_ALIGN_PARAGRAPH.CENTER
    
    pdf_doc = fitz.open(pdf_path)
    
    for page_num, page in enumerate(pdf_doc, 1):
        # 添加页码标题
        doc.add_heading(f"第 {page_num} 页", level=2)
        
        # 提取文本
        text = page.get_text()
        
        if text.strip():
            # 按段落添加文本
            for paragraph in text.split('\n'):
                if paragraph.strip():
                    p = doc.add_paragraph(paragraph)
        else:
            doc.add_paragraph(f"[第 {page_num} 页为图片或扫描件，建议使用 OCR 提取]")
        
        # 提取图片
        images = page.get_images()
        if images:
            for img_index, img in enumerate(images):
                xref = img[0]
                base_image = pdf_doc.extract_image(xref)
                image_data = base_image["image"]
                image_ext = base_image["ext"]
                
                # 临时保存图片
                temp_img = f"temp_img_{page_num}_{img_index}.{image_ext}"
                with open(temp_img, "wb") as f:
                    f.write(image_data)
                
                # 添加到 Word
                try:
                    doc.add_picture(temp_img, width=Inches(6))
                    # 删除临时文件
                    Path(temp_img).unlink()
                except Exception as e:
                    print(f"Warning: 无法添加图片 {img_index + 1}: {e}")
    
    pdf_doc.close()
    
    # 保存 Word 文档
    doc.save(output_path)
    print(f"转换完成: {output_path}")
    return True


def main():
    parser = argparse.ArgumentParser(description='PDF 转 Word')
    parser.add_argument('pdf_path', help='输入 PDF 文件路径')
    parser.add_argument('output_path', help='输出 Word 文件路径')
    
    args = parser.parse_args()
    
    try:
        pdf_to_word(args.pdf_path, args.output_path)
    except Exception as e:
        print(f"Error: {e}")
        sys.exit(1)


if __name__ == '__main__':
    main()

path: scripts/split_pdf.py
content: #!/usr/bin/env python3
"""
PDF 拆分脚本
用法: python split_pdf.py <pdf_path> <output_dir> [--pages <page_ranges>]
示例: python split_pdf.py input.pdf outputdir --pages 1-3,5,7-10
"""
import sys
import argparse
from pathlib import Path

try:
    import fitz  # PyMuPDF
except ImportError:
    print("Error: PyMuPDF not installed. Run: pip install pymupdf")
    sys.exit(1)


def parse_page_ranges(page_str: str, max_pages: int) -> list:
    """解析页码范围，如 '1-3,5,7-10' -> [0,1,2,4,6,7,8,9]"""
    pages = []
    parts = page_str.split(',')
    
    for part in parts:
        part = part.strip()
        if '-' in part:
            start, end = part.split('-')
            pages.extend(range(int(start) - 1, int(end)))
        else:
            pages.append(int(part) - 1)
    
    # 过滤无效页码
    return [p for p in pages if 0 <= p < max_pages]


def split_pdf(pdf_path: str, output_dir: str, page_ranges: str = None) -> list:
    """拆分 PDF 文件"""
    doc = fitz.open(pdf_path)
    total_pages = len(doc)
    
    output_path = Path(output_dir)
    output_path.mkdir(parents=True, exist_ok=True)
    
    base_name = Path(pdf_path).stem
    split_files = []
    
    if page_ranges:
        # 按指定范围拆分
        page_indices = parse_page_ranges(page_ranges, total_pages)
        
        new_doc = fitz.open()
        for idx in page_indices:
            new_doc.insert_pdf(doc, from_page=idx, to_page=idx)
        
        output_file = output_path / f"{base_name}_selected.pdf"
        new_doc.save(output_file)
        new_doc.close()
        
        split_files.append(str(output_file))
        print(f"已保存选中页面: {output_file.name} ({len(page_indices)}页)")
    else:
        # 每页单独拆分为一个文件
        for page_num in range(total_pages):
            new_doc = fitz.open()
            new_doc.insert_pdf(doc, from_page=page_num, to_page=page_num)
            
            output_file = output_path / f"{base_name}_page{page_num + 1}.pdf"
            new_doc.save(output_file)
            new_doc.close()
            
            split_files.append(str(output_file))
            print(f"已拆分: 第{page_num + 1}页 -> {output_file.name}")
    
    doc.close()
    
    print(f"\n拆分完成! 共 {len(split_files)} 个文件")
    return split_files


def main():
    parser = argparse.ArgumentParser(description='拆分 PDF 文件')
    parser.add_argument('pdf_path', help='输入 PDF 文件路径')
    parser.add_argument('output_dir', help='输出目录')
    parser.add_argument('--pages', '-p', help='要提取的页码范围，如 1-3,5,7-10')
    
    args = parser.parse_args()
    
    try:
        split_pdf(args.pdf_path, args.output_dir, args.pages)
    except Exception as e:
        print(f"Error: {e}")
        sys.exit(1)


if __name__ == '__main__':
    main()

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