ぽんぽこ日記

プログラミング、読書、日々の生活

アジア言語圏のPDFのテキスト抽出 - 2

前回の続きです。

ponpoko1968.hatenablog.com



PDFの文字列描画命令は、TJ、Tj演算子のオペランド文字列のエンコードとキャラクタセットは、先だって実行されたTmオペレータのフォントの指定に従っています。
たとえば、

/C2_0 1 Tf
0 Tc 4.093 0 Td
<0DFA0F6703D303E903B703BF03B1029406120370037703C3029403BB069E0F17037708370996053D>Tj

みたいな感じ。

    1. 1行目のTfオペレータでフォントを指定し、
    2. 2行目で文字のスペーシングとテキストの位置を指定
    3. 3行目で文字列描画
で、Tfオペレータで指定されている"/C2_0"という名前がフォントの指定なので、この名前をたぐって、文字列のエンコードを得ます。




ちなみに、たいていのPDFではエディタで直接PDFを開いてもこういうコードは書いてなくて、圧縮されたコンテントストリームとして格納されています。

36641 0 obj
&lt;&lt;/First 2311/Length 6988/Filter/FlateDecode/N 200/Type/ObjStm>>stream
	 バイナリ
endstream


上記の例だとバイナリの部分は、FlateDecodeというフィルタで圧縮されていると書かれており、zlibを使えば解凍できるはずです。PDFではBase64のフィルタなどが定義されています。

CGPDFScannerをつかって特定のオペレータとオペランドの情報を取得すればいいと思いますが、フィルタを適用した後の生のストリームを取り出すこともできます。
Core GraphicsのPDFパーサ機能を使えば、

  CGPDFPageRef page =  CGPDFDocumentGetPage ( pdfDocument, pageNum );

でページオブジェクトを取得して、

  CGPDFContentStreamRef stream=  CGPDFContentStreamCreateWithPage(page);
  CFArrayRef array = CGPDFContentStreamGetStreams(stream);

ページに含まれるコンテントストリームの配列を取得し、

  CGPDFStreamRef o =  CFArrayGetValueAtIndex(array, 0 );
  CFDataRef data =  CGPDFStreamCopyData(O, CGPDFDataFormatRaw);

CGPDFStreamCopyData()で生のデータを取得できます。ストリームはJPEGデータであることもあるので、実際に使う場合は、事前にCGPDFStreamGetDictionary()をつかってストリームの種類を判別しておく必要があります。


PDFの描画の仕組み上、TfオペレータはTjオペレータより前に出現するので、CGPDFScannerをつかって、Tfオペレータを検出して、あらかじめエンコーディングの情報を得ておき、直後に出現したTjオペレータのオペランドを適切にデコードしてやればテキスト情報が取得できます。

ここから本題の、PDFにおけるフォントと文字エンコーディングを調べる方法になるのですが、コードの準備がまだなので、次回書きます。

その代わりといっては何ですが、今回私が調査に使った資料とツールを紹介しておきます。

PDFの国際規格 ISO 32000-1 の企画書

PDF Reference and Adobe Extensions to the PDF Specification

700ページ超の大部ですが、今回のような用途の場合、7章と9章を読めば大方の情報は得られます。

CJKV日中韓越情報処理

通称フグ本。

プログラマのための文字コード技術入門

(それにしても、WEB+DBPress+シリーズは名著揃いですね!)

PDFの構造を表示するAppleのサンプルツール

Voyeur-PDF

below氏がMacOSX 10.6.x用にビルドできるようにしたもの。
http://github.com/below/PDF-Voyeur

続く


↓こういう本が出るようです。期待。

[asin:4873115493:detail]