PDF 構文 ファイル構造 (各部)
PDF構文 (PDF Syntax ISO 32000-2)
ここでは32000-2規格に基づいたPDF1.7の構文(構造)について説明しています。
PDFファイルは8-bitデータを単位として構成されていて一般の文書編集アプリケーションで開くことができその内容を読取ることができます。ただし、バイナリのデータもそのまま(表示可能な文字に変換されずに)格納されていますので文字化けしているように表示されますが、これはPDFの仕様です。
ここではそのデータの一部を読み解くことでPDF文書へのデータ追加などPDF再構成の意味を説明し、PDF-ToolsでのPDFデータ解析や編集をより詳細にできるようにします。
2.PDFファイル構造 ‐各部‐
PDFのデータ構造は、以下のように4つの部分(ヘッダー、ボディー、クロスリファレンス・テーブル、トレイラー)で構成されています。
ヘッダー (Header) |
ボディー (Body) |
クロスリファレンス テーブル (Cross-reference Table) |
トレイラー (Trailer) |
PDFはその内容をあとから変更(追加更新「Incremental Update」といいます)できるようになっています。更新されたPDFでも左図の構造が基本になります。
ここでは、説明のために初期状態のPDF構造を利用します。
2.1 ヘッダー (Header)
PDFファイルの第1行目にあるデータで、そのバージョンを表しています。以下のようなものです。
%PDF-1.N
Nには、0,1,2,3,4,5,6,7 のいずれかの数値が入ります(%PDF-2.0と記載された場合もあります)。
最初の文字"%"はそれに続く文字列がコメントであることを示しています。
しかし、この行は特殊の意味を持っていてPDF文書のバージョンを表します。
「PDF文書のバージョン」の意味するところは「PDF文書を構成するPDF構文のシンタックスバージョン」です。
なお、PDF1.7(ISO 32000-1)は、それ以前のバージョンのPDFデータをすべて解釈できる規格です。
PDF2.0(ISO 32000-2)も同様です。
ご注意ください: ここで示されたバージョンはPDF文書の更新によっ更新される場合があります(PDF1.4以上)。 その場合の新しいバージョンはカタログ ディクショナリ(トレイラーのRoot項目で示される)で示されます。
2.2 ボディー (Body)
文書の内容を表す、以下のような間接オブジェクト(PDFを構成する名前付きオブジェクト)で構成されています。
内容は、F1フォントで50ポイントの文字“Hello World”を位置(100,600)に表示することを示したダイレクト・オブジェクトの例です。
ストリームのLengthエントリの値は、ファイル内のstreamキーワードに続くデータ長を示す整数オブジェクトです。
これにより、シングルパスでPDFを生成するアプリケーションは、コンテンツが生成されるまでストリームの長さの指定を延期できます。
5 0 obj << /Length 43 >> stream BT /F1 50 Tf 100 600 Td (Hello World) Tj ET endstream endobj
オブジェクトにはboolean、number(整数/実数)、string(文字列)、name、array(配列)、dictionary(辞書)、Stream(ストリーム)、null の8種類があります。
特に、dictionaryには上記のようにstreamを付加できます。
streamが付加されたオブジェクトをストリーム オブジェクトといいます。
オブジェクトには、他のオブジェクトから参照できるようにラベルを付けることができます。
ラベル付けされたオブジェクトは、インダイレクト・オブジェクト(Indirect Object)と呼ばれます。
インダイレクト・オブジェクト(Indirect Object)
PDFファイル内の任意のオブジェクトは、インダイレクト・オブジェクトとしてラベル付けできます。
これにより、他のオブジェクトが参照できる一意のオブジェクト識別子がオブジェクトに与えられます。
これらの識別子は、Arrayの要素やDictionaryの値要素となります。
オブジェクト識別子は、次の2つの部分で構成されます。
- 正整数のオブジェクト番号
- 非負整数の世代番号
オブジェクト番号と世代番号の組み合わせは、インダイレクト・オブジェクトを一意に識別します。
PDFファイルでのインダイレクト・オブジェクトの定義は、そのオブジェクト番号と世代番号(空白で区切られています)と、それに続くキーワードobjとendobjで囲まれたオブジェクトの値で構成されます。
以下はその例です。
12 0 obj (Brillig) endobj
これは、オブジェクト番号12世代番号0で文字列値Brilligの文字列オブジェクトを定義しています。
オブジェクトは間接参照によってファイル内の他の場所から参照される場合があります。
このような間接参照は、オブジェクト番号と世代番号そしてキーワードR(各部分を空白で区切る)で以下のように構成されます。
12 0 R
PDF 1.5以降ではインダイレクト・オブジェクトはオブジェクトストリームに記載される場合があります(「7.オブジェクトストリーム」を参照)。 その場合でも同じ方法で参照されますが、定義にはキーワードobjおよびendobjは含まれず、それらの世代番号はゼロでなければなりません。
2.3 クロスリファレンス・テーブル (Cross-reference Table)
クロスリファレンス・テーブルとは、PDFのボディ部にあるダイレクト・オブジェクトをランダムにアクセスするため、それらがどの位置(オフセット“Offset”といい、PDFデータの先頭からのバイト位置です)にあるかを表したテーブルです。
クロスリファレンス・テーブルは、キーワード"xref"が記載された行から開始されます。
他にクロスリファレンス ストリームで記述される場合もあります。
このセクションの書式は固定です。
各行に記載される情報の区切り文字はスペース(ASCIIの0x20)でなければなりませんし、クロスリファレンスの各行は必ず2バイトの行末コードで終了します。
ここでの行末コードは必ず2バイトで、復帰コード(0x0D)に続く改行コード(0x0A)、空白(0x20)に続く復帰コード(0x0D)、空白(0x20)に続く改行コード(0x0A)のいずれかです。
以下はクロスリファレンス・テーブルの例です。
xref 0 8 0000000000 65535 f 0000000017 00000 n 0000000376 00000 n 0000000117 00000 n 0000000266 00000 n 0000000544 00000 n 0000000447 00000 n 0000000610 00000 n
2行目からがクロスリファレンス サブセクションです。
PDF Imager-LP(無償版)でクロスリファレンス・テーブルを表示するソースコード(クロスリファレンス・ストリームのデータも表示します。)
C# | C/C++ | Python |
using (var mlp = new PdfImager()){ // ライセンスキー mlp.Initialize("0-032E-E03H5EW76R15"); // オープン if (0 <= mlp.OpenDoc("HelloWorld.pdf")) { // Primitive インターフェース PrimitiveInterface prm = mlp.GetPrimitiveInterface(); // クロスリファレンス テーブル Console.WriteLine(prm.StringXref()); // クローズ mlp.CloseDoc(); } }
全体のソースコード |
クロスリファレンス サブセクション
クロスリファレンスのサブセクションはそのセクションに記載されるオブジェクト番号の範囲を示す行で開始されます。
その行には2種類の数値が含まれています。
1番めの数値は、そのサブセクションに含まれている最初のオブジェクト番号で、2番めの数値はオブジェクトの数量です。
上記例の「0 8」はオブジェクト番号0から7までの8個のオブジェクトのクロスリファレンスが記載されていることを示しています。
オブジェクト範囲を示した行に続くのがクロスリファレンス自身です。
クロスリファレンス
ひとつのクロスリファレンスは、行末コードを含めて20バイトで以下のように構成されています。
nnnnnnnnnn ggggg n
「nnnnnnnnnn」は10桁の数値でそのオブジェクトが配置されたオフセット位置を示しています。 「ggggg」は5桁の数値でオブジェクトの世代番号を示します。 「n」は“使用中”を表します。 各クロスリファレンスは、2バイトの行末コードで終了します。
使わなくなったオブジェクトのクロスリファレンス
更新(削除や変更)によって使われなくなったオブジェクトのクロスリファレンスもクロスリファレンス・テーブルにリストされます。
そのオブジェクトのクロスリファレンスはキーワード「f」が以下のように使用されます。
nnnnnnnnnn ggggg f
なお、この場合の「nnnnnnnnnn」は次の使わなくなった(フリーな)オブジェクトの番号を示します(詳細は、オブジェクト番号0参照)。 使われなくなったインダイレクト・オブジェクトが更新によって削除されることはありません。 そのため、このダイレクト・オブジェクトは更新前のクロスリファレンス テーブルを使えばいつでも参照できます。
オブジェクト番号0 (フリーなインダイレクト・オブジェクト)
オブジェクト番号0のオブジェクトは常に使わなくなった(フリーな)オブジェクトを表します。
世代番号もその最大値である65535を示しています。
複数の使わなくなったオブジェクトがクロスリファレンスにある場合は、以下のようになります。
xref 0 6 0000000003 65535 f 0000000017 00000 n 0000000081 00000 n 0000000000 00007 f 0000000331 00000 n 0000000409 00000 n
オブジェクト0はその次にフリーなオブジェクトの番号が3であることを示しています(オブジェクト3にリンクしているといいます)。
オブジェクト3が示しているのはオブジェクト番号0なので(オブジェクト0にリンクしているので)これ以上のフリーなオブジェクトが(現在の)PDF文書に無いことがわかります。
またその行の世代番号は次に生成するべきインダイレクト・オブジェクトの番号が7であることを示しています。
なお、フリーなオブジェクトを表すもうひとつの形式があります。フリーなオブジェクトは必ずオブジェクト0にリンクし世代番号を65535とする形式です。
2.4 トレイラー (Trailer)
トレイラーはクロスリファレンス・テーブルと特殊なオブジェクトを読み取るためのものでPDFファイルの最後に置かれています。 PDFファイルの最後は必ず以下のように「trailer」、「startxref」、「%%EOF」がこの順で出現しなければなりません。
trailer << /Size 8 /Info 6 0 R /Root 7 0 R /ID [<1775615b6d180ff72f4473d56aaa72bf><1775615b6d180ff72f4473d56aaa72bf>] >> startxref 665 %%EOF
PDFファイルの最終行は「%%EOF」です。
その前2行には"startxref"キーワードと数値それぞれを記載した行が順にあります。
この数値はxref(クロスリファレンス)セクションが記載されている位置を示すオフセット値(PDFファイル先頭からのバイト位置)です。
さらに、startxrefの前には"trailer"キーワードで示されたトレイラー(trailer)ディクショナリーがあります。
ディクショナリーとは、「<<」と「>>」で囲まれてキーと値の組が含まれたものです。
PDF Imager-LP(無償版)でトレイラーを表示するソースコード
C# | C/C++ | Python |
以下は、トレイラーに記載されるキー項目です。
キー | 値 |
---|---|
Size | クロスリファレンス・テーブルのエントリ数 |
Prev | 以前のクロスリファレンス・テーブルの位置を示すオフセット値 |
Root | カタログ(Catalog)ディクショナリ |
Encrypt | 暗号化ディクショナリ |
Info | 文書情報(Document Information)ディクショナリ |
ID | 2っのバイトストリング(「<」と「>」で囲まれた)で構成されたファイル識別子 |
以前のクロスリファレンス・テーブル
PDFの更新とはデータを「書き換える」のではなく、「変更部分をPDFデータの最後に追加」することによって更新します(Incremental Updateといいます)。
そのため、更新前のクロスリファレンス・テーブルが削除されずにPDF内部に残されその意味も失いません。
更新前のクロスリファレンス・テーブルのオフセット値はトレイラーのPrev要素として記載されます。
カタログ(Catalog)ディクショナリ
トレイラーのRoot項目にあるカタログ ディクショナリは、PDFオブジェクト階層のルートです。
このディクショナリは文書の内容、しおり、記事のスレッド、名前の付いた(文書内の)リンク先および他の属性を示すオブジェクトのリファレンスさらに、PDF文書がどのように表示されるか(しおりやサムネール画像を表示するか、最初に表示するページなど)を示す情報が含まれています。
このカタログ ディクショナリの詳細は「ドキュメント カタログ」を参照してください。
暗号化(Encrypt)ディクショナリ
パスワードや電子証明書を使ってPDF文書が暗号化されていることが示されます。
その場合は暗号化ディクショナリに暗号化の詳細情報が記載されます。
このディクショナリがPDF文書内に存在していない場合はPDF平分(非暗号化)文書です。
文書情報(Info)ディクショナリ
PDF文書のメタデータのひとつです。以下の情報が含まれます。
キー | タイプ | 値 |
---|---|---|
Title | text string | 文書のタイトル |
Author | text string | 文書を作成した人の名前 |
Subject | text string | 文書のサブジェクト |
Keyword | text string | 文書に関するキーワード |
Creator | text string | PDF以外の形式から変換した場合に、そのオリジナルの作成ツール |
Producer | text string | PDF以外の形式から変換した場合に、その変換ツール |
CreateDate | 日付 | 文書が生成された日時(人が読める形式) |
ModDate | 日付 | 文書が更新された最終の日時 |
Trapped | name | 文書がトラッピング情報を含むように変更されたかどうかを示す 名前オブジェクト |
オブジェクトのタイプは「2.5オブジェクト」を参照してください。
文書情報ディクショナリの例を以下に記します。
1 0 obj << /Title (PDF Imager-LP Reference) /Author (Trust Software System) /Creator (PDF Imager-LP) /Producer (Trust Software System, PDF Imager-LP) /CreationDate (D:2024-11-21010203+09'00') /ModDate (D:2024-11-21040506+09'00') >> endobj
PDF Imager-LP(無償版)で「文書情報」を表示するソースコード
C# | C/C++ | Python |
ID - ファイル識別子
PDFファイルを識別するユニークなバイト列です。
2.5 オブジェクト (Object)
オブジェクトには、8種類(boolean、number、string、name、array、dictionary、stream、null)の基本的なタイプがあります。 また、オブジェクトにはラベルの付いたものとそうでないものがありラベルの付いたオブジェクト(インダイレクト・オブジェクトといいます)は他のオブジェクトが参照することができます。
boolean
真偽値をキーワードの"true"または"falase"で表します。
これらはarrayもしくはdictionaryの項目として利用できます。
また、PostScriptの計算の戻り値としても利用されます。
number - integer(整数)とreal(実数)
数値の整数と固定小数点の実数を表します。
数値の値や精度の制限は以下のように規定されています。
integer | -2,147,483,648 から 4,147,483,647 まで |
real | ±3.403x1038(最大・最小値)、 |
±1.175x10-38(0ではない一番小さな値)、 | |
5(精度、小数点以下の桁数) |
以下にinteger及びrealの例を示します。
123 43445 +17 -98 0 34.5 -3.62 +123.6 4. -.002 0.0
string
stringは、バイトデータの並びを表現します。いわゆる文字列データ(literal string)やバイナリーデータ(hexadecimal string)を表します。文字列データは、"( )"で囲み以下のように表します。
(This is a string) (Strings may contain newlines and such.) (日本語文字列)
バイナリーは、数字の0から9とAからFまたはaからfで表し"<"と">"で囲み以下のように表します。
<4E6F762073686D6F7A206B6120706F702E> <901FA3> <901FA> <901FA0>
ご注意ください。バイナリーの桁数が偶数でない場合は、0が補充されて解釈されます。そのため、上記の例で最後の2行は同じバイナリーデータを表します。
stringは、その長さが32,767バイトまでと規定されています。
name
nameは、NULL以外のASCII文字を連続して並べたものです。先頭に"/"(スラッシュ)を付けて表します。以下に例を示します。
/Name1 /ASomewhatLongerName /A;Name_With−Various***Characters?
nameの最大長さは、127バイトと規定されています。
array
arrayは、1次元の配列でその並び順に意味があるものです。PDFのarrayは、その項目に他のオブジェクトを含めることができます。arrayは、"["と"]"で囲み以下のように表します。
[549 4.14 false (Ralph) /SomeName]
dictionary
dictionaryは、キーと値のペアを要素としたものです。dictionaryの要素数は0個でもかまいません。dictionaryは、"<<"と">>"で囲み以下のように表します。
<< /Type /Example /Subtype /DictionaryExample /Version 0 . 01 /IntegerItem 12 /StringItem ( a string ) /Subdictionary << /Item1 0 . 4 /Item2 true /LastItem ( not ! ) /VeryLastItem ( OK ) >> >>
stream
streamは、バイトのデータが直接記載されたものです。PDFアプリケーションはこのデータを直接バイトデータとして順に読み取ります。
また、そのサイズに(アプリケーションの実装上の制限以外に)制限はありませんので、streamには大きなデータや画像などのデータを格納できます。
streamはdictionaryに続けて、キーワード"stream"と"endstream"で囲んで記載します。
dictionary stream ...0個以上のバイトデータ... endstream
null
nullは、他のどのオブジェクトにも等しくない、正にヌルのオブジェクトです。"null"で表します。
indirect
indirectは、名前の付いたオブジェクトへの参照をあらわすオブジェクトです。以下は"6 0 obj"への参照を示しています。
6 0 R
2.6 ストリーム オブジェクト (Stream Object)
dictionaryタイプのインダイレクト・オブジェクトにはストリーム(Stream)を付加できます。このストリームが付加されたオブジェクトを、ストリーム オブジェクトといいます。
ストリームには、大きなサイズのバイトデータ(画像データや描画用のデータ)を格納できます。多くの場合、このバイトデータは圧縮されています。
ストリームは、ディクショナリ オブジェクトでなければならず、そのディクショナリ オブジェクトはLength項目を含まなければなりません。この項目は、ストリームのバイトサイズ(データが圧縮されている場合は、圧縮されたデータのバイト数)を表します。
以下は、ストリーム オブジェクトの例です。
3 0 obj << /Filter /FlateDecode /Length 72 >> stream ... 72バイトのバイナリデータ... endstream endobj
このオブジェクトは、72バイトのストリーム データを含み、そのデータはFlateDecodeで圧縮されていることがDictionaryに示されています。
ストリーム(Strem) オブジェクト ディレクトリの項目
ストリーム(Strem) オブジェクトのディクショナリには、以下の項目が追加されます。
キー | タイプ | 値 |
---|---|---|
Length | integer | (必須)ストリーム(Stream)に記載されたデータのバイト数 |
Filter | name / array | (オプション)ストリーム データが圧縮された場合のフィルター名 複数フィルターの場合はarrayで記載 |
DecodeParms | dictionary / array | (オプション)フィルターのパラメータを指定 |
オブジェクトのタイプは「2.5オブジェクト」を参照してください。