PDFの内部構造 Part 3:テキストエディタで自作PDFを作成する
PDFの内部構造シリーズ第3回。テキストエディタとバイナリエディタを使って、ゼロからPDFファイルを手作りする手順を解説。ヘッダー、ボディ、クロスリファレンス、トレーラーの役割を実践的に学びます。

PDFは「見る」だけのフォーマットではありません。内部構造を理解すれば、テキストエディタだけでPDFファイルを作成することも可能です。本記事は「PDFの内部構造」シリーズの第3回として、前回までに学んだヘッダー・ボディ・クロスリファレンステーブル・トレーラーの知識を総動員し、実際にゼロからPDFファイルを組み立てる方法をステップバイステップで解説します。意図的にエラーを発生させることで、PDFリーダーがファイルのどの部分をどのように検証しているかも確認していきます。
必要なツール
自作PDFを作成するために、以下の2つのツールを用意してください。
- テキストエディタ -- メモ帳、VS Code、サクラエディタなど、プレーンテキストを編集できるものであれば何でも構いません。ただし、保存時にBOM(Byte Order Mark)が付加されない設定にしておく必要があります。
- バイナリエディタ(16進数エディタ) -- HxD(Windows用)、Hex Fiend(macOS用)などの16進数表示に対応したエディタを使用します。オブジェクトのバイトオフセットを正確に調べるために必要です。
ステップ1: 空のファイルを作成してテストする
まず、mypdf.pdf という名前の空のファイルを作成し、Adobe Acrobat Readerで開いてみましょう。以下のようなエラーメッセージが表示されるはずです。
"ファイルの種類がサポートされていないか、またはファイルが破損している可能性があります"
このメッセージは、ファイルがPDF形式として認識されなかったことを意味します。PDFリーダーはファイルの先頭にある「マジックナンバー」(%PDF-で始まるヘッダー)を探しに行きますが、空のファイルにはそれが存在しないためです。ここから1つずつ構造を追加していきます。
ステップ2: ヘッダーを追加する
PDFファイルの先頭には、必ずバージョン情報を含むヘッダーが必要です。テキストエディタで以下の1行を記述します。
%PDF-2.0この行は「このファイルはPDF 2.0仕様に準拠しています」という宣言です。PDF仕様のバージョンには1.0から2.0まで複数ありますが、ここでは最新の2.0を指定しています。%記号はPDFではコメント行の開始を意味しますが、ファイル先頭の%PDF-だけは特別にバージョン宣言として扱われます。
ステップ3: ボディセクションにオブジェクトを追加する
ヘッダーの次に、少なくとも1つのオブジェクトを含むボディセクションが必要です。ここでは、最もシンプルな空の辞書オブジェクトを追加します。
1 0 obj
<< >>
endobj1 0 obj-- オブジェクト番号1、世代番号0のオブジェクトの開始を宣言しています。<< >>-- 空の辞書(Dictionary)です。通常はここに/Type /Catalogなどのキーと値のペアが入りますが、今回はあえて空にしています。endobj-- オブジェクトの終了を示します。
ステップ4: クロスリファレンステーブルを作成する
クロスリファレンステーブル(xref)は、PDFリーダーがファイル内の各オブジェクトを素早く見つけるための「索引」です。各オブジェクトのバイトオフセット(ファイル先頭からの位置)を記録します。
xref
0 2
0000000000 65535 f
0000000010 00000 nxref-- クロスリファレンステーブルの開始宣言です。0 2-- オブジェクト番号0から始まり、合計2エントリが含まれることを示します。- 1行目(
0000000000 65535 f)-- オブジェクト0は「フリーオブジェクト」で、常にこの形式で記述されます。 - 2行目(
0000000010 00000 n)-- オブジェクト1はファイル先頭から10バイト目に位置しています(nは「使用中」の意味)。
ここで重要なのが、オブジェクト1のオフセット値(10)を正確に算出することです。バイナリエディタでファイルを開き、1 0 objの1が何バイト目にあるかを確認してください。%PDF-2.0は9バイト(文字8バイト+改行1バイト)なので、次の行は10バイト目(0始まりで0000000010)から始まります。改行コードがCR+LFの場合は10バイトになるため、環境に応じて値を調整してください。
ステップ5: トレーラーセクションを追加する
トレーラーはPDFファイルの末尾に配置され、クロスリファレンステーブルの位置とルートオブジェクトの情報を提供します。
trailer
<< /Size 2 /Root 1 0 R >>
startxref
33
%%EOFtrailer-- トレーラーセクションの開始です。/Size 2-- クロスリファレンステーブルのエントリ数です。/Root 1 0 R-- ドキュメントカタログ(文書のルートオブジェクト)への参照です。ここではオブジェクト1を指しています。startxref33 -- クロスリファレンステーブルの開始位置(バイトオフセット)です。この値もバイナリエディタで確認して正確に設定する必要があります。%%EOF-- ファイルの終端マーカーです。
完成したPDFファイル
すべてを組み合わせると、以下のような内容になります。
%PDF-2.0
1 0 obj
<< >>
endobj
xref
0 2
0000000000 65535 f
0000000010 00000 n
trailer
<< /Size 2 /Root 1 0 R >>
startxref
33
%%EOFわずか数行のテキストですが、これはPDFの4大構成要素(ヘッダー、ボディ、クロスリファレンステーブル、トレーラー)をすべて含んだ、構造的に正しいPDFファイルです。
結果を確認する
この内容を mypdf2.pdf として保存し、Adobe Acrobat Readerで開いてみましょう。今度は先ほどとは異なるエラーメッセージが表示されます。
"dictオブジェクトを指定してください"
最初の「ファイルが壊れている」というエラーは解消されました。PDFリーダーがファイルをPDFとして認識し、構造を解析できるようになったのです。しかし今度は、ルートオブジェクト(オブジェクト1)が空の辞書であるため、ドキュメントカタログとして必要なキー(/Type /Catalogや/Pagesへの参照など)が見つからないというエラーになっています。
これは次のステップへの重要な手がかりです。機能するPDFを完成させるためには、カタログオブジェクト、ページツリーオブジェクト、そして個々のページオブジェクトを正しく定義していく必要があります。
※ macOS版のAdobe Acrobat Readerでは、エラーメッセージの表示が異なる場合があります。また、プレビュー.appなど他のPDFリーダーでは挙動が異なることがあります。
まとめ
今回は、テキストエディタとバイナリエディタだけでPDFファイルをゼロから作成する方法を学びました。空のファイルから始めて、ヘッダー、ボディ、クロスリファレンステーブル、トレーラーを1つずつ追加していくことで、PDFリーダーのエラーメッセージがどのように変化するかを確認できました。PDFの内部構造を理解することは、PDF関連のソフトウェア開発やトラブルシューティングにおいて大きなアドバンテージとなります。
Part 4:白紙のPDFページを自作するでは、カタログオブジェクト、ページツリー、ページオブジェクトを正しく定義して、実際に開くことができるPDFファイルを完成させます。
PDFアプリ開発ツール(SDK)をお探しのみなさま、効率のよい開発作業のためにJPedal、BuildVu、JDeliがきっとお役に立つことと思います。これら3製品は無料で試用していただけますので、まずはお試しのうえ、ぜひ導入をご検討ください。

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