【コラム】
今回からは、PDFドキュメントの内部データにアクセスする方法を説明しよう。まず最初のターゲットは、テキストを取り出す事だ。テキストにアクセスできれば、検索などの処理が可能になる。
だが現状のiOSでは、PDFデータにアクセスするAPIが用意されてはいるものの、何らかの有意なデータを取り出すにはPDFフォーマット自体の理解が不可欠となる。そこでまずは、簡単にPDFフォーマットについて説明しよう。
PDF (Portable Document Format)は、アドビシステムズが開発した電子文書のためのフォーマットだ。1993年に登場しており、現在の最新バージョンは1.7となっている。2008年にISO 32000-1として標準化された。フォーマットの仕様書は公開されており、アドビシステムズのWebサイトからダウンロードすることができる。
PDFの特徴として、異なるデバイスであってもオリジナルのレイアウトを再現できる事、を挙げる事ができる。これは技術的には、PDFは印刷用のページ記述言語であるPostScriptをベースにしているためであり、印刷物をそのまま電子化したものと捉えることが出来る。
そのためページを紙として捉えたときの再現性は高いが、電子情報として考えると再利用性は低いとも言える。たとえば、様々なデバイスで表示させるときに、そのスクリーンに応じて最適な表示に変更する事ができない。また、これは本連載で詳しく見ていくのだが、内部のテキストデータなどにアクセスするのが容易ではないし、セマンティックな情報も抜け落ちている。
このように考えると、PDFフォーマットは、柔軟なレイアウトや検索の容易性に重点を置いた、HTMLなどとは対極にあるフォーマットとも言えるだろう。
PDFフォーマットの全容を理解するには、さきほど紹介した仕様書を読み下していけばいいのだが、これはあまりにも規模が大きくて複雑だ。そこで、もう少しプラクティカルなアプローチをしたい。まずはPDFの構造を体感してみよう。そしてその中から、必要な情報だけを抜き出していくのだ。
そのために使うのが、「Voyeur」というツールだ。これは、もともとMac OS XのSDKにサンプルとして付属していたものであり、Mac OS Xが持つPDFのためのAPIを使って、PDFドキュメントのデータを表示するものだ。いまのSDKには残念ながら含まれていないが、GitHubの方にソースコードがアップロードされているので、そちらから手に入れる事が可能だ。
上の画面が、VoyeurによるPDFのデータ構造の表示だ。このように、木構造として把握する事ができる。
この木構造のルートを見てみよう。「/Type」「/Pages」「/Version」という項目が並んでいる。PDFは辞書型のオブジェクト構造を持ち、これらはそのキーとなっている。「/Type」というキーには「/Catalog」、「/Version」というキーには「/1.4」という値が関連付けられている。そして、「/Pages」というキーにはさらなる辞書オブジェクトが関連付けられており、ここにPDFドキュメントのページ情報があるのだ。
続いて、「/Pages」の中身を見てみよう。ここには、「/Type」「/MediaBox」「/Count」「/Kids」というキーがある。これらのうち、「/Count」がPDFドキュメントのページ数を表している。ここでは「5」だ。そして「/Kids」の下は配列になっており、ここにページデータがある。
ページデータを開くと、ここにも様々なキーがある。「/Resources」には、カラースペースやフォントに関する情報がある。そして「/Contents」の下に、テキストデータを含むページのコンテンツデータがあるのだ。この内容を解析するのが、最初の目標になる。
コンテンツデータの内容を確認してみよう。Voyeurでは、[File]→[Show Info]メニューで表示する事ができる。これが、PDFの生データとなる。コンテンツデータは、ストリームデータとも呼ばれる。これは、データを先頭から読み込みながら処理する事ができるからだ。
ざっと見ても、ただのバイナリデータではなく、何らかの処理を示しているような記号が含まれている事が分かるだろう。これらを使ってデータを読み解いていく事になる。
PDFデータがどのような構造になっているかを少し体験してもらったところで、ここにアクセスするためのiOSのAPIを紹介しよう。コンテンツデータにアクセスするためには、CGPDFContentStreamというオブジェクトを使う。このオブジェクトは、PDFページからCGPDFContentStreamCreateWithPageという関数を使って取得する事ができる。
// PDFファイルのパスを取得
NSString* path;
path = [[NSBundle mainBundle] pathForResource:@"sample" ofType:@"pdf"];
// PDFドキュメントを作成
CGPDFDocumentRef document;
document = CGPDFDocumentCreateWithURL((CFURLRef)[NSURL fileURLWithPath:path]);
// PDFページを取得
CGPDFPageRef page;
page = CGPDFDocumentGetPage(document, 1);
// PDFコンテントストリームを取得
CGPDFContentStreamRef stream;
stream = CGPDFContentStreamCreateWithPage(page);
このstreamから、様々なデータを引っ張りだす事になる。また、このオブジェクトは、ページオブジェクトから取得している事に注目してほしい。PDFでは、すべてのデータはページ毎に分割されているのだ。もしテキストがページをまたいでつないでいるとすると、多くの場合はそうなのだが、バラバラに取得して、その後つなぎあわせることになる。
次回はコンテントストリームへのアクセス方法について説明しよう。
| 大阪市大とエコナビスタ、疲労医学研究を元にした「快眠健康ナビ」を製品化 [18:00 5/23] |
| 東大、ほ乳類の成体でも「神経幹細胞」が維持される仕組みを解明 [16:59 5/23] |
| 武蔵野化学研究所、高純度ポリ乳酸の高品質化に成功 [14:21 5/23] |
| 成層圏の「赤道準2年振動」は過去数10年弱まり続けている - JAMSTECなど [14:09 5/23] |
| TI、迅速なモーター回転設定が可能なセンサレスBLDCモータードライバを発表 [14:05 5/23] |
|
フェイス、SLI構成モデルなどGeForce GTX 780搭載ゲーミングPCを4製品 [22:19 5/23] パソコン |
|
米NVIDIA、GK110コア採用のハイエンドGPU「GeForce GTX 780」発表 [22:00 5/23] パソコン |
|
【レビュー】「GeForce GTX 780」を試す - GK110コア採用の「TITANの弟分」はどこまでやれるのか [22:00 5/23] パソコン |
|
LG、スマホで撮影した写真をワイヤレス印刷できるモバイルフォトプリンタ [21:56 5/23] 携帯 |
|
[進撃の巨人]主題歌「紅蓮の弓矢」が異例の“歌詞なし”先行カラオケ配信 [21:49 5/23] ホビー |