前回は、PDFドキュメントでテキストがどのようにエンコーディングされているかを説明した。今回からは、その仕組みに対応するソースコードを書いていこう。
まずは、フォント情報の取得からだ。
Tfオペレータに対するコールバックの追加
この連載で説明しているサンプルでは、テキストの抽出はPDFViewControllerクラスで行っていた。これを拡張していこう。
前にも説明したが、PDFデータはストリームとして与えられる。テキストとエンコーディングがどのような関係でストリームに登場するか見てみると、まずフォント情報に内包される形でエンコーディングが指定される。続いて、テキストデータが登場する、という流れだ。ということはエンコーディングを利用するには、フォント情報をピックアップすることと、得られたエンコーディングを保持しておく、という対応が必要になるだろう。
現在のエンコーディングを保持しておくために、PDFViewControllerクラスにインスタンス変数を追加しよう。_encodingという名前にしておく。
List 1.
@interface PDFViewController : UIViewController
{
CGPDFDocumentRef _document;
int _index;
NSMutableString* _text;
CGPDFContentStreamRef _stream;
NSString* _encoding;
...
次に、PDFオペレータテーブルの設定を変更する。フォント情報をキャプチャするために、コールバックを追加するのだ。対象となるオペレータ名は「Tf」となる。コールバック関数としては、operator_Fontという名前のものを用意した。
List 2.
- (IBAction)textAction
{
...
// PDFコンテントストリームを取得
_stream = CGPDFContentStreamCreateWithPage(page);
// PDFオペレータテーブルを作成
CGPDFOperatorTableRef table;
table = CGPDFOperatorTableCreate();
CGPDFOperatorTableSetCallback(table, "TJ", operator_Text);
CGPDFOperatorTableSetCallback(table, "Tj", operator_Text);
CGPDFOperatorTableSetCallback(table, "Tf", operator_Font);
これでストリーム中にTfオペレータが現れるたびに、operator_Fontが呼び出される事になる。
フォント情報とエンコーディングの取得
operator_Font関数では、operatorFontScanned:メソッドを呼ぶ事にしよう。このメソッドで、エンコーディングを取り出す事になる。
まずは、フォント名を取得する。
List 3.
- (void)operatorFontScanned:(CGPDFScannerRef)scanner
{
bool result;
// フォントサイズの取得
CGPDFInteger size;
result = CGPDFScannerPopInteger(scanner, &size);
if (!result) {
return;
}
// フォント名の取得
const char* name;
result = CGPDFScannerPopName(scanner, &name);
if (!result) {
return;
}
...
PDFデータ中のフォント情報は、前回Voyeurで確認したように、「/F2.1 1」という形になっている。先頭の「/F2.1」がフォント名で、それに続く「1」はフォントサイズとなる。このフォント名が欲しい訳なのだが、CGPDFのAPIではストリームとして流れて来たPDFデータを、スタックに順次積んでいく構造になっている。そこで、まず始めにフォントサイズをポップし、それからフォント名を取得している。
フォント名が取得できたら、次はフォントデータを取り出す。フォントデータは、これも前回説明したように、/Resourceの下に入っている。この領域のデータにアクセスするために、CGPDFContentStreamGetResourceという関数が用意されている。この関数に、アクセスしたいリソースのカテゴリと名前を入れる事で、データを取得する事ができる。次のように使う。
List 4.
...
// フォントの取得
CGPDFObjectRef object;
object = CGPDFContentStreamGetResource(_stream, "Font", name);
if (!object) {
return;
}
// PDF辞書の取得
CGPDFDictionaryRef dict;
result = CGPDFObjectGetValue(object, kCGPDFObjectTypeDictionary, &dict);
if (!result) {
return;
}
// エンコーディングの取得
const char* encoding;
result = CGPDFDictionaryGetName(dict, "Encoding", &encoding);
if (!result) {
return;
}
// エンコーディングの設定
[_encoding release], _encoding = nil;
_encoding = [[NSString stringWithCString:encoding encoding:NSASCIIStringEncoding] retain];
}
CGPDFContentStreamGetResource関数の、第一引き数にはストリーム、第二引き数にカテゴリ名である"Font"、そして第三引き数にさきほど取得したフォント名を渡してやる。すると、フォント情報がCGPDFDictionaryの形でとれるはずだ。この辞書から、キー"Encoding"を指定して、エンコーディング名を取り出せることになる。
これでPDFドキュメントからエンコーディングが取得できた。このようにCGPDFのAPIを使いこなすには、PDFフォーマットに対する知識が必須となる事が分かるだろう。
次回は、このエンコーディングをもとに、テキストデータのUnicodeへの変換を行おう。
ここまでのソースコード: PDF-4.zip