PDFってこうなってる? Part 4:白紙のPDFページを自作する
テキストエディタとバイナリエディタを使って、白紙のPDFドキュメントを一から手作りする方法を解説します。カタログ、ページツリー、xrefテーブルの構造を理解しましょう。

PDFファイルの内部構造を理解する最良の方法は、実際に自分の手でPDFを作ってみることです。このシリーズのPart 4では、テキストエディタとバイナリエディタを使って、白紙のPDFドキュメントをゼロから自作します。完成したPDFファイルは、PDFリーダーで実際に開くことができる「自分だけの」一品になります。前回までのパートで学んだヘッダー、トレーラー、xrefテーブルの知識をベースに、いよいよPDFのボディセクションを組み立てていきましょう。
PDFボディセクションの概要
PDFファイルのボディセクションには、ドキュメントを構成するすべてのオブジェクトが格納されます。テキスト、画像、フォント情報、ページレイアウトなど、PDFの中身を表現するあらゆる要素がオブジェクトとして定義されています。
これらのオブジェクトは「カタログ」と呼ばれるディクショナリーオブジェクトを根本(ルート)に持つ木構造で配置されています。PDFリーダーは、まずトレーラーからカタログを見つけ、そこからページツリーをたどって各ページの内容にアクセスします。この階層構造を理解することが、PDFを手作りするための第一歩です。
カタログディクショナリー
カタログはPDFドキュメント全体の入り口となるオブジェクトです。PDFリーダーはトレーラーに記載された /Root エントリからカタログを参照し、ドキュメントの構造を把握します。
カタログディクショナリーには最低限以下の要素が必要です:
/Type /Catalog:このオブジェクトがカタログであることをPDFリーダーに伝えます/Pages 2 0 R:ページツリーのルートノードへの間接参照です
実際のPDFコードでは、以下のように記述します:
1 0 obj
<< /Type /Catalog /Pages 2 0 R >>
endobj1 0 obj はオブジェクト番号1、世代番号0を表します。<< と >> で囲まれた部分がディクショナリーの中身です。
ページツリーの構造
ページツリーは、ドキュメント内のすべてのページを管理する階層構造です。2種類のノードで構成されています:
- ページツリーノード(Pages):他のページツリーノードやページノードへの参照をまとめる中間ノード。大規模ドキュメントでは複数の中間ノードを使い、ページを効率的に管理します。
- ページノード(Page):ドキュメント内の個々のページを表すリーフノード。ページサイズやコンテンツ、リソースなどの情報を保持します。
大規模なドキュメントでは、ページツリーがバランスよく構成されることで、特定のページへの迅速なアクセスが可能になります。たとえば、1000ページのPDFで500ページ目を開く場合、フラットなリストではなく木構造をたどることで、少ないステップで目的のページに到達できます。
ページツリーノードの記述
2 0 obj
<< /Type /Pages /Kids [3 0 R] /Count 1 >>
endobjこのオブジェクトに必要な要素は以下のとおりです:
/Type /Pages:ページツリーノードであることを示します/Kids配列:配下に接続されているページツリーノードまたはページノードへの参照を含みます/Count:このノード以下に存在するページノードの総数です
ページオブジェクトの定義
ページノード(この例では3 0 obj)は、個々のページの属性を定義します。
3 0 obj
<< /Type /Page /Parent 2 0 R /MediaBox [0 0 500 800] /Resources << >> >>
endobj必須要素の解説
/Type /Page:このオブジェクトがページであることを示します/Parent 2 0 R:親のページツリーノードへの参照。ページツリー内での位置関係を定義します/MediaBox [0 0 500 800]:ページの物理的なサイズを定義します。4つの数値は、左下の座標(0, 0)と右上の座標(500, 800)を表し、単位はポイント(1ポイント = 1/72インチ)です。この例では約176mm x 282mmのページサイズになります/Resources:ページが使用するフォントや画像などのリソースを指定します。白紙ページの場合は空のディクショナリー<< >>を指定します
完全なPDFファイル例
ここまでの要素をすべてまとめると、以下のような白紙PDFファイルが完成します。テキストエディタで以下の内容を入力し、.pdf 拡張子で保存してみましょう。
%PDF-2.0
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 500 800] /Resources << >> >>
endobj
xref
0 4
0000000000 65535 f
0000000010 00000 n
0000000060 00000 n
0000000115 00000 n
trailer
<< /Size 4 /Root 1 0 R >>
startxref
180
%%EOFこのファイルをPDFリーダーで開くと、白紙のページが1枚表示されるはずです。たった数行の記述で、正規のPDFファイルが作れることに驚かれるのではないでしょうか。
クロスリファレンス(xref)テーブルの調整
PDFファイルを手作業で編集する場合、xrefテーブルの値を正確に合わせる必要があります。これはPDFファイルの中でも特にミスが起きやすい部分です。
以下の3点を確認・修正してください:
- xrefテーブルのエントリ数:
xref直後の行(0 4)の2番目の数値が、ファイル内のオブジェクト数 + 1(フリーオブジェクト分)と一致していること - 各オブジェクトのバイトオフセット:各エントリの10桁の数値が、対応するオブジェクトのファイル先頭からのバイト位置を正確に指していること
startxrefの値:xrefという文字列が始まるバイト位置を正確に記載すること
「xrefという文字列が始まる直前の全てのバイトが、startxrefに記載すべき正確な位置となります。」バイナリエディタで実際のバイト位置を確認しながら値を調整するのが確実です。

次のステップ
このPart 4で学んだカタログ、ページツリー、ページオブジェクトの知識を使えば、白紙のPDFを自作できるようになりました。Part 5:テキストエディタでHello World PDFを自作するでは、いよいよ「Hello World」のテキストを含むPDFを作成します。そのためには、ストリームオブジェクトを使ってページにテキストコンテンツを追加する方法を学ぶ必要があります。
PDFアプリ開発ツール(SDK)をお探しのみなさま、効率のよい開発作業のためにJPedal、BuildVu、JDeliがきっとお役に立つことと思います。これら3製品は無料で試用していただけますので、まずはお試しのうえ、ぜひ導入をご検討ください。

開発者向けPDF入門ガイド
PDFの仕様や活用方法など、開発者に必要な情報をコンパクトにまとめました。初めてPDFを扱う開発者にも分かりやすく、基礎から応用までカバーしているため、PDFのポテンシャルを最大限に引き出し、アプリケーション開発やドキュメント管理の効率化を図るための手引きとなるでしょう。