PicoPDF

PDFの構造 🔗

PDFは4つのデータ構造で構成される。

名前 説明
ヘッダー バージョンを指定
ボディー PDFの内容
クロスリファレンステーブル ボディーのオフセット
トレイラー クロスリファレンステーブルのオフセット

ヘッダー 🔗

ヘッダーにはPDFのバージョンを指定する。
最初の行が%PDF-m.nで始まる必要がある。m.nはPDFのバージョンである。

%PDF-1.7
%🍣

PDFファイルにはバイナリデータが含まれるため、ヘッダー行の直後に4バイト以上のバイナリ文字(0x80以上)のコメント行があることが望ましい。
🍣はU+1F363であり、UTF-8ではF0 9F 8D A3となる。(4バイト以上のバイナリ文字であればなんでもよい)

ボディー 🔗

間接オブジェクト(indirect object)が並んでいる。
オブジェクト番号 世代番号 objで始まる。
オブジェクト番号は正の整数で連番であることが多い。
世代番号は正の整数で、PDFが後から更新された際に追加されることがある。
以後、世代番号は0固定であるものとして説明する。

obj ~ endobjの間がオブジェクトの内容になる。
特別な場合を除き<< xxx >>という辞書形式のみを説明する。

% オブジェクト番号123、世代番号0、辞書データを持つ
123 0 obj
<<
  /Type /Page
>>
endobj

ストリームデータを持つ場合はstream ~ endstreamの間に記述する。
/Lengthの指定は必須である。

% オブジェクト番号234、世代番号0、ストリームデータを持つ
234 0 obj
<<
  /Length 49
>>
stream
BT
  /F1 50 Tf
  10 20 Td
  (Text Data) Tj
ET
endstream
endobj

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

ランダムアクセスを可能にするため、間接オブジェクトのオフセットを記録する。
xrefの次行に開始オブジェクト番号 個数を記述する。
その後にエントリとして個数分のオブジェクトへのオフセットを記述する。

オブジェクト番号0番は常に空きであり、世代番号は65535固定になるため0000000000 65535 fとなる。末尾のfは空きを表す。
その後、オブジェクト番号1番からnnnnnnnnnn ggggg nの形式で指定する。
nnnnnnnnnnは10桁のオブジェクト番号、gggggは5桁の世代番号、nは使用中であることを表す。

エントリは1行20バイトでなければならない。1
nnnnnnnnnn ggggg nの形式が18バイトになるため、2バイトの改行コードを使用するか、スペース1バイトと1バイトの改行コードを使用する必要がある。

xref
0 4
0000000000 65535 f
0000000016 00000 n
0000000070 00000 n
0000000136 00000 n

PDFを後から更新すると、エントリの後に再度開始オブジェクト番号 個数とエントリが出現することがある。
本説明ではこの形式は説明しない。

トレイラー 🔗

trailerに続いて辞書形式で設定する。
startxrefの次行にクロスリファレンステーブルへのオフセットを指定する。
最終行は%%EOFで終了する。2

キー 説明
Size integer(間接参照不可) 必須、クロスリファレンステーブルのエントリ総数
Root dictionary(間接参照でないといけない) 必須、カタログタイプへの間接参照
Info dictionary(間接参照でないといけない) 任意、ドキュメント情報への間接参照
Encrypt dictionary 任意、暗号化の際は必須
ID array 任意、ファイル識別子となる要素が2のバイト配列、暗号化のにID自体は暗号化してはいけない
trailer
<<
  /Size 4
  /Root 1 0 R
>>
startxref
1234
%%EOF

オブジェクトの種類 🔗

PDFでは次のオブジェクトがサポートされる。

タイプ 説明
Boolean 真偽値、true または false(アルファベット小文字)
Integer 整数、上限・下限はアプリケーション依存
Real Number 実数、上限・下限はアプリケーション依存
String 文字列、( xxx )形式のリテラル文字列、<0000>形式の16進文字列
Name 名前、/Name形式
Array 配列、配列内には任意のオブジェクトが格納できる、[ xxx ]形式
Dictionary 辞書、キーと値のペア、<< /Key /Value >>形式
Stream ストリーム、<< /Length xxx >> stream ~ endstream形式、間接オブジェクトである必要がある
Null Object null値
Indirect Object 間接オブジェクトの参照、オブジェクト番号 世代番号 R形式

リテラル文字列内では不均衡なカッコとバックスラッシュ以外が使用できる。
次のエスケープシーケンスを使用できる。

シーケンス 説明
\n ラインフィード(LF)
\r キャリッジリターン(CR)
\t 水平タブ(HT)
\b バックスペース(BS)
\f フォームフィード(FF)
\( 左カッコ
\) 右カッコ
\\ バックスラッシュ
\ddd キャラクターコード(10進数)

名前はスラッシュで始めてスペースを含めることはできない。
大文字、小文字は区別される。
名前中にスペースなどを入れる場合は、#00形式で埋め込むことができる。

/Adobe#20Green           %→Adobe Green
/PANTONE#205757#20CV     %→PANTONE 5757 CV
/paired#28#29parentheses %→paired()parentheses
/The_Key_of_F#23_Minor   %→The_Key_of_F#_Minor
/A#42                    %→AB
  1. エントリが20バイトでないとPDFリーダーでは正しく読み込めないことがある。Adobe Acrobatでは表示できるが閉じる際に保存を促された。 

  2. Adobe Acrobatではファイル末尾から1024バイト以内に%%EOFが必要とのこと。