PDF ファイル構造の詳細
ここからは、実際の PDF ファイルの構成を解説していきます。
PDF ファイルは、基本的に、以下の4つの部分で構成されています。
PDF ファイルは、基本的に、以下の4つの部分で構成されています。
ヘッダ | ファイルが準拠している PDF のバージョンを指定する |
---|---|
ボディ | ドキュメントを構成するオブジェクトの定義部分。 複数の間接オブジェクトを記述します。 |
クロスリファレンステーブル | 間接オブジェクトに関する情報を指定する |
トレーラー | クロスリファレンステーブルの位置と、ドキュメントの基本情報を定義する |
ヘッダ
ヘッダでは、PDF のバージョンを指定します。
基本的に PDF ファイルの先頭にありますが、Acrobat Reader では、ファイルの先頭から 1024 byte 以内の範囲で検索が行われます。
M はメジャーバージョン、m はマイナーバージョンの数字です。
PDF 1.4 に準拠した PDF ファイルであれば、%PDF-1.4 となります。
これによって、ファイルがすべてテキストで記述されているか、そうでないかを判定できます。
バイナリ文字は、バイト値が 128 以上であれば、何でも構いません。
[F6]...は、実際にはバイナリ値です。
基本的に PDF ファイルの先頭にありますが、Acrobat Reader では、ファイルの先頭から 1024 byte 以内の範囲で検索が行われます。
%PDF-M.m
M はメジャーバージョン、m はマイナーバージョンの数字です。
PDF 1.4 に準拠した PDF ファイルであれば、%PDF-1.4 となります。
ヘッダの直後
PDF ファイル内にバイナリデータが含まれる場合は、ヘッダと改行の直後に、4 つ以上のバイナリ文字 (値が 128 以上のバイト値) を含むコメント行を置くことが推奨されています。これによって、ファイルがすべてテキストで記述されているか、そうでないかを判定できます。
バイナリ文字は、バイト値が 128 以上であれば、何でも構いません。
%PDF-1.4[改行] %[F6][E4][FC][DF][改行] ...
[F6]...は、実際にはバイナリ値です。
トレーラー
「トレーラー」は、ファイルの終端に記述された情報です。
PDF を読み込む際には、ヘッダで PDF のバージョンを識別した後、このトレーラーからデータを読み込んでいきます。
まず、PDF ファイルの終端には、%%EOF が記述されています。
この位置を基準にして、トレーラーを読み込みます。
%%EOF の前には、startxref キーワードと、ASCII 文字で数値が記述された行があります。
この数値は、「最後のクロスリファレンステーブルのオフセット位置 (ファイルの先頭を 0 としたバイト単位)」です。
クロスリファレンステーブルの先頭は xref キーワードであるため、その位置が指定されます。
startxref の前には、trailer キーワードがあり、その後に、トレーラー辞書が記述されます。
この辞書には、PDF に関する基本情報が定義されています。
PDF を読み込む際には、ヘッダで PDF のバージョンを識別した後、このトレーラーからデータを読み込んでいきます。
trailer << /Size 22 /Root 2 0 R /Info 1 0 R /ID [ <81b14aafa313db63dbd6f981e49f94f4> <81b14aafa313db63dbd6f981e49f94f4> ] >> startxref 18799 %%EOF
まず、PDF ファイルの終端には、%%EOF が記述されています。
この位置を基準にして、トレーラーを読み込みます。
%%EOF の直後には、改行が続く場合があります (任意)。
Acrobat Reader では、ファイルの終端から 1024 byte 以内の範囲で、%%EOF の検索が行われます。
Acrobat Reader では、ファイルの終端から 1024 byte 以内の範囲で、%%EOF の検索が行われます。
%%EOF の前には、startxref キーワードと、ASCII 文字で数値が記述された行があります。
この数値は、「最後のクロスリファレンステーブルのオフセット位置 (ファイルの先頭を 0 としたバイト単位)」です。
クロスリファレンステーブルの先頭は xref キーワードであるため、その位置が指定されます。
startxref の前には、trailer キーワードがあり、その後に、トレーラー辞書が記述されます。
この辞書には、PDF に関する基本情報が定義されています。
トレーラー辞書
"直接参照" は、間接オブジェクトを使わずに直接記述しなければならない、という意味です。
"間接参照" は、常に間接オブジェクトを使って指定しなければならない、という意味です。
指定がない場合は、どちらでも構いません。
"間接参照" は、常に間接オブジェクトを使って指定しなければならない、という意味です。
指定がない場合は、どちらでも構いません。
Size | integer | (必須。直接参照) ファイル内で使用されているすべての間接オブジェクトの、最大のオブジェクト番号 + 1 の値。 ※この値以上のオブジェクト番号は無視されます。 |
---|---|---|
Prev | integer | (複数のクロスリファレンステーブルがある場合のみ。直接参照) 前のクロスリファレンステーブルのオフセット位置 (ファイル先頭から)。 |
Root | dictionary | (必須。間接参照) カタログ辞書。 ドキュメントの表示方法や、ページの情報などを定義します。 |
Encrypt | dictionary | (PDF 1.1。暗号化されている場合は必須) 暗号化辞書。 |
Info | dictionary | (間接参照) Info 辞書。 タイトルや作成者などの情報を定義します。 |
ID | array | (PDF 1.1。Encrypt エントリがある場合は必須) このファイルの一意な識別子を指定する、2つの byte string の配列。 1番目は、ファイルの新規生成時の識別子。 2番目は、最後に更新された時の内容による識別子 (新規生成時は1番目と同じ値)。 2つの値が異なっている場合は、ファイルが更新されているということです。 「現在の時刻」「ファイルのパス」「ファイルサイズ」「Info 辞書のすべてのエントリの値」などの情報を元に、MD5 などでチェックサムを計算して、他の PDF ファイルと同じ値にならないような、任意の識別子を生成します。 通常の場合、指定はオプションですが、PDF/X では必須となります。 |
クロスリファレンステーブル
「クロスリファレンステーブル」では、間接オブジェクトのオフセット位置などが定義されています。
PDF ファイル内の任意の位置に記述します。
定義は、キーワード xref で始まります。
※ファイル終端の startxref キーワードの次の行で、クロスリファレンステーブルのオフセット位置が指定されます。
PDF ファイル内の任意の位置に記述します。
定義は、キーワード xref で始まります。
※ファイル終端の startxref キーワードの次の行で、クロスリファレンステーブルのオフセット位置が指定されます。
- xref キーワードの次の行から定義が始まり、1つ以上のサブセクションが存在します。
- 連続していないオブジェクト番号は、複数のサブセクションに分けられます。
- サブセクションは、PDF ファイルの更新時に、追加または削除することができます。
- 各サブセクションは、「スペースで区切られた2つの数値」が記述された行で始まります。
1番目の値は、「このサブセクションの最初のオブジェクト番号」。
2番目の値は、「このサブセクションのエントリの数」。 - サブセクションの各エントリは、1行ごとに、連続したオブジェクト番号のオブジェクトの情報を記述します。
エントリの各行の長さは、行末マーカーも含めて、常に 20 byte です。
エントリには、PDF 内で使用中のオブジェクトと、更新によって削除されたオブジェクトの2種類があります。
サブセクションのエントリ
間接オブジェクトの、「オフセット位置」「世代番号」「フラグ」の3つの情報を定義します。
各区切りは、半角スペース1文字です。
行の末尾は必ず2文字で、CR+LF、または、SPACE+[CR or LF] にします。
これで常に1行が 20 byte となります。
"NNNNNNNNN" は、10進数で10桁のオフセット位置 (ファイル先頭からのバイト数)。
"GGGGG" は、10進数で5桁の世代番号 (初期番号は 0、最大値 65535)。
"n" は、使用中のオブジェクトであることを示すキーワード文字。
"f" の場合は、使用されていない (削除された) オブジェクトです。
このエントリは、リンクリストを形成します。
フラグが f の場合、そのオフセット位置には、「次の空のオブジェクト番号」が指定されます。
また、世代番号が 65535 の場合は、空のリンクが続くという意味になります。
オブジェクト番号 0 は、空のリンクの終点として扱うと便利であるため、常に空のエントリとして定義される場合が多いです (無くても良い)。
その場合、(オブジェクト番号 0 を除く) 空のエントリが一つもない時は、オブジェクト番号 0 のエントリは、
"0000000000 65535 f" として定義します。
これは、自身に対してリンクを行うことで、他に空のエントリがないことを示します。
他に空のエントリがある場合は、オブジェクト番号 0 のオフセット値に、次の空のオブジェクト番号を指定します。
その後、各オフセット値で空のリンクを繋げていき、リンクの終端となる最後のオブジェクトでは、オフセット位置に 0 を指定することで、リンクの終了を明示します。
なお、この時、最後のオブジェクトでは、世代番号の値に 65535 ではなく「次に新規作成されるオブジェクトの番号」を指定します。
NNNNNNNNNN GGGGG n NNNNNNNNNN GGGGG f
各区切りは、半角スペース1文字です。
行の末尾は必ず2文字で、CR+LF、または、SPACE+[CR or LF] にします。
これで常に1行が 20 byte となります。
"NNNNNNNNN" は、10進数で10桁のオフセット位置 (ファイル先頭からのバイト数)。
"GGGGG" は、10進数で5桁の世代番号 (初期番号は 0、最大値 65535)。
"n" は、使用中のオブジェクトであることを示すキーワード文字。
"f" の場合は、使用されていない (削除された) オブジェクトです。
削除されたオブジェクト
f フラグで指定されたエントリは、PDF 内で使用されないオブジェクトです。このエントリは、リンクリストを形成します。
フラグが f の場合、そのオフセット位置には、「次の空のオブジェクト番号」が指定されます。
また、世代番号が 65535 の場合は、空のリンクが続くという意味になります。
オブジェクト番号 0 は、空のリンクの終点として扱うと便利であるため、常に空のエントリとして定義される場合が多いです (無くても良い)。
その場合、(オブジェクト番号 0 を除く) 空のエントリが一つもない時は、オブジェクト番号 0 のエントリは、
"0000000000 65535 f" として定義します。
これは、自身に対してリンクを行うことで、他に空のエントリがないことを示します。
他に空のエントリがある場合は、オブジェクト番号 0 のオフセット値に、次の空のオブジェクト番号を指定します。
その後、各オフセット値で空のリンクを繋げていき、リンクの終端となる最後のオブジェクトでは、オフセット位置に 0 を指定することで、リンクの終了を明示します。
なお、この時、最後のオブジェクトでは、世代番号の値に 65535 ではなく「次に新規作成されるオブジェクトの番号」を指定します。
例
▼ 単一のサブセクション (値がすべて連続しているオブジェクト番号) xref 0 6 % オブジェクト番号 0〜5 (6個) 0000000003 65535 f % [0] 空 -> [3] へ 0000000017 00000 n % [1] 0000000081 00000 n % [2] 0000000000 00007 f % [3] 空 -> 終了。次のオブジェクト番号は 7 0000000331 00000 n % [4] 0000000409 00000 n % [5] ▼ 複数のサブセクション (値が連続していないオブジェクト番号を含む) xref 0 1 % オブジェクト番号 0 (1個) 0000000000 65535 f % [0] 空 -> 終了 3 1 % オブジェクト番号 3 (1個) 0000025325 00000 n % [3] 23 2 % オブジェクト番号 23,24 (2個) 0000025518 00002 n % [23] 世代番号 2 0000025635 00000 n % [24]
検証
それでは、前回の 01.pdf を元に、実際の PDF ファイルの中身を順に見ていきましょう。
%PDF-1.3 %---- ボディ部分 (間接オブジェクト定義) %% カタログ辞書 1 0 obj << /Type /Catalog /Pages 2 0 R % ページツリーノード >> endobj %% ページツリーノード 2 0 obj << /Type /Pages /Kids [ 3 0 R ] % ページオブジェクト /Count 1 >> endobj %% ページオブジェクト 3 0 obj << /Type /Page /Parent 2 0 R % 親ノード /MediaBox [ 0 0 612 792 ] % ページの領域 (1/72 inch 単位) /Contents 4 0 R % ページの内容 >> endobj %% コンテンツストリーム (ページ内容) 4 0 obj << /Length 18 >> % データの長さ stream 10 10 100 100 re % 四角形のパスを追加 S % ストローク描画 endstream endobj %----- クロスリファレンステーブル xref 0 5 % オブジェクト番号 0〜4 0000000000 65535 f % 空 0000000009 00000 n % [1] カタログ辞書 0000000062 00000 n % [2] ページツリーノード 0000000128 00000 n % [3] ページオブジェクト 0000000227 00000 n % [4] コンテンツストリーム %----- トレーラー trailer << /Size 5 % 最大のオブジェクト番号(4) + 1 = 5 /Root 1 0 R % カタログ辞書 = オブジェクト番号 1 >> startxref 296 % クロスリファレンステーブルのオフセット位置 %%EOF