PDF ファイル構造の詳細

PDF ファイル構造の詳細
ここからは、実際の PDF ファイルの構成を解説していきます。

PDF ファイルは、基本的に、以下の4つの部分で構成されています。

ヘッダファイルが準拠している PDF のバージョンを指定する
ボディドキュメントを構成するオブジェクトの定義部分。
複数の間接オブジェクトを記述します。
クロスリファレンステーブル間接オブジェクトに関する情報を指定する
トレーラークロスリファレンステーブルの位置と、ドキュメントの基本情報を定義する
ヘッダ
ヘッダでは、PDF のバージョンを指定します。

基本的に 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 のバージョンを識別した後、このトレーラーからデータを読み込んでいきます。

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 の検索が行われます。

%%EOF の前には、startxref キーワードと、ASCII 文字で数値が記述された行があります。
この数値は、「最後のクロスリファレンステーブルのオフセット位置 (ファイルの先頭を 0 としたバイト単位)」です。
クロスリファレンステーブルの先頭は xref キーワードであるため、その位置が指定されます。

startxref の前には、trailer キーワードがあり、その後に、トレーラー辞書が記述されます。
この辞書には、PDF に関する基本情報が定義されています。
トレーラー辞書
"直接参照" は、間接オブジェクトを使わずに直接記述しなければならない、という意味です。
"間接参照" は、常に間接オブジェクトを使って指定しなければならない、という意味です。
指定がない場合は、どちらでも構いません。

Sizeinteger(必須。直接参照)
ファイル内で使用されているすべての間接オブジェクトの、最大のオブジェクト番号 + 1 の値。
※この値以上のオブジェクト番号は無視されます。
Previnteger(複数のクロスリファレンステーブルがある場合のみ。直接参照)
前のクロスリファレンステーブルのオフセット位置 (ファイル先頭から)。
Rootdictionary(必須。間接参照) カタログ辞書。
ドキュメントの表示方法や、ページの情報などを定義します。
Encryptdictionary(PDF 1.1。暗号化されている場合は必須) 暗号化辞書。
Infodictionary(間接参照) Info 辞書。
タイトルや作成者などの情報を定義します。
IDarray(PDF 1.1。Encrypt エントリがある場合は必須)
このファイルの一意な識別子を指定する、2つの byte string の配列。

1番目は、ファイルの新規生成時の識別子。
2番目は、最後に更新された時の内容による識別子 (新規生成時は1番目と同じ値)。

2つの値が異なっている場合は、ファイルが更新されているということです。

「現在の時刻」「ファイルのパス」「ファイルサイズ」「Info 辞書のすべてのエントリの値」などの情報を元に、MD5 などでチェックサムを計算して、他の PDF ファイルと同じ値にならないような、任意の識別子を生成します。

通常の場合、指定はオプションですが、PDF/X では必須となります。
クロスリファレンステーブル
クロスリファレンステーブル」では、間接オブジェクトのオフセット位置などが定義されています。

PDF ファイル内の任意の位置に記述します。
定義は、キーワード xref で始まります。

※ファイル終端の startxref キーワードの次の行で、クロスリファレンステーブルのオフセット位置が指定されます。

  • xref キーワードの次の行から定義が始まり、1つ以上のサブセクションが存在します。
  • 連続していないオブジェクト番号は、複数のサブセクションに分けられます。
  • サブセクションは、PDF ファイルの更新時に、追加または削除することができます。
  • 各サブセクションは、「スペースで区切られた2つの数値」が記述された行で始まります。
    1番目の値は、「このサブセクションの最初のオブジェクト番号」。
    2番目の値は、「このサブセクションのエントリの数」。
  • サブセクションの各エントリは、1行ごとに、連続したオブジェクト番号のオブジェクトの情報を記述します。
    エントリの各行の長さは、行末マーカーも含めて、常に 20 byte です。
    エントリには、PDF 内で使用中のオブジェクトと、更新によって削除されたオブジェクトの2種類があります。
サブセクションのエントリ
間接オブジェクトの、「オフセット位置」「世代番号」「フラグ」の3つの情報を定義します。

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