PDF 文書の解析


Quartz は、PDF 文書の構造とコンテンツストリームを検査することができる関数を提供します。文書の構造を調べることで、文書カタログのエントリと各エントリに関連した内容を読み取ることができます。カタログを再帰的に走査することにより、文書全体を検査することができます。


PDF のコンテンツストリームは、その名前が示唆しているとおりです。つまり、'BT 12 / F71 Tf (このテキストを描画) Tj . . . ' などのデータの連続ストリームで、ここで PDF オペレータとそのデスクリプタが実際の PDF コンテンツと混ざり合っています。コンテンツストリームを検査するには、それに順番にアクセスする必要があります。


この章では、PDF 文書の構造を調べ、PDF 文書の内容を解析する方法を示します。


PDF 文書の構造の検査


PDF ファイルには複数ページのイメージとテキストが含まれている場合があります。Quartz を使用して、PDF ページ上のオブジェクトだけでなく、文書やページレベルのメタデータにアクセスできます。この説では、アクセス可能なメタデータについて簡単に紹介します。


PDF 文書オブジェクト(CGPDFDocument) には、PDF 文書に関連する、カタログおよびコンテンツを含むすべての情報が含まれます。カタログ内のエントリは、PDF 文書の内容を繰り返し記述します。 CGPDFDocumentGetCatalog 関数を呼び出して、PDF 文書カタログの内容にアクセスできます。


PDF ページオブジェクト(CGPDFPage) は、PDF 文書のページを表し、ページ辞書やページの内容を含む特定のページに関連する情報を含みます。CGPDFPageGetDictionary 関数を呼び出してページ辞書を得ることができます。


図 14-1 に、図 13-2 に示した PDF ファイルを構成する 2 つのイメージ(テキストと雄鶏のイメージ) を説明するメタデータの一部を示します。


PDF メタデータにアクセスすることにより、より有用な情報を得ることができます。図 14-1 のアイテムは単なるサンプルです。たとえば、リスト 14-1 に示したコードを使用して、PDF にサムネイル画像があるかどうかを確認できます(図 14-2 を参照)。


リスト 14-1   PDF のサムネイル表示を得る


CGPDFDictionaryRef d;
CGPDFStreamRef stream; // represents a sequence of bytes
d = CGPDFPageGetDictionary(page);
// check for thumbnail data
if (CGPDFDictionaryGetStream (d, “Thumb”, &stream)){
    // get the data if it exists
    data = CGPDFStreamCopyData (stream, &format);


Quartz はデータストリームの復号と解読をあなたに代わりすべて行います。


Quartz には、PDF メタデータ内のアイテムの個々の値を得るために使用できるいくつかの関数が用意されています。CGPDFObjectGetValue 関数を使用して、CGPDFObjectRef、PDF オブジェクト型(kCGPDFObjectTypeBoolean、kCGPDFObjectTypeInteger などなど)、および値の格納域を渡して下さい。返ってくると、格納域は値で満たされています。


さまざまなノードとその子供にアクセスするため、PDF ファイルの階層を横断するために使用できる他の多くの関数があります。たとえば、CGPDFArray 関数(CGPDFArrayGetBoolean、CGPDFArrayGetDictionary、CGPDFArrayGetInteger などなど) を使用すると、値の配列にアクセスして特定の型の値を得られます。これらの関数の使用方法の詳細については、PDF 仕様書を参照してください。


PDF コンテンツの解析


PDF コンテンツストリームには、アプリケーションにとって重要な PDF コンテンツストリームの一部を知らせる演算子が含まれています。演算子は、単一の点またはシーケンスをマークします。演算子は、プロパティリストまたはそれに関連したオブジェクトを持つタグとして指定されます。タグは、点またはコンテンツシーケンスが表すものを指定します。プロパティリストは、PDF コンテンツの作成者が指定したキー値のペアを含む辞書です。PDF コンテンツストリームを解析すると、アプリケーションは全ての目的のマーカーを検索さし、タグ、プロパティリスト、またはマーカーに関連したオブジェクトを検査し、さらなる適切な処理が実行されます。PDF 演算子の完全なリストについては、PDF リファレンス を参照してください。


CGPDFScanner オブジェクト(CGPDFScannerRef データ型) を使用して、PDF コンテンツストリームを解析します。CGPDFScanner オブジェクトは、呼び出し関数を登録したストリームの全ての演算子の呼び出し関数を呼び出します。


以下の節で説明するタスクを実行して、コンテンツストリームを解析します:


  1. 演算子呼び出し関数を書く。 処理したい演算子に対してのみ呼び出し関数を記述する必要があります。
  2. 演算子の表の作成と設定
  3. PDF 文書を開く
  4. 各ページのコンテンツストリームをスキャン

そうするのが適切な場合は、スキャナ、コンテンツストリーム、および演算子の表を解放する必要があります。


以下の節では、コンテンツストリームを解析して マーク付きコンテンツ演算子 (表 14-1 を参照) を検索する方法を示します。マーク付きコンテンツ演算子は、PDF コンテンツで使用される PDF 演算子の一部のみを表します。独自のコードを書くときは、アプリケーションに適した PDF 演算子を探して下さい。


表 14-1 マーク付きコンテンツ演算子は、解析できる PDF 演算子の一部を表します


演算子説明
MP関連しているタグがあるマークされた点。
DPタグとそれに関連したプロパティリストまたはオブジェクトがあるマークされた点。
BMCマーク付きコンテンツシーケンスの開始(マーク付きコンテンツの開始) を示し、シーケンスの終了を知らせる EMC マーカーと対になります。関連するタグがあります。
BDCマーク付きコンテンツシーケンスの開始を知らせ、シーケンスの終了を知らせる EMC マーカーと対になります。タグとそれに関連したプロパティリストまたはオブジェクトがあります。
EMCBMC または BDC マーカーで始まるマーク付きコンテンツシーケンス(マーク付きコンテンツの終わり) の終わりを通知します。この演算子にはタグが関連していません。


演算子呼び出し関数を書く


Quartz が PDF 演算子の呼び出し関数を呼び出すと、CGPDFScanner オブジェクトと、呼び出し関数で必要な全ての情報へのポインタを渡します。通常、呼び出し関数は演算子に関連した全てのアイテムを取得します。たとえば、リスト 14-2 に示した MP 演算子の呼び出し関数は、CGPDFScannerPopName 関数を呼び出して、演算子に関連した文字列をスタックから得ます。リスト内のコードがスキャナスタックから名前を得るのに成功すると、その名前を印刷します。


Quartz には、オブジェクト、ブール値、名前、数値、文字列、配列、辞書、及びストリームを得るための CGPDFScannerPop 関数が用意されています。各関数は、項目を得るのが成功したかどうかを示すブール値を返します。


リスト 14-2 MP 演算子の呼び出し関数


static void
op_MP (CGPDFScannerRef s, void *info)
{
    const char *name;
 
    if (!CGPDFScannerPopName(s, &name))
        return;
 
    printf("MP /%s\n", name);
}


演算子の表の作成と設定


CGPDFOperatorTable オブジェクトには、記述した PDF 演算子呼び出し関数が格納されます。リスト 14-3 に示したように、CGPDFOperatorTableCreate 関数は演算子の表を作成します。演算子の表を作成した後、表に追加する呼び出し関数ごとに CGPDFOperatorTableSetCallback 関数を呼び出します。表、PDF 演算子を指定する文字列、およびその演算子を処理するために書く呼び出し関数へのポインタを渡します。呼び出し関数には、何でも好きな名前を付けられます。CGPDFOperatorTableSetCallback 関数に渡す呼び出し関数名のスペルが間違っていないことを確認してください。


リスト 14-3 のコードは、表 14-1 にリストされているマーク付きコンテンツ演算子の各々の呼び出し関数を設定します。あなたのアプリケーションは、関心のある演算子の呼び出し関数のみを設定して下さい。PDF 演算子文字列は、Adobe の PDF リファレンス に定義されています。


リスト 14-3 演算子の表用の呼び出し関数の設定


CGPDFOperatorTableRef myTable;
 
myTable = CGPDFOperatorTableCreate();
 
CGPDFOperatorTableSetCallback (myTable, "MP", &op_MP);
CGPDFOperatorTableSetCallback (myTable, "DP", &op_DP);
CGPDFOperatorTableSetCallback (myTable, "BMC", &op_BMC);
CGPDFOperatorTableSetCallback (myTable, "BDC", &op_BDC);
CGPDFOperatorTableSetCallback (myTable, "EMC", &op_EMC);


PDF 文書を開く


PDF 文書の内容をスキャンする前に、それを開く必要があります。リスト 14-4 に、コードに提供された URL から CGPDFDocument オブジェクトを作成するコードの断片を示します。リストはコードの断片であるため、すべての変数が宣言されているわけではありません。番号付きコード行の詳細な説明は、リストの後に表示します。


リスト 14-4 URL から PDF 文書を開く

CGPDFDocumentRef myDocument;
myDocument = CGPDFDocumentCreateWithURL(url);// 1
if (myDocument == NULL) {// 2
        error ("can't open `%s'.", filename);
        CFRelease (url);
        return EXIT_FAILURE;
}
CFRelease (url);
if (CGPDFDocumentIsEncrypted (myDocument)) {// 3
    if (!CGPDFDocumentUnlockWithPassword (myDocument, "")) {
        printf ("Enter password: ");
        fflush (stdout);
        password = fgets(buffer, sizeof(buffer), stdin);
        if (password != NULL) {
            buffer[strlen(buffer) - 1] = '\0';
            if (!CGPDFDocumentUnlockWithPassword (myDocument, password))
                error("invalid password.");
        }
    }
}
if (!CGPDFDocumentIsUnlocked (myDocument)) {// 4
        error("can't unlock `%s'.", filename);
        CGPDFDocumentRelease(myDocument);
        return EXIT_FAILURE;
    }
}
 if (CGPDFDocumentGetNumberOfPages(myDocument) == 0) {// 5
        CGPDFDocumentRelease(myDocument);
        return EXIT_FAILURE;
}


コードの動作は以下の通りです:


  1. コードに提供された URL から CGPDFDocument オブジェクトを作成します。
  2. CGPDFDocument オブジェクトが作成されたことを確認します。そうでない場合、文書なしで続行することは意味がないため、コードは終了します。
  3. 文書が暗号化されているかどうかを確認します。文書が暗号化されている場合、コードは空白のパスワードを使用して開こうとします。それが失敗すると、コードはユーザーにパスワードを要求し、パスワードで文書のを解錠しようとします。
  4. 文書が解錠されているかどうかを確認します。そうでない場合、コードは終了します。
  5. 文書に少なくとも 1 ページあることを確認します。それ以外の場合、コードは終了します。

各ページのコンテンツストリームをスキャン


リスト 14-5 のコードの断片は、文書内の各ページをスキャンします。スキャナが、あなたが呼び出し関数を登録した PDF 演算子の 1 つに遭遇すると、Quartz は呼び出し関数を呼び出します。コードの各行の詳細な説明は、リストの後に示します。


リスト 14-5 文書の各ページをスキャン


int k;
CGPDFPageRef myPage;
CGPDFScannerRef myScanner;
CGPDFContentStreamRef myContentStream;
 
numOfPages = CGPDFDocumentGetNumberOfPages (myDocument);// 1
for (k = 0; k < numOfPages; k++) {
    myPage = CGPDFDocumentGetPage (myDocument, k + 1 );// 2
    myContentStream = CGPDFContentStreamCreateWithPage (myPage);// 3
    myScanner = CGPDFScannerCreate (myContentStream, myTable, NULL);// 4
    CGPDFScannerScan (myScanner);// 5
    CGPDFPageRelease (myPage);// 6
    CGPDFScannerRelease (myScanner);// 7
    CGPDFContentStreamRelease (myContentStream);// 8
 }
 CGPDFOperatorTableRelease(myTable);// 9


コードの動作は以下の通りです:


  1. 以前に開いた文書のページ数を得ます。PDF 文書を開く を参照してください。
  2. スキャンするページを得ます。ページの番号は 1 から始まります。
  3. ページのコンテンツストリームを作成します。
  4. コンテンツストリーム用のスキャナを作成します。 以前に作成して呼び出し関数を設定したコンテンツストリームと演算子の表を渡さなければなりません。演算子の表の作成と設定 を参照してください。呼び出し関数に必要な全てのデータを渡すこともできます。
  5. スキャナに関連したコンテンツストリームを解析します。Quartz は、呼び出し関数を提供した演算子の 1 つに遭遇するたびに呼び出し関数を呼び出します。

  6. ページを解放します。
  7. スキャナを解放します。
  8. コンテンツストリームを解放します。
  9. PDF 内のすべてのページをスキャンした後、演算子の表を解放します。


前の章 次の章



目次
Xcode 10 の新機能

  • 前書き
  • 誰がこの文書を読むべきか?
    この文書の構成
  • Quartz 2D の概要
  • ページ
    描画先:グラフィックスコンテキスト
    Quartz 2D 不透明(Opaque) データ型
    グラフィックス状態
    Quartz 2D 座標系
    メモリー管理:オブジェクトの所有権
  • グラフィックスコンテキスト
  • iOS のビューグラフィックスコンテキストに描画する
    Mac OS X でのウィンドウグラフィックスコンテキストの作成
    PDF グラフィックスコンテキストの作成
  • ビットマップグラフィックスコンテキストの作成
  • サポートされるピクセル形式
    アンチエイリアス
    印刷用のグラフィックスコンテキストの取得
  • パス
  • パスの作成とパスの描画
  • ビルディングブロック
  • パスの作成
  • パスのペイント (Painting a Path)
  • ブレンドモードの設定
  • パスをクリップする
  • 色と色空間
  • 色と色空間について
    アルファ値
    色空間の作成
    色の設定と作成
    レンダリングインテントの設定
  • 変換
  • Quartz 変換関数について
    現在の変換配列の変更
    アフィン変換の作成
    アフィン変換の評価
    ユーザをデバイス空間変換に導く
    配列の背後の数学
  • パターン
  • パターンの構造
    色付きパターンとステンシル(無色の) パターン
    タイル張り
    パターンの仕組み
  • 色付きパターンのペイント
  • ステンシルパターンのペイント
  • 陰影
  • 陰影の仕組み
    コンテキストに基づいて変化する陰影描画規則
    陰影を用いたペイント
  • グラデーション
  • 軸方向および放射状グラデーションの例
    CGShading と CGGradient オブジェクトの比較
    グラデーションの端を超えて色を拡張
    CGGradient オブジェクトの使用
  • CGShading オブジェクトの使用
  • CGShading オブジェクトを使用して軸方向のグラデーションをペイント
  • CGShading オブジェクトを使用して放射状グラデーションをペイント
  • 以下も見よ
  • 透過レイヤー
  • 透過レイヤーの仕組み
    透過レイヤーへのペイント
  • Quartz 2D でのデータ管理
  • Quartz 2D へのデータの移動
    Quartz 2D からのデータの移動
    Mac OS X で Quartz 2D と Core Image 間でデータを移動
  • ビットマップイメージとイメージマスク
  • ビットマップイメージとイメージマスクについて
    ビットマップイメージ情報
  • イメージの作成
  • イメージマスクの作成
  • イメージをマスクする
  • イメージにブレンドモードを使用
  • Core Graphics のレイヤー描画
  • レイヤー描画のしくみ
  • レイヤーを使った描画
  • 複数の CGLayer オブジェクトを使用して旗を描画する例
  • PDF 文書の作成、表示、および変換
  • PDF を開いて表示
    PDF ページの変換を作成する
    PDF ファイルの作成
    リンクの追加
    PDF コンテンツの保護
  • PDF 文書の解析
  • PDF 文書の構造の検査
  • PDF コンテンツの解析
  • PostScript 変換
  • 呼び出し関数を書く
    呼び出し関数構造体を埋める
    PostScript 変換オブジェクトの作成
    データプロバイダおよびデータコンシューマオブジェクトの作成
    変換の実行
    テキスト
    用語集
    文書改訂履歴












    トップ(Quartz 2D プログラミングガイド)












    トップ(Quartz 2D プログラミングガイド)












    トップ(Quartz 2D プログラミングガイド)












    トップ(Quartz 2D プログラミングガイド)












    トップ(Quartz 2D プログラミングガイド)












    トップ(Quartz 2D プログラミングガイド)












    トップ(Quartz 2D プログラミングガイド)












    トップ(Quartz 2D プログラミングガイド)












    トップ(Quartz 2D プログラミングガイド)












    トップ(Quartz 2D プログラミングガイド)












    トップ(Quartz 2D プログラミングガイド)












    トップ(Quartz 2D プログラミングガイド)