PDF 構文

PDF 構文
まずは、PDF の構文について解説しておきます。

  • 基本的には ASCII 文字で記述します。
  • バイナリデータはそのままファイル内に含めることができます。
    (2 byte 以上の値の場合、ビッグエンディアン順で並べます)
  • PDF 内において、「文字列 (string)」という用語は、通常の文字列に限らず、8 bit のバイトデータも含みます。
  • PDF 内では、文字は、「通常文字」「区切り文字」「空白文字」の3つのクラスに分けられます。

    空白文字0x00 (Null)、0x09 (Tab)、0x0A (LF)、0x0C (FF)、0x0D (CR)、0x20 (SPACE)
    区切り文字( ) < > [ ] { } / %
    通常文字空白文字と区切り文字以外のすべて
  • 空白文字または区切り文字が出現すると、そこで単語が分かれます。
  • 改行は、[CR], [LF], [CR+LF] のいずれかが使えます。
    これらは、「行末マーカー (EOL)」と呼ばれ、ほとんどの場合、空白文字と同様に扱われます。
  • PDF 内では、アルファベットの大文字と小文字は区別されます。
  • % はコメントの開始として扱われ、% から行末までの部分がコメントして無視されます。
    ※文字列オブジェクト内など、一部対象外となる箇所があります。
空白について
使用例のテキストでは、内容がわかりやすくなるように、インデントとして空白を用いていますが、実際に PDF として書き出す際には、ファイルサイズを減らすために、なるべく空白は使用せずに記述してください。

2 0 obj
<< /Type /Pages
   /Kids [ 3 0 R ]
   /Count 1
>>
endobj

▼ 出来る限り空白を除去した例

2 0 obj
<</Type/Pages/Kids[3 0 R]/Count 1>>
endobj
基本オブジェクト
PDF では、基本的なオブジェクトとして、以下を使用することができます。

  • ブール値 (Boolean)
  • 整数および実数 (Integer, real number)
  • 文字列 (String)
  • 名前 (Name)
  • 配列 (Array)
  • 辞書 (Dictionary)
  • ストリーム (Stream)
  • null オブジェクト
null
キーワード null で指定でき、空のデータとして扱われます。
辞書のエントリの値として null を指定した場合、エントリを省略するのと同じになります。
ブール値
キーワード true, false で示される真偽値です。
数値
数値には、「整数」と「実数」の2種類があります。

値の範囲と精度は、PDF を処理するアプリケーションによります。
基本的には、整数は「符号付き 32 bit 値」、実数は「単精度浮動小数点数 (32bit)」として扱われます。

値は、オプションとして符号を付けた 10 進数で記述します。16 進数などでは表現できません。
値が整数の制限値を超えた場合は、実数に変換されます。

[整数] 123 43445 +17 −98 0
[実数] 34.5 -3.62 +123.6 4. -.002 0.0
配列
[ ] で囲まれた範囲内は、1次元の配列となります。

配列内では、任意のオブジェクトを順番に並べます。
要素の区切りは、空白文字または区切り文字です。

配列の要素には、数値や文字列、辞書、他の配列など、複数のタイプのオブジェクトを含めることができます。
配列内でさらに配列を記述すると、入れ子にすることもできます。

※配列の要素数には、実装上の制限があります。

[549 3.14 false (Ralph) /SomeName [0 1]]
名前オブジェクト
名前」は、文字列で構成された識別子です。

同じ文字列で構成された名前は、同一の名前オブジェクトとなります。
名前は、PDF 内では頻繁に使われます。

名前を定義する場合は、文字列の前に「スラッシュ (/)」を付けます。
(スラッシュは名前の文字列には含まれません)

名前の大文字と小文字は区別されます。
区切り文字や空白文字は、名前の一部としてそのまま記述することは出来ませんが、それ以外の通常文字は、すべて使用することができます。

PDF 1.2 以降では、null (0) を除く任意の文字は、「#XX (XX は2桁の16進数)」と記述することで、名前に含めることができます。
これによって、名前に、区切り文字や空白文字などを含めることができます。
ただし、「'!' (33) 〜'~' (126)」以外の文字を使用することは推奨されません。

名前の長さには、実装における制限があり、一般的には最大 127 byte となっています。

/Name1
/A;Name_With−Various***Characters?
/1.2
/.notdef
/Adobe#20Green % #20→空白。"Adobe Green"
/The_Key_of_F#23_Minor % #23→#。"The_Key_of_F#_Minor"
文字列オブジェクト
  • 文字列は、8bit (0〜255 の符号なし整数値) で構成されます。
  • ( ) で囲まれた文字列は、リテラル文字列です。
  • < > で囲まれた文字列は、16進数でバイトを表現する文字列です。
リテラル文字列
( ) 内で、ASCII 文字などを直接記述します。

  • 「バックスラッシュ (\)」は特殊文字を扱うために使用されますが、それ以外の ASCII 文字はそのまま記述できます。
  • 文字列内で '(' または ')' を記述する場合、'(' と ')' の2つが対になって存在する場合は、そのまま記述できます。
    対になっていない場合は、各文字をバックスラッシュでエスケープする必要があります。
  • 文字列内に改行が含まれる場合 (括弧の内側で改行が行われた場合)、行末マーカーは \n と同じ扱いになります。
    (CR+LF, CR, LF いずれの場合でも)

リテラル文字列内でバックスラッシュを使うと、特殊文字を扱えます。

行末にバックスラッシュがある場合、その後に続く改行は無視されます。
(改行は無かったかのように扱われて、次の行の文字に続きます)

バックスラッシュの次の文字が、対象外の文字であった場合は、バックスラッシュは無視されます。

\nLF
\rCR
\tTab
\bBackspace
\fForm feed
\((
\))
\\\
\dddddd は、1〜3 桁の8進数 (0〜255。範囲外の値は無視される)。
1文字の文字コードを指定します。

※この記述の後に、文字として、通常の半角数字を記述したい場合は、
2つを明確に区別するために、\ddd で3桁分の数値を記述した後、続けて、文字としての数字を記述します。
(3桁の数字を読み込んだ時点で、この形式は常に終了するため)

(This is a string)

% 途中の改行は \n となる
(Strings may contain newlines
and such.)

% 対になっている ( ) と記号は、そのまま記述できる
(Strings may contain balanced parentheses () and
special characters (*!&}^% and so on).)

% 対になっていない ( ) はエスケープする
(string\(abc)

% 改行は無視される
(These \
two strings \
are the same.)

% 直接記述できない文字をコードで指定
(This string contains \245two octal characters\307.)

% \005 と 3
(\0053)
16進数文字列
< > で囲まれた文字列は、16進数表現によるバイナリデータとなります。

1 byte を、2文字の16進数 (0-9, A-F, a-f) で記述していきます。
< > 内の空白文字はすべて無視されます。

文字数が奇数で終わった場合、最後の文字は '0' として扱われます。

<4E6F762073686D6F7A206B6120706F702E> % 4E,6F,76,...
<901FA> % 90,1F,A0
辞書オブジェクト
辞書は、「キー」と「値」の2つからなる連想テーブルです。

"<<" が辞書の開始、">>" が辞書の終了となります。

その中で、キーと値の2つをペアにして、それを連続で記述していきます。
空白文字または区切り文字によって、それぞれが区切られます。

1つ目の要素が「キー (名前オブジェクト)」で、
2つ目の要素が「キーに対応する値 (null を含む、すべての種類のオブジェクト)」となります。

辞書のエントリの数には、実装上の制限があります。

※ 同じ辞書内で、同じキーを複数持つことは出来ません。同じキーが複数出現する場合、その値は未定義となります。

<< /Type /Example
   /Subtype /DictionaryExample
   /Version 0.01
   /IntegerItem 12
   /StringItem (a string)
   /Subdictionary << /Item1 0.4
                     /Item2 true
                     /LastItem (not!)
                     /VeryLastItem (OK)
                  >>
>>
ストリームオブジェクト
ストリームオブジェクトは、画像などの大きいデータや、長い文字列などを定義する時に使います。
ストリームのデータの長さは無制限です。

ストリームのデータは、フィルタによって、暗号化や圧縮を行うことができます。
そのため、バイナリデータであることが多いです。

ストリームオブジェクトの場合は、先頭に辞書オブジェクトが存在します。
この辞書内で、データのサイズなど、データに関する情報を記述します。

その辞書の後に、キーワード stream が続きます。
stream の次の行から、キーワード endstream までの間が、ストリームデータとなります。

<<
  /Length 100
>>
stream
... data ...
endstream

  • すべてのストリームは「間接オブジェクト」である必要があります。
    (間接オブジェクトについては、後述します)
  • ストリームの先頭の辞書は、直接記述する必要があります (間接参照しない)。
  • ストリームデータの正確なバイト数は、ストリームの先頭の辞書の Length エントリで指定します。
  • stream キーワードの後には、必ず [LF][CR+LF] のいずれかの行末マーカーが続く必要があります。
    行末マーカーが CR の場合、ストリームデータの先頭が LF (0x0A) だった場合は、CR と LF が一つの行末マーカとして判別されてしまうため、stream の後の改行に CR のみを使うのは避けてください。
  • endstream キーワードの直前には、行末マーカーを付けることが推奨されます。
    ※この場合、データの末尾にある行末マーカーは、ストリームデータの長さには含まれません。
  • PDF 1.2 以降では、実際のストリームデータは外部ファイルに存在する場合があります。
    その場合、stream〜endstream の間は無視されます。
すべてのストリーム辞書で共通のエントリ
ストリームの先頭の辞書で定義するエントリを説明します。

なお、これ以降、「〜辞書」と言った場合は、辞書の指定エントリの値として定義する辞書、または、ストリームの先頭で定義する辞書のことだと思ってください。

また、"必須" と書かれているエントリは、必ず含めなければならない項目です。
それ以外は、オプションとして、必要な場合のみ指定します。
PDF バージョンが指定されている場合は、そのバージョン以降で使用できる項目です。

表のキー名には先頭に / を付けていませんが、実際に使う際には、/Length などとして、名前オブジェクトとして記述してください。

辞書のキー値のタイプ説明
Lengthinteger(必須) ストリームデータのバイト数。
フィルタを適用した後のサイズです。
stream の次の行の先頭から、endstream の直前の改行までのバイト数となります。
Filtername or arrayデータに適用するフィルタの名前。

フィルタが複数適用されている場合は、各フィルタの名前の配列です。
その場合、先頭から順にデコードを適用していきます。
DecodeParmsdictionary or arrayFilter エントリで指定されたフィルタで使用するパラメータの辞書。
フィルタが複数指定されている場合は、辞書の配列です。

パラメータがすべてデフォルト値の場合、DecodeParms は省略できます。
複数のフィルタがあり、いずれかのフィルタのパラメータがデフォルト以外の値に設定されている場合は、全てのフィルタの値を指定する必要があります。

※ Acrobat Reader では、省略のキー名として DP という名前も受け付けます。両方存在する場合は、DecodeParms が優先されます。
Fファイル指定(PDF 1.2) ストリームデータの外部ファイル
FFiltername or array(PDF 1.2) F が存在する場合のフィルタ
FDecodeParmsdictionary or array(PDF 1.2) F が存在する場合のフィルタパラメータ
DLinteger(PDF 1.5) フィルタをデコードした後の、データのバイト数。
全体のサイズを事前に確認するためのヒントとしてのみ使用されます。