データ構造と間接オブジェクト

データ構造
前回説明した「ストリーム辞書」の表では、辞書の値のタイプとして、integer や dictionary と表記していました。
それらは、基本のオブジェクトを使って構成されており、以下のようなタイプに分かれます。

nullNull オブジェクト
booleanブール値オブジェクト
integer整数 (実数ではない値)
number整数または実数
name名前オブジェクト
array配列オブジェクト
dictionary辞書オブジェクト
date日付の文字列 (ASCII string)
rectangle矩形の指定 (array)
function関数 (dictionary or stream)
file specificationファイル指定 (string or dictionary)
name tree名前ツリー (dictionary)
number tree数値ツリー (dictionary)
streamストリームオブジェクト
text stream(PDF 1.5) テキストストリーム。
ストリームデータとして text string を記述したもの。
文字列
stringPDF 1.6 以前の場合は、text string として表現できない文字列 (バイトデータ)。
PDF 1.7 以降の場合は、さらに text string, ASCII string, byte string の3つに分類される。
text string人間が読める文字列。
PDFDocEncoding または UTF-16BE でエンコードされた文字列。
PDFDocEncoded string(PDF 1.7) PDFDocEncoding による文字列
ASCII string(PDF 1.7) ASCII 文字のみの文字列
byte string(PDF 1.7) 8 bit で表されるバイナリデータ

※名前ツリーなどについては、使用する時が来た段階で説明します。
PDFDocEncoding
PDF 内での欧文用のエンコーディングです。
1文字を 1 byte (0〜255) で表します。

ASCII 文字はそのまま記述しますが、0x80〜0xFF の範囲内には、欧文や記号などが割り当てられています。
詳しくは、PDF の仕様書をご覧ください。
UTF-16BE 文字列
PDF タイトルなどで日本語などの文字列を記述したい場合は、UTF-16BE のコードを16進数のバイトデータで記述します。

その場合、先頭には常に BOM (0xFE,0xFF) を置く必要があります。
BOM の後、2 byte 単位でビッグエンディアン順に (上位バイトから順に)、コードを記述していきます。

例えば、"あ" の場合は、Unicode のコード値が U+3042 なので、16進数文字列で表記する場合は、
「BOM (FEFF)」と「"あ" (3042)」になります。
U+10000 以上のコードについては、4 byte で表現することになります。

<FEFF3042>

また、UTF-16BE の文字列中に、以下のようにエスケープシーケンスを記述すると、後続のテキストの言語を指定することができます。

「001B」+「2文字の ISO 639 言語コード (en, ja など)」+「(オプション)2文字の ISO 3166 国コード (US, JP など)」+「001B」
rectangle
四角形の範囲を指定します。
配列で、「左下 X、左下 Y、右上 X、右上 Y」の4つの数値を指定します。

PDF では、数学のグラフなどと同じように、左下を原点として、「X 軸は右方向、Y 軸は上方向」の座標系となります。

[ 0 0 612 792 ]
日付
日付は、ASCII 文字列で指定します。

(D:YYYYMMDDHHmmSSOHH'mm')

D:日付の接頭語
YYYY
MM
DD日 (01-31)
HH時間 (00-23)
mm分 (00-59)
SS秒 (00-59)
O現地時間と世界時 (UT) の関係。[ +, -, Z ] のいずれか。
'+' で UT より遅い、'-' で UT より早い、'Z' で UT と等しい。
HH'UT からのオフセットの時間の絶対値 (00-23)。省略可
mm'UT からのオフセットの分の絶対値 (00-59)。省略可

(D:199812231952+09'00')
=> 1998年12月23日午後7時52分、JST 日本標準時
間接オブジェクト
間接オブジェクト」は、他のオブジェクトから参照される目的で、それ自体が独立して定義されたオブジェクトです。

例えば、エントリの数が多い辞書の場合、直接その位置に記述すると、全体が冗長となり複雑になってしまいます。
そのような辞書を、間接オブジェクトとして別の場所に定義して、実際に使用する際には間接参照をさせると、すっきりとした記述になります。

▼ 直接記述した場合

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


▼ 間接オブジェクトを使った場合

1 0 obj
<< /Type /Catalog
   /Pages 2 0 R  % 2 0 obj の内容を参照
>>
endobj

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

このように、直接記述すると長くなるような、配列・辞書・ストリームなどで主に使われます。
(ストリームは常に間接オブジェクトとして定義する必要があります)

辞書の値として指定するものの中には、常に間接オブジェクトを使って参照しなければならないものもあります。
そのため、PDF 内では頻繁に使われます。
使い方
間接オブジェクトは、PDF のボディ部分で、複数定義されます。

間接オブジェクトの一つ一つには、「オブジェクト番号」が割り当てられます。
このオブジェクト番号を使うことで、間接オブジェクトを参照できます。

  • オブジェクト番号 (正の整数)
    各間接オブジェクトごとに付けられる番号です。
    通常は 0,1,2... というように、順番に番号が付けられますが、必ずしも値が連続する必要はないため、任意の数値を指定することができます。
    (ただし、実際には、番号は連続していた方が、後々扱いやすくなります)
  • 世代番号 (正の整数)
    PDF ファイルが編集・更新された場合の、更新ごとの識別番号です。
    新規作成された PDF では、すべてのオブジェクトの世代番号は 0 となります。
    ファイルが更新されて、オブジェクトの内容が変更された場合、新しい世代番号が追加されます。

間接オブジェクトの定義は、「オブジェクト番号 世代番号 obj」の行で始まり、endobj の行で終了します。
obj と endobj の間に、内容となるオブジェクトを記述します。

間接オブジェクトの内容を参照する場合は、参照したい場所で、「オブジェクト番号 世代番号 R」と記述します。
(間接オブジェクトを参照することを、「間接参照」と呼びます)

※ 間接オブジェクトの値を参照した時、その間接オブジェクトが PDF 内で定義されていなかった場合は、エラーとはならず、null オブジェクトへの参照として扱われます。

% 間接オブジェクトの定義
% オブジェクト番号 = 12
% 世代番号 = 0
% 内容 = "string" の文字列

12 0 obj
(string)
endobj

% オブジェクト番号 12 の内容を参照
% (辞書の /Name キーの値として)

<< /Name 12 0 R >>

% 結果として、12 0 R が (string) に置き換わる

<< /Name (string) >>