PDFを覗いてみよう(入門編)

この記事は、Imaizumi Lab Advent Calendar 8日目です。

もはや今日が何日なのかわからなくなってきました。

はじめに

騙されたと思って、お好きなテキストエディタ(vimとかemacsとか...)でPDFを開いてみてください。 きっとこのようなコードが表示されたはずです。

f:id:RussianBlue25:20191201010550p:plain
pdfの例-1
f:id:RussianBlue25:20191201010624p:plain
pdfの例-2

ところどころですが、PDF、Length、Filterなどといった人間に理解できる単語が出てきていますね。

意外とPDFって読めるんだと思いませんか? 構造について詳しくなれば出てくる文字列がどういう意味なのかわかるようになりそうです。

実はPDFの構造の仕様は公開されており、無料で読むことができます。

  • PDFリファレンス

https://www.adobe.com/content/dam/acom/en/devnet/pdf/pdf_reference_archive/pdf_reference_1-7.pdf

PDFの概略をつかむのに良いですが、大変なことに1310ページもあります。

PDFの構造に興味を持っても、さすがにこれを全部読むのは厳しいですよね。

というわけで本稿では初めてPDFの構造に触れる人を対象とし、PDFリファレンスの第三章(Syntax)を中心に、PDFの簡単な仕様について見ていきたいと思います。*1

上から順番に覗いてみる

PDFの構造は上から以下のようになっています。是非皆さんもお手元にファイルを用意して覗いてみてください。

(注意:PDFはなるべく中身がシンプルなもの(文章がHello, worldだけとか)を用意した方がいいと思います。何回も更新しているファイルだと追加更新という仕組みのせいで、特定の構造が複数回出てきたりするため本稿の説明と合わなくなります。追加更新については本稿では扱いません)

f:id:RussianBlue25:20201211121617p:plain:w150
PDFの構造(一部省略あり)

ヘッダ

PDFの一番先頭にあります。「%PDF-(数字)」となっている部分がヘッダです。数字部分はPDFのバージョンを表しています。

ボディ

インダイレクトオブジェクト(名前付きオブジェクト)と呼ばれるものが上から順番に並んでいます。

インダイレクトオブジェクトの説明をする前に、まずオブジェクトについて説明します。オブジェクトとはPDFを構成する要素で、

  • Boolean
  • Number
  • String
  • Name
  • Array
  • Dictionary
  • Stream
  • The null object

の8種類が存在します。

エディタでPDFを開いた時に表示されているものは、オブジェクトの集合体です。

f:id:RussianBlue25:20201211004628p:plain
ArrayとNumberの例

f:id:RussianBlue25:20201211004941p:plain
Name、Stream、Dictionaryの例

あるオブジェクトの中には別のオブジェクトを含むことができます。ArrayとNumberの例だとわかりやすいですね。

さて、本題に戻ります。インダイレクトオブジェクトとは、他のオブジェクトから参照されるようにラベル付けされたオブジェクトのことです。

f:id:RussianBlue25:20201211113649p:plain
インダイレクトオブジェクトの例

画像で説明すると、6 0 obj ~ endobj までがインダイレクトオブジェクトです。

インダイレクトオブジェクトはラベルとしてオブジェクト番号と世代番号を持ちます。この二つにより、インダイレクトオブジェクトを特定することができます。

上記の画像だと、6がオブジェクト番号で0が世代番号です。オブジェクト番号はオブジェクトを表す番号です。世代番号はオブジェクトが更新された際に変更される可能性がある番号です。

ところで、画像中に「7 0 R」「8 0 R」といった文字列がありますね。これは、このオブジェクトが7 0 objと8 0 objを参照していることを指しています。

クロスリファレンステーブル

PDFをビューワで見ているとき、ページの途中からでも瞬時に読み込まれますよね? これを実現しているのがこのクロスリファレンステーブルです。

f:id:RussianBlue25:20201211122340p:plain:w150
クロスリファレンステーブルの例

画像中の、0 21はオブジェクト番号0-20までの情報が入っていることを示しています。(オブジェクト番号が0のインダイレクトオブジェクトなんてないじゃないかと言われそうですが、これは特殊なインダイレクトオブジェクトのことで、常に使われません)

その下の「xxxxxxxxxx yyyyy n」となっているものは、インダイレクトオブジェクトがファイルの先頭からどの位置にあるかを示しています。画像の場合だと、上から順番にオブジェクト番号0からの情報が入っています。

xxxxxxxxxxが先頭からのオフセット、yyyyyが世代番号、最後のnは使用中のインダイレクトオブジェクトであることを指します。(最後のnがfになると未使用であることを指します)

トレイラー

クロスリファレンステーブルと特殊なオブジェクトを素早く読み込むためのものです。

上から順番にtrailer(キーワード)、トレイラーディクショナリ、startxref(キーワード)、xref(キーワード)の先頭までのオフセット、%%EOFとなっています。

f:id:RussianBlue25:20201211124107p:plain
トレイラーの例

おわりに

PDFをエディタで覗いてみると、意外と人間にも理解できる構造をしていることが分かったのではないでしょうか。

PDFに関してはネタがまだいくつかあるので、追々紹介していこうと思っています。次回は(多分)PDFの解析ツール編です。

参考文献

PDFの仕様書を読む 第24回 3.4.5 Incremental Updates : log log log

PDF 構文 -ファイル構造(各部)-

https://www.adobe.com/content/dam/acom/en/devnet/pdf/pdf_reference_archive/pdf_reference_1-7.pdf

*1:PDFの最新の規格はPDF2.0ですが、リファレンスがPDF1.7までのものなので1.7までの仕様で解説します。ここで書いていることは変更はないはずです