mcpField
P
pdf-processing

pdf-processing

github.com/pixel-process-ugドキュメント381 回インストール33 週間前に更新
リポジトリ

pdf-processing について

PDF の生成、抽出、編集を行う Python 向けスキルです。OCR、表の抽出、フォーム入力、結合、透かしなどの処理に適したライブラリを選び、手順を検証できます。

主な機能

  1. 01ReportLab で PDF を生成
  2. 02pdfplumber でテキストや表を抽出
  3. 03pypdf で結合、分割、メタデータ編集
  4. 04スキャン文書を OCR 処理
  5. 05フォーム入力と透かし追加
  6. 06出力を検証し PDF の例外に対応

活用シーン

  1. 01書式付き PDF レポートを作成
  2. 02PDF からテキストや表を抽出
  3. 03OCR でスキャン文書を処理
  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シンプルで、信頼性が高く、高速—
フォーム入力pypdfAcroForms の読み取りと入力pdfrw(代替 API)
メタデータの読み書きpypdfPDF プロパティの読み書き—
OCR(スキャン文書)pytesseract + pdf2imageスキャン文書からのテキスト抽出EasyOCR(ディープラーニング)
透かしの追加pypdf + reportlabページを重ね合わせる—
HTML から PDF への変換weasyprintCSS ベースのレイアウトで、サーバーに適しています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 を実行します
暗号化された PDF を処理しないクラッシュや、気付かないまま処理に失敗する原因になります暗号化を検出し、パスワードを求めるか、適切にスキップします
大容量 PDF 全体をメモリに読み込むサーバーでメモリ不足になりますページをストリーミングするか、チャンク単位で処理します
ページの回転メタデータを無視するテキスト抽出結果が文字化けします抽出前に回転を読み取り、適用します
ページサイズをハードコードするA4 以外の文書で動作しなくなります元の PDF からページサイズを読み取ります
ファイルハンドルを閉じない長時間実行するプロセスでリソースリークが発生しますコンテキストマネージャー(with 文)を使用します
複数のビューアーでテストせずに生成するビューアーによって表示に差異が生じますAdobe Reader、Preview、Chrome でテストします
設定を調整せずに表を抽出する列の位置ずれやセルの結合が正しく処理されませんドキュメントの種類ごとに table_settings を調整します

不合理な正当化を防ぐための注意事項

  • 直接テキストを抽出してみる前に、OCRを使用しないでください。まずPDFの種類を確認してください。
  • 暗号化の検出を省略しないでください。「ほとんどのPDFは暗号化されていない」場合でも、明示的に対処してください。
  • 用紙サイズをA4と決めつけないでください。元の文書から寸法を読み取ってください。
  • PDFビューアーを1つだけ使ってテストしないでください。Adobe、Preview、Chromeではレンダリングが異なります。
  • メモリに配慮した方法(ストリーミング、チャンク化)を使わずに、大きなPDFを処理しないでください。

連携ポイント

スキル連携方法
docx-processingDOCXからPDFへの変換パイプライン、または形式の選択
xlsx-processingExcelのデータをPDFレポートの表に反映
email-composer生成したPDFをビジネスメールに添付
content-research-writer調査結果をPDFのホワイトペーパーとして書式設定
file-organizer出力ファイルの命名規則とディレクトリ構成
deploymentサーバー/CI環境でのPDF生成パイプライン

スキルの種類

FLEXIBLE — PDFの具体的なタスクに応じて、適切なライブラリと方法を選択してください。生成にはReportLab、抽出にはpdfplumber、操作にはpypdfを使用します。必要に応じて組み合わせてください。

コメント