mcpField
P
pdf-processing

pdf-processing

github.com/pixel-process-ug文档处理381 次安装33 周前更新
代码仓库

关于 pdf-processing

面向 Python 的 PDF 处理技能,可生成、提取和修改 PDF 文档。它帮助开发者为 OCR、表格提取、表单填写、合并和水印等任务选择库并验证处理流程。

主要功能

  1. 01使用 ReportLab 生成 PDF
  2. 02使用 pdfplumber 提取文本和表格
  3. 03使用 pypdf 合并、拆分和编辑元数据
  4. 04对扫描文档执行 OCR
  5. 05填写表单并添加水印
  6. 06验证输出并处理 PDF 边缘情况

适用场景

  1. 01创建格式化 PDF 报告
  2. 02从 PDF 提取文本或表格
  3. 03通过 OCR 处理扫描文档
  4. 04合并文件或填写 PDF 表单

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/pixel-process-ug/superkit-agents --skill pdf-processing

安装后在 Claude Code 中输入/pdf-processing确认已加载。

详细介绍

PDF 处理

概述

生成、操作 PDF 文档并从中提取数据。本技能涵盖 Python PDF 生态系统:用于合并、拆分和处理元数据的 pypdf,用于文本和表格提取的 pdfplumber,用于生成的 reportlab,用于 OCR 的 pytesseract,以及表单填写、水印添加和复杂文档组装策略。

只要需要通过代码创建、解析、转换或合并 PDF,就应用本技能。

多阶段流程

阶段 1:需求分析
  1. 确定操作类型(生成、提取、操作)
  2. 识别输入 PDF 的特征(扫描件、数字版、表单)
  3. 定义输出要求(格式、质量、大小)
  4. 规划数据处理流程(从源数据到 PDF,或从 PDF 到数据)
  5. 评估数据量和性能要求

停止 — 在明确操作类型和输入特征之前,不要选择库。

阶段 2:实现
  1. 为任务选择合适的库(参见决策表)
  2. 实现核心处理逻辑
  3. 处理边缘情况(文件损坏、PDF 已加密、内容混杂)
  4. 添加错误处理和验证
  5. 优化文件大小和处理速度

停止 — 不要跳过对已加密、旋转或扫描版 PDF 的边缘情况处理。

阶段 3:验证
  1. 确认输出在多个 PDF 查看器中都能正确渲染
  2. 在适用情况下,检查文本是否可选中(而非栅格化)
  3. 验证提取数据的准确性
  4. 使用边缘情况 PDF 进行测试(大型、已加密、扫描版)
  5. 验证可访问性(必要时使用带标签的 PDF)

库选择决策表

任务库原因替代方案
文本提取pdfplumber准确度最高,可处理布局pypdf(更简单,但准确度较低)
表格提取pdfplumber可解析结构化表格camelot(专用表格工具)
PDF 生成reportlab完全可控,品质专业weasyprint(HTML 转 PDF)
合并 / 拆分pypdf简单、可靠、快速—
表单填写pypdf可读取并填写 AcroFormspdfrw(替代 API)
元数据读写pypdf读写 PDF 属性—
OCR(扫描文档)pytesseract + pdf2image提取扫描文档中的文本EasyOCR(深度学习)
添加水印pypdf + reportlab叠加页面—
HTML 转 PDFweasyprint基于 CSS 的布局,适合服务器环境playwright(浏览器渲染)

使用 ReportLab 生成 PDF

from reportlab.lib.pagesizes import A4
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.units import cm, mm
from reportlab.lib.colors import HexColor
from reportlab.platypus import (
    SimpleDocTemplate, Paragraph, Spacer, Table,
    TableStyle, Image, PageBreak
)
from reportlab.lib import colors

def generate_report(output_path, data):
    doc = SimpleDocTemplate(
        output_path,
        pagesize=A4,
        topMargin=2.5*cm,
        bottomMargin=2.5*cm,
        leftMargin=2.5*cm,
        rightMargin=2.5*cm,
    )

    styles = getSampleStyleSheet()
    styles.add(ParagraphStyle(
        name='CustomTitle',
        parent=styles['Title'],
        fontSize=24,
        textColor=HexColor('#2F5496'),
        spaceAfter=20,
    ))

    story = []

    # Title
    story.append(Paragraph(data['title'], styles['CustomTitle']))
    story.append(Spacer(1, 12))

    # Body text
    story.append(Paragraph(data['body'], styles['Normal']))
    story.append(Spacer(1, 20))

    # Table
    table_data = [['Name', 'Value', 'Status']]
    for row in data['rows']:
        table_data.append([row['name'], row['value'], row['status']])

    table = Table(table_data, colWidths=[6*cm, 4*cm, 4*cm])
    table.setStyle(TableStyle([
        ('BACKGROUND', (0, 0), (-1, 0), HexColor('#2F5496')),
        ('TEXTCOLOR', (0, 0), (-1, 0), colors.white),
        ('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
        ('FONTSIZE', (0, 0), (-1, 0), 11),
        ('ALIGN', (0, 0), (-1, -1), 'CENTER'),
        ('GRID', (0, 0), (-1, -1), 0.5, colors.grey),
        ('ROWBACKGROUNDS', (0, 1), (-1, -1), [colors.white, HexColor('#F0F4FA')]),
        ('TOPPADDING', (0, 0), (-1, -1), 8),
        ('BOTTOMPADDING', (0, 0), (-1, -1), 8),
    ]))
    story.append(table)

    doc.build(story)
自定义页面模板(页眉/页脚)
from reportlab.platypus import BaseDocTemplate, Frame, PageTemplate
from datetime import datetime

def add_header_footer(canvas, doc):
    canvas.saveState()
    # Header
    canvas.setFont('Helvetica', 9)
    canvas.setFillColor(HexColor('#888888'))
    canvas.drawString(2.5*cm, A4[1] - 1.5*cm, 'Company Name — Confidential')
    canvas.drawRightString(A4[0] - 2.5*cm, A4[1] - 1.5*cm, f'Page {doc.page}')
    # Footer
    canvas.drawCentredString(A4[0]/2, 1.5*cm, f'Generated on {datetime.now():%Y-%m-%d}')
    canvas.restoreState()

doc = BaseDocTemplate(output_path, pagesize=A4)
frame = Frame(2.5*cm, 2.5*cm, A4[0]-5*cm, A4[1]-5*cm)
doc.addPageTemplates([PageTemplate(id='main', frames=[frame], onPage=add_header_footer)])

文本和表格提取

pdfplumber
import pdfplumber

with pdfplumber.open('document.pdf') as pdf:
    # Extract text from all pages
    full_text = ''
    for page in pdf.pages:
        full_text += page.extract_text() + '\n'

    # Extract tables
    for page in pdf.pages:
        tables = page.extract_tables()
        for table in tables:
            for row in table:
                print(row)

    # Extract text from specific area
    page = pdf.pages[0]
    bbox = (50, 100, 400, 300)  # (x0, top, x1, bottom)
    cropped = page.within_bbox(bbox)
    text = cropped.extract_text()
表格提取设置
table_settings = {
    "vertical_strategy": "lines",    # or "text", "explicit"
    "horizontal_strategy": "lines",
    "snap_tolerance": 3,
    "join_tolerance": 3,
    "edge_min_length": 3,
    "min_words_vertical": 3,
    "min_words_horizontal": 1,
}

tables = page.extract_tables(table_settings)

表单填写

from pypdf import PdfReader, PdfWriter

reader = PdfReader('form.pdf')
writer = PdfWriter()
writer.append(reader)

# Fill form fields
writer.update_page_form_field_values(
    writer.pages[0],
    {
        'full_name': 'Alice Johnson',
        'email': '[email protected]',
        'date': '2025-03-15',
        'agree_terms': '/Yes',  # Checkbox
    },
    auto_regenerate=False,
)

with open('filled_form.pdf', 'wb') as f:
    writer.write(f)

OCR(扫描版 PDF)

from pdf2image import convert_from_path
import pytesseract

def ocr_pdf(pdf_path, language='eng'):
    images = convert_from_path(pdf_path, dpi=300)
    full_text = ''
    for i, image in enumerate(images):
        text = pytesseract.image_to_string(image, lang=language)
        full_text += f'\n--- Page {i+1} ---\n{text}'
    return full_text

# For better accuracy with specific layouts:
def ocr_with_config(image):
    custom_config = r'--oem 3 --psm 6'  # LSTM engine, assume uniform block
    return pytesseract.image_to_string(image, config=custom_config)

合并和拆分

from pypdf import PdfReader, PdfWriter

# Merge multiple PDFs
def merge_pdfs(input_paths, output_path):
    writer = PdfWriter()
    for path in input_paths:
        reader = PdfReader(path)
        for page in reader.pages:
            writer.add_page(page)
    with open(output_path, 'wb') as f:
        writer.write(f)

# Split PDF by page ranges
def split_pdf(input_path, ranges, output_dir):
    reader = PdfReader(input_path)
    for i, (start, end) in enumerate(ranges):
        writer = PdfWriter()
        for page_num in range(start - 1, min(end, len(reader.pages))):
            writer.add_page(reader.pages[page_num])
        with open(f'{output_dir}/part_{i+1}.pdf', 'wb') as f:
            writer.write(f)

# Extract specific pages
def extract_pages(input_path, page_numbers, output_path):
    reader = PdfReader(input_path)
    writer = PdfWriter()
    for num in page_numbers:
        writer.add_page(reader.pages[num - 1])
    with open(output_path, 'wb') as f:
        writer.write(f)

添加水印

from pypdf import PdfReader, PdfWriter
from reportlab.pdfgen import canvas as rl_canvas
from reportlab.lib.pagesizes import A4
from io import BytesIO

def create_watermark(text, opacity=0.1):
    buffer = BytesIO()
    c = rl_canvas.Canvas(buffer, pagesize=A4)
    c.setFillAlpha(opacity)
    c.setFont('Helvetica-Bold', 60)
    c.setFillColorRGB(0.5, 0.5, 0.5)
    c.translate(A4[0]/2, A4[1]/2)
    c.rotate(45)
    c.drawCentredString(0, 0, text)
    c.save()
    buffer.seek(0)
    return PdfReader(buffer)

def apply_watermark(input_path, output_path, watermark_text):
    watermark = create_watermark(watermark_text)
    reader = PdfReader(input_path)
    writer = PdfWriter()

    for page in reader.pages:
        page.merge_page(watermark.pages[0])
        writer.add_page(page)

    with open(output_path, 'wb') as f:
        writer.write(f)

元数据处理

from pypdf import PdfReader, PdfWriter

# Read metadata
reader = PdfReader('document.pdf')
info = reader.metadata
print(f'Title: {info.title}')
print(f'Author: {info.author}')
print(f'Pages: {len(reader.pages)}')

# Write metadata
writer = PdfWriter()
writer.append(reader)
writer.add_metadata({
    '/Title': 'Updated Title',
    '/Author': 'Author Name',
    '/Subject': 'Document Subject',
    '/Creator': 'My Application',
})
with open('updated.pdf', 'wb') as f:
    writer.write(f)

反模式 / 常见错误

反模式失败原因建议做法
对数字版(基于文本的)PDF 执行 OCR文本已可提取时,OCR 速度慢且准确度低先检查能否提取文本,仅在结果为空时执行 OCR
未处理已加密的 PDF导致崩溃或静默失败检测加密状态,提示输入密码或妥善跳过
将整个大型 PDF 加载到内存导致服务器内存耗尽按页流式处理或分块处理
忽略页面旋转元数据提取的文本会变得乱码提取前读取并应用旋转信息
硬编码页面尺寸无法处理非 A4 文档从源 PDF 读取尺寸
未关闭文件句柄长时间运行的进程会发生资源泄漏使用上下文管理器(with 语句)
生成后未在多个查看器中测试不同查看器的渲染效果有差异在 Adobe Reader、Preview 和 Chrome 中测试
提取表格时未调整设置列对齐效果差,单元格可能合并针对不同文档类型调整 table_settings

反合理化防护

  • 不要在尝试直接提取文本之前使用 OCR——先检查 PDF 类型。
  • 不要跳过加密检测——即使“大多数 PDF 都未加密”,也要明确处理。
  • 不要假设页面尺寸为 A4——从源文档中读取尺寸。
  • 不要只在一个 PDF 查看器中测试——Adobe、Preview 和 Chrome 的渲染效果各不相同。
  • 不要在处理大型 PDF 时忽略内存占用——采用节省内存的模式(流式处理、分块)。

集成点

技能关联方式
docx-processingDOCX 转 PDF 流程,或格式选择
xlsx-processingExcel 中的数据填充 PDF 报告表格
email-composer将生成的 PDF 附加到专业邮件中
content-research-writer将研究结果排版为 PDF 白皮书
file-organizer输出文件命名和目录结构规范
deployment服务器/CI 环境中的 PDF 生成流程

技能类型

灵活——根据具体的 PDF 任务选择合适的库和方法。生成时使用 ReportLab,提取时使用 pdfplumber,处理时使用 pypdf。可按需组合。

评论

支持 Markdown