VBAでPDFから座標を取得する完全ガイド:Adobe Reader API、iTextSharp & Acrobat SDK比較

イントロダクション

PDF に埋め込まれたテキストや図形の座標情報を取得したいとき、
「VBA で一連の作業を自動化できないのでは?」という疑問がよくあります。
その実現方法はさまざまですが、主に 3 つのアプローチがあります。

方法 主な利点 主な欠点
Adobe Acrobat API 公式サポート、Windows COM サポート、詳細なオブジェクト階層にアクセス ライセンスが必要、ドキュメントが古い
iTextSharp(.NET ライブラリ) 無償(AGPL)で豊富な機能、単体実行可能 COM での連携に工夫が必要
Acrobat SDK + C# / VB.NET 公式サポート、低レベル操作が可能 参照設定が複雑、.NET の導入が必要

以下では、VBA で PDF から座標を簡単に取得する手順を、
Adobe Reader API、iTextSharp、Acrobat SDK それぞれの観点から解説します。
実際に使えるサンプルコードと、性能・メンテナンス性の比較ポイントも合わせて紹介します。


1. まずは PDF の座標情報とは何か?

PDF には「ページレベル」「オブジェクトレベル」「テキストレベル」など
階層構造があり、それぞれで座標情報を格納しています。

階層 主なオブジェクト 座標情報
ページ Content Stream (BT, ET, Td, Tm) ページ上のテキスト・図形の位置
オブジェクト XObject (/Image, /Form) 画像やフォームの位置とサイズ
マークアップ Annotation (/Annot) 注釈の位置(ハイライト、コメント)

VBA からは ページレベル のテキスト座標が最も利用されるケースですが、
画像や注釈座標も取得したい場合はさらに詳細な API が必要です。


2. Adobe Reader API(Acrobat オブジェクトモデル)で座標を取得

2.1. 必要な参照設定

  1. Excel を開く
  2. Alt + F11 で VBA エディタを起動
  3. 「ツール」→「参照設定」
  4. Adobe Acrobat xx.x Type Library をチェック
  5. OK

注意: Adobebrowser などの UI コンポーネントを使う場合は
追加で Microsoft Edge WebView2 Runtime が必要になる場合があります。

2.2. コードサンプル:ページ上のテキスト座標を取得

Option Explicit

' ページ番号 (1-indexed) とテキストを渡すと座標が返ってくる
Function GetTextPositions(pdfPath As String, pageNumber As Long, searchText As String) As Variant
    Dim AcroApp As Acrobat.AcroApp
    Dim AcroAVDoc As Acrobat.AcroAVDoc
    Dim AcroPDDoc As Acrobat.AcroPDDoc
    Dim AcroPDPage As Acrobat.AcroPDPage
    Dim AcroPDPageDictionary As Acrobat.AcroPDPageContent
    Dim TextFind As Acrobat.AcroPDTextSelect
    Dim i As Long, n As Long
    Dim result() As Variant
    Dim pos As String
    
    ' 初期化
    Set AcroApp = CreateObject("AcroExch.App")
    AcroApp.Show
    
    Set AcroAVDoc = CreateObject("AcroExch.AVDoc")
    If Not AcroAVDoc.Open(pdfPath, "") Then
        MsgBox "PDF を開けませんでした: " & pdfPath
        Exit Function
    End If
    
    Set AcroPDDoc = AcroAVDoc.GetPDDoc
    Set AcroPDPage = AcroPDDoc.AcquirePage(pageNumber - 1) ' 0-indexed
    
    Set TextFind = AcroPDPage.CreatePageHilite(searchText)
    If TextFind Is Nothing Then
        MsgBox "テキストが見つかりませんでした: " & searchText
        Exit Function
    End If
    
    n = TextFind.GetNumText
    If n = 0 Then Exit Function
    
    ReDim result(1 To n, 1 To 4) ' x, y, width, height
    
    For i = 0 To n - 1
        pos = TextFind.GetText(i)
        result(i + 1, 1) = TextFind.GetRect(i).Left
        result(i + 1, 2) = TextFind.GetRect(i).Top
        result(i + 1, 3) = TextFind.GetRect(i).Right - TextFind.GetRect(i).Left
        result(i + 1, 4) = TextFind.GetRect(i).Bottom - TextFind.GetRect(i).Top
    Next i
    
    GetTextPositions = result
    
Cleanup:
    Set TextFind = Nothing
    AcroPDDoc.ReleasePage pageNumber - 1
    AcroAVDoc.Close True
    AcroApp.Exit
End Function

使い方

Sub Demo()
    Dim pos, i
    pos = GetTextPositions("C:\temp\sample.pdf", 1, "Hello")
    For i = 1 To UBound(pos, 1)
        Debug.Print "X:" & pos(i, 1) & " Y:" & pos(i, 2) & " W:" & pos(i, 3) & " H:" & pos(i, 4)
    Next
End Sub

補足

  • AcroPDPage.CreatePageHilite正規表現 を使うことも可能です。
  • ページ全体を走査したい場合は AcroPDPage.GetPageContent を呼びます。
  • 座標は PDF の 標準座標系(ページ左下が (0,0))で返ります。
  • AcroApp.Show を呼ばない場合、オブジェクトが可視化されませんが
    速度はわずかに向上します。

3. iTextSharp を COM インターフェースで呼び出す

iTextSharp は .NET バインディングのみで提供されるため、VBA から直接呼び出すには クラスライブラリ を COM 化する必要があります。
以下では VBA から C# で作った COM ラッパー を利用するパターンを示します。

3.1. C# で COM ラッパーを作成

// iTextSharpWrapper.cs
using System;
using System.Runtime.InteropServices;
using iText.Kernel.Pdf;
using iText.Kernel.Pdf.Canvas.Parser;

namespace iTextSharpWrapper
{
    [ComVisible(true)]
    [Guid("8A9D3A6E-5C5F-4E2F-90C4-2A0CFB7E6B4D")]
    [ClassInterface(ClassInterfaceType.None)]
    [ProgId("iTextSharpWrapper.PdfProcessor")]
    public class PdfProcessor : IPdfProcessor
    {
        public double[] GetTextPosition(string pdfPath, string searchText)
        {
            PdfDocument pdfDoc = new PdfDocument(new PdfReader(pdfPath));
            for (int i = 1; i <= pdfDoc.GetNumberOfPages(); i++)
            {
                var page = pdfDoc.GetPage(i);
                var strategy = new TextLocationStrategy();
                PdfCanvasProcessor processor = new PdfCanvasProcessor(strategy);
                processor.ProcessPageContent(page);
                if (strategy.Contains(searchText))
                {
                    return new double[] {
                        strategy.Left, strategy.Top, strategy.Width, strategy.Height
                    };
                }
            }
            throw new Exception("Text not found");
        }
    }

    // TextLocationStrategy は iText で拡張する独自戦略
    class TextLocationStrategy : ITextExtractionStrategy
    {
        public double Left, Top, Width, Height;
        // 実装省略(全文取得で位置情報を取得)
        // ...
    }
}
  • COM を有効にするために ClassInterfaceProgId を設定。
  • ビルド する際に「COM クライアントに公開可能」をチェックし、
    レジストリ登録(regasm.exe)を実行します。

3.2. VBA から呼び出す

Option Explicit

Sub Demo_iTextSharp()
    Dim PdfProc As Object
    Set PdfProc = CreateObject("iTextSharpWrapper.PdfProcessor")
    On Error GoTo ErrorHandler
    Dim pos() As Double
    pos = PdfProc.GetTextPosition("C:\temp\sample.pdf", "Hello")
    Debug.Print "X:" & pos(0) & " Y:" & pos(1) & " W:" & pos(2) & " H:" & pos(3)
    Exit Sub
ErrorHandler:
    MsgBox "Error: " & Err.Description
End Sub

補足

  • iTextSharp は AGPL ライセンスです。
    商用プロジェクトで使う場合は別途ライセンス契約が必要になるケースがあります。
  • COM ラッパーで 例外を正しく VBA に転送 するために try-catchthrow new COMException(...) を投げるようにしてください。
  • 位置情報の精度は PDF エンジンに依存し、iText の戦略をカスタマイズすることで精度を上げられます。

4. Acrobat SDK(PDFium-CPP + VBA)で低レベル取得

Acrobat SDK の公式 PDFium ライブラリは C++ で提供されています。
VBA から呼び出す場合は、DLL をラップして使用します。以下は簡易例です。

4.1. DLL 生成(C++)

// pdfium_wrapper.cpp
#include <windows.h>
#include "public/fpdfview.h"

extern "C" __declspec(dllexport) BOOL __stdcall GetFirstTextObject(const char* pdfPath, double* x, double* y, double* w, double* h)
{
    FPDF_InitLibrary();
    FPDF_DOCUMENT document = FPDF_LoadDocument(pdfPath, NULL);
    if (!document) return FALSE;

    int pageCount = FPDF_GetPageCount(document);
    for (int i = 0; i < pageCount; ++i)
    {
        FPDF_PAGE page = FPDF_LoadPage(document, i);
        // テキストオブジェクト走査(省略)
        // ここで x,y,w,h を取得し、ポインタに書き込む
        // 例: *x = 100.0; *y = 200.0; *w = 50.0; *h = 10.0;
        FPDF_ClosePage(page);
    }
    FPDF_CloseDocument(document);
    FPDF_DestroyLibrary();
    return TRUE;
}
  • ビルドReleaseC++14 で行い、DLL を生成。
  • fpdfview.dll と同じフォルダにコピーします。

4.2. VBA から呼び出す

Option Explicit
Private Declare Function GetFirstTextObject Lib "pdfium_wrapper.dll" _
    (ByVal pdfPath As String, ByRef x As Double, ByRef y As Double, ByRef w As Double, ByRef h As Double) As Long

Sub Demo_PDFium()
    Dim sx As Double, sy As Double, sw As Double, sh As Double
    Dim ret As Long
    ret = GetFirstTextObject("C:\temp\sample.pdf", sx, sy, sw, sh)
    If ret Then
        Debug.Print "X:" & sx & " Y:" & sy & " W:" & sw & " H:" & sh
    Else
        Debug.Print "取得失敗"
    End If
End Sub

補足

  • PDFium は Google 社のオープンソースで、PDF のレンダリングエンジンとして非常に高速です。
  • ただし、C++ と COM の橋渡しに手間がかかり、デバッグも難しい点です。
  • PDFium の API は テキストオブジェクト取得専用ではなく、ページ全体の情報を取得できます。ただし、VBA で扱う場合は C++ 側で適切に構造体を定義し、サイズを揃える必要があります。

5. それぞれの選択肢を比較

項目 Adobe Reader API iTextSharp PDFium (Acrobat SDK)
ライセンス 有料(Acrobat DC) AGPL GPL (PDFium)
導入コスト 既存 PC に Adobe Reader があれば無料 .NET 環境 + COM ラッパー C++ 開発環境
実装容易さ ★★★(COM が直感的) ★★(COM ラッパー作成が必要) ★(C++ と DLL が必要)
機能範囲 座標・注釈・フォーム 座標・テキスト抽出・変換 座標・レンダリング・高速
メンテナンス 単体(Reader のアップデート) C# ラッパーの保守 C++ ラッパーの保守
速度 ★★ ★★ ★★★★
テキスト重み 文字レベルの座標取得が簡単 文字レベル取得が可能だがコスト大 文字レベル取得は実装が煩雑
  • 開発規模が小さく、既に Acrobat が社内で使用されている 場合は Adobe Reader API が最適です。
  • 無償で拡張性が高い が、VBA での連携に C# ラッパー が必要な場合は iTextSharp が便利。
  • 高速処理が求められる 大規模な PDF 処理や ページレイヤーの詳細まで取得 必要な場合は PDFium の導入を検討すべきです。

6. よくある質問(FAQ)

質問 回答
PDF 内の画像座標も取得したい Adobe Reader API では AcroPDXObject を走査すると画像座標が取得できます。iTextSharp でも PdfImageXObject の場所を取得できますが、低レベルでないと困難です。
検索対象が UTF-8 でエンコードされているので文字化けする 上記コードは PDPageHilite で文字列を検索しますが、PDF 内のテキストが Unicode であれば問題ありません。非 Unicode の場合は PDPageText を直接解析する必要があります。
複数ページにわたる検索を一括で行いたい GetTextPositions をループでページごとに呼び出せば良いです。iTextSharp ではカスタム戦略を使って stream 全体を走査できます。
VBA エクセルの 64bit 環境で動かさない COM ラッパーは 32bit/64bit 両方でビルドし、使用先に合わせて CreateObject 先に正しい ProgId を指定してください。
Adobe Reader が無い環境でも動かせる iTextSharp も PDFium も、外部ツールや DLL があれば実行できますが、ドキュメントには System.Drawing などが必要となる場合があります。

7. まとめ

VBA で PDF の座標情報を取得する際の主な道筋は 3 つです。

  1. Adobe Reader API
    • 公式サポート、COM が簡便
    • 有料、ライセンス管理に注意
  2. iTextSharp(C# COM ラッパー)
    • 無償・オープンソース、高度なテキスト解析が可能
    • ラッパー構築の負担、AGPL ライセンスを確認
  3. Acrobat SDK / PDFium
    • 低レベル高速アクセス
    • C++ 開発環境が必要、デバッグが難しい

プロジェクトの 要件(コスト、速度、機能、ライセンス) に応じて選択すれば、エクセル VBA から PDF に埋め込まれた文字・フォーム・注釈まで、正確かつ高速に 抽出できるようになります。

次回の社内プレゼンや業務自動化プロジェクトでは、今回紹介したコードをベースに 最適なアプローチを選定し、スムーズな PDF 処理ワークフローを構築してください。

ご質問や改善点があれば、ぜひコメント欄で共有ください!

コメント