PythonでPDF座標取得の完全ガイド|基本から実践まで詳しく解説

プログラミング・IT

「PDFファイルから特定のテキストや要素の位置を取得したい」「PDF内の座標情報を使って自動処理を行いたい」そんなニーズを持つ開発者の方は多いのではないでしょうか?

PDF座標取得は、文書解析やデータ抽出の自動化において重要な技術です。請求書の金額欄を自動で読み取ったり、フォームの入力位置を特定したりする際に欠かせない機能なんです。

今回は、Pythonを使ってPDFから座標情報を取得する方法を、初心者の方でも分かりやすく解説していきます。基本的な使い方から実践的な応用例まで、詳しくご紹介しますね。

スポンサーリンク

PDF座標取得の基本概念

PDF座標系の仕組み

PDFファイルでは、独特の座標系が使われています。

基本的な座標系

  • 原点(0, 0)は通常ページの左下角
  • X軸は右方向が正
  • Y軸は上方向が正
  • 単位はポイント(1ポイント = 1/72インチ)

実際のサイズ例

  • A4サイズ:約595 × 842ポイント
  • レターサイズ:約612 × 792ポイント
  • B5サイズ:約516 × 729ポイント

この座標系を理解することで、正確な位置情報を取得できるようになります。

座標取得が必要な場面

PDF座標取得は、以下のような場面で活用されています:

データ抽出の自動化

  • 請求書の金額や日付の自動読み取り
  • 契約書の署名欄の位置特定
  • 表組みデータの構造化抽出

フォーム処理

  • 入力フィールドの位置確認
  • チェックボックスの状態取得
  • ボタンやリンクの座標取得

実際に、多くの企業でこれらの技術が業務効率化に活用されています。

PyPDF2を使った基本的な座標取得

PyPDF2のインストールと基本設定

まずは、最も基本的なライブラリであるPyPDF2から始めましょう。

インストール方法

pip install PyPDF2

基本的なPDFファイルの読み込み

import PyPDF2

# PDFファイルを開く
with open('sample.pdf', 'rb') as file:
    pdf_reader = PyPDF2.PdfReader(file)
    
    # ページ数を確認
    num_pages = len(pdf_reader.pages)
    print(f"ページ数: {num_pages}")
    
    # 最初のページを取得
    first_page = pdf_reader.pages[0]

この基本コードで、PDFファイルの読み込みと基本情報の取得ができます。

テキストの座標情報を取得する方法

PyPDF2では、テキストとその位置情報を取得できます。

テキスト抽出と座標取得のコード例

def get_text_coordinates(pdf_path):
    with open(pdf_path, 'rb') as file:
        pdf_reader = PyPDF2.PdfReader(file)
        page = pdf_reader.pages[0]  # 最初のページ
        
        # ページサイズを取得
        page_box = page.mediabox
        width = float(page_box.width)
        height = float(page_box.height)
        
        print(f"ページサイズ: {width} × {height} ポイント")
        
        # テキストを抽出
        text = page.extract_text()
        print(f"抽出されたテキスト: {text}")
        
        return width, height, text

# 使用例
width, height, text = get_text_coordinates('sample.pdf')

ただし、PyPDF2では詳細な座標情報の取得に限界があるため、より高度な処理にはpdfplumberがおすすめです。

pdfplumberを使った高精度座標取得

pdfplumberの特徴とインストール

pdfplumberは、PDF座標取得において最も人気の高いライブラリです。

インストール方法

pip install pdfplumber

主な特徴

  • 高精度なテキスト座標取得
  • 表組みの自動認識
  • 画像や図形の位置情報取得
  • 直感的なAPI設計

詳細な座標情報を取得するコード

pdfplumberを使った具体的な座標取得方法をご紹介します。

基本的な座標取得コード

import pdfplumber

def extract_text_with_coordinates(pdf_path):
    with pdfplumber.open(pdf_path) as pdf:
        # 最初のページを処理
        page = pdf.pages[0]
        
        # ページサイズを取得
        width = page.width
        height = page.height
        print(f"ページサイズ: {width} × {height}")
        
        # テキストの座標情報を取得
        for char in page.chars:
            print(f"文字: '{char['text']}'")
            print(f"座標: x0={char['x0']:.2f}, y0={char['y0']:.2f}")
            print(f"     x1={char['x1']:.2f}, y1={char['y1']:.2f}")
            print(f"フォント: {char['fontname']}, サイズ: {char['size']}")
            print("---")

# 使用例
extract_text_with_coordinates('sample.pdf')

このコードにより、文字単位での詳細な位置情報が取得できます。

特定テキストの座標を検索する方法

目的のテキストを見つけて、その座標を取得する実用的な方法です。

特定テキスト検索のコード例

def find_text_coordinates(pdf_path, target_text):
    with pdfplumber.open(pdf_path) as pdf:
        for page_num, page in enumerate(pdf.pages):
            # ページ内のすべてのテキスト要素を確認
            for word in page.extract_words():
                if target_text in word['text']:
                    print(f"ページ {page_num + 1} で発見:")
                    print(f"テキスト: '{word['text']}'")
                    print(f"座標: x0={word['x0']:.2f}, y0={word['y0']:.2f}")
                    print(f"     x1={word['x1']:.2f}, y1={word['y1']:.2f}")
                    
                    return {
                        'page': page_num + 1,
                        'text': word['text'],
                        'x0': word['x0'],
                        'y0': word['y0'],
                        'x1': word['x1'],
                        'y1': word['y1']
                    }
    
    print(f"'{target_text}' は見つかりませんでした")
    return None

# 使用例
coordinates = find_text_coordinates('invoice.pdf', '合計金額')

この機能により、請求書の金額欄など、特定の項目を効率的に見つけられます。

PyMuPDFを使った高速座標取得

PyMuPDFの特徴

PyMuPDF(fitz)は、高速処理が特徴的なライブラリです。

インストール方法

pip install PyMuPDF

主な利点

  • 処理速度が非常に高速
  • 画像の座標取得も可能
  • ベクター形式の図形にも対応
  • メモリ使用量が効率的

PyMuPDFでの座標取得実装

高速な座標取得を実現するコード例をご紹介します。

基本的な実装コード

import fitz  # PyMuPDF

def get_coordinates_with_pymupdf(pdf_path):
    # PDFドキュメントを開く
    doc = fitz.open(pdf_path)
    
    # 最初のページを取得
    page = doc[0]
    
    # ページサイズを取得
    rect = page.rect
    print(f"ページサイズ: {rect.width} × {rect.height}")
    
    # テキストブロックとその座標を取得
    text_dict = page.get_text("dict")
    
    for block in text_dict["blocks"]:
        if "lines" in block:  # テキストブロックの場合
            for line in block["lines"]:
                for span in line["spans"]:
                    bbox = span["bbox"]  # バウンディングボックス
                    text = span["text"]
                    
                    print(f"テキスト: '{text}'")
                    print(f"座標: x0={bbox[0]:.2f}, y0={bbox[1]:.2f}")
                    print(f"     x1={bbox[2]:.2f}, y1={bbox[3]:.2f}")
                    print("---")
    
    doc.close()

# 使用例
get_coordinates_with_pymupdf('sample.pdf')

画像や図形の座標取得

PyMuPDFなら、テキスト以外の要素の座標も取得できます。

画像座標取得のコード

def get_image_coordinates(pdf_path):
    doc = fitz.open(pdf_path)
    page = doc[0]
    
    # 画像リストを取得
    image_list = page.get_images()
    
    for img_index, img in enumerate(image_list):
        # 画像の詳細情報を取得
        xref = img[0]
        bbox = page.get_image_bbox(img)
        
        print(f"画像 {img_index + 1}:")
        print(f"座標: x0={bbox.x0:.2f}, y0={bbox.y0:.2f}")
        print(f"     x1={bbox.x1:.2f}, y1={bbox.y1:.2f}")
        print(f"サイズ: {bbox.width:.2f} × {bbox.height:.2f}")
        print("---")
    
    doc.close()

# 使用例
get_image_coordinates('document_with_images.pdf')

実践的な応用例

請求書の金額自動抽出

実際のビジネスでよく使われる、請求書からの金額抽出システムです。

請求書解析のコード例

import pdfplumber
import re

def extract_invoice_amounts(pdf_path):
    amounts = []
    
    with pdfplumber.open(pdf_path) as pdf:
        for page in pdf.pages:
            # 金額パターンを検索(¥記号 + 数字 + カンマ)
            text = page.extract_text()
            
            # 正規表現で金額を検索
            amount_pattern = r'¥[\d,]+'
            found_amounts = re.findall(amount_pattern, text)
            
            # 座標情報付きで詳細検索
            for word in page.extract_words():
                if re.search(amount_pattern, word['text']):
                    amount_info = {
                        'text': word['text'],
                        'x': (word['x0'] + word['x1']) / 2,
                        'y': (word['y0'] + word['y1']) / 2,
                        'page': page.page_number
                    }
                    amounts.append(amount_info)
    
    return amounts

# 使用例
invoice_amounts = extract_invoice_amounts('invoice.pdf')
for amount in invoice_amounts:
    print(f"金額: {amount['text']}, 座標: ({amount['x']:.1f}, {amount['y']:.1f})")

表組みデータの構造化抽出

表形式のデータを座標情報を使って正確に抽出する方法です。

表データ抽出のコード

def extract_table_with_coordinates(pdf_path):
    with pdfplumber.open(pdf_path) as pdf:
        page = pdf.pages[0]
        
        # 表を自動検出
        tables = page.extract_tables()
        
        if tables:
            table = tables[0]  # 最初の表を処理
            
            print("表データと座標情報:")
            for row_index, row in enumerate(table):
                for col_index, cell in enumerate(row):
                    if cell:  # セルが空でない場合
                        # セルの座標を推定(簡易版)
                        print(f"行{row_index}, 列{col_index}: '{cell}'")
            
            # より詳細な座標情報が必要な場合
            table_settings = {
                "vertical_strategy": "lines",
                "horizontal_strategy": "lines"
            }
            
            detailed_table = page.extract_table(table_settings)
            return detailed_table
        
        return None

# 使用例
table_data = extract_table_with_coordinates('table_document.pdf')

座標変換とページ座標系の対応

異なる座標系への変換

PDF座標をピクセル座標や他の単位系に変換する方法です。

座標変換のコード例

def convert_coordinates(pdf_coords, page_height, dpi=72):
    """
    PDF座標を画面座標に変換
    PDFは左下が原点、画面は左上が原点
    """
    x_pdf, y_pdf = pdf_coords
    
    # Y座標を反転(左下原点 → 左上原点)
    y_screen = page_height - y_pdf
    
    # DPI変換(必要に応じて)
    if dpi != 72:  # 72 DPIがPDFの標準
        scale_factor = dpi / 72
        x_screen = x_pdf * scale_factor
        y_screen = y_screen * scale_factor
    else:
        x_screen = x_pdf
    
    return x_screen, y_screen

# 使用例
pdf_x, pdf_y = 100, 200
page_height = 842  # A4の高さ
screen_x, screen_y = convert_coordinates((pdf_x, pdf_y), page_height)
print(f"PDF座標: ({pdf_x}, {pdf_y}) → 画面座標: ({screen_x}, {screen_y})")

ページ回転への対応

回転されたPDFページでの座標補正方法です。

回転補正のコード

import math

def adjust_for_rotation(x, y, rotation, page_width, page_height):
    """
    ページ回転に対応した座標補正
    """
    if rotation == 0:
        return x, y
    elif rotation == 90:
        return y, page_width - x
    elif rotation == 180:
        return page_width - x, page_height - y
    elif rotation == 270:
        return page_height - y, x
    else:
        # 任意角度の回転(ラジアンに変換)
        rad = math.radians(rotation)
        new_x = x * math.cos(rad) - y * math.sin(rad)
        new_y = x * math.sin(rad) + y * math.cos(rad)
        return new_x, new_y

# 使用例
original_x, original_y = 100, 200
rotation_angle = 90  # 90度回転
page_w, page_h = 595, 842

adjusted_x, adjusted_y = adjust_for_rotation(
    original_x, original_y, rotation_angle, page_w, page_h
)
print(f"回転補正後の座標: ({adjusted_x}, {adjusted_y})")

エラー処理とデバッグ

よくあるエラーと対処法

PDF座標取得でよく発生する問題と解決方法をご紹介します。

エラー処理を含む実装例

def safe_coordinate_extraction(pdf_path):
    try:
        with pdfplumber.open(pdf_path) as pdf:
            if len(pdf.pages) == 0:
                raise ValueError("PDFにページが含まれていません")
            
            page = pdf.pages[0]
            
            # 座標取得処理
            coordinates = []
            for word in page.extract_words():
                coordinates.append({
                    'text': word['text'],
                    'x0': word['x0'],
                    'y0': word['y0'],
                    'x1': word['x1'],
                    'y1': word['y1']
                })
            
            return coordinates
            
    except FileNotFoundError:
        print(f"ファイルが見つかりません: {pdf_path}")
        return None
    except Exception as e:
        print(f"エラーが発生しました: {str(e)}")
        return None

# 使用例
result = safe_coordinate_extraction('sample.pdf')
if result:
    print(f"{len(result)}個の要素を取得しました")

デバッグ用の可視化

座標情報を視覚的に確認するためのコードです。

座標可視化のコード

import matplotlib.pyplot as plt
import matplotlib.patches as patches

def visualize_coordinates(pdf_path, output_image='coordinates.png'):
    with pdfplumber.open(pdf_path) as pdf:
        page = pdf.pages[0]
        
        # 図を作成
        fig, ax = plt.subplots(1, 1, figsize=(8, 11))
        
        # ページサイズに合わせて軸を設定
        ax.set_xlim(0, page.width)
        ax.set_ylim(0, page.height)
        ax.set_aspect('equal')
        
        # テキストの座標を可視化
        for word in page.extract_words():
            x0, y0, x1, y1 = word['x0'], word['y0'], word['x1'], word['y1']
            
            # バウンディングボックスを描画
            rect = patches.Rectangle(
                (x0, y0), x1-x0, y1-y0,
                linewidth=1, edgecolor='red', facecolor='none'
            )
            ax.add_patch(rect)
            
            # テキストを描画
            ax.text(x0, y0, word['text'], fontsize=6, ha='left', va='bottom')
        
        plt.title('PDF座標の可視化')
        plt.xlabel('X座標')
        plt.ylabel('Y座標')
        plt.savefig(output_image, dpi=150, bbox_inches='tight')
        plt.show()

# 使用例
visualize_coordinates('sample.pdf')

パフォーマンスの最適化

大容量PDFの効率的な処理

大きなPDFファイルを扱う際のパフォーマンス改善方法です。

最適化されたコード例

def optimized_coordinate_extraction(pdf_path, target_pages=None):
    """
    メモリ効率を考慮した座標取得
    """
    coordinates = []
    
    with pdfplumber.open(pdf_path) as pdf:
        pages_to_process = target_pages or range(len(pdf.pages))
        
        for page_num in pages_to_process:
            if page_num < len(pdf.pages):
                page = pdf.pages[page_num]
                
                # 必要な情報のみ抽出
                page_coords = []
                for word in page.extract_words():
                    # 座標のみを保存(メモリ節約)
                    page_coords.append((
                        word['text'],
                        round(word['x0'], 2),
                        round(word['y0'], 2),
                        round(word['x1'], 2),
                        round(word['y1'], 2)
                    ))
                
                coordinates.extend(page_coords)
                
                # ガベージコレクションを促進
                del page_coords
    
    return coordinates

# 使用例(最初の5ページのみ処理)
coords = optimized_coordinate_extraction('large_document.pdf', target_pages=range(5))

まとめ

PythonによるPDF座標取得は、文書処理の自動化において非常に強力なツールです。

PyPDF2、pdfplumber、PyMuPDFなど、それぞれに特徴があるライブラリを使い分けることで、様々なニーズに対応できます。基本的な座標取得ならpdfplumberが最も使いやすく、高速処理が必要ならPyMuPDFがおすすめです。

重要なのは、PDF座標系の仕組みを理解し、適切なエラー処理を行うことです。また、実際の業務で使用する際は、パフォーマンスとメモリ使用量も考慮する必要があります。

これらの技術を活用して、請求書処理や文書解析の自動化など、あなたの業務をより効率的にしていきましょう!

コメント

タイトルとURLをコピーしました