日本語
法人版

ゼロから手書きの PDF(01):Hello, World——最小限の利用可能な PDF を構築する

Doclingo Team2026年1月30日

ゼロから手書きの PDF(01):Hello, World——最小限の利用可能な PDF を構築する

シリーズの目標:PDF を読み取り可能なファイル形式として理解する——まずは「動く」最小の例から始め、徐々にグラフィックス、多ページ、圧縮、リソースの再利用に拡張します。

シリーズ目次

  • 第 01 篇(本文):最小の PDF を手書き(1 ページ + 1 行のテキスト)し、ツールを使って開ける標準 PDF に補完します
  • 第 02 篇:コンテンツストリーム内で線を描く/矩形を描く(パス、ストローク、塗りつぶしを理解する)
  • 第 03 篇:多ページ PDF(Pages ツリーの成り立ち)
  • 第 04 篇:より現実世界に近づく(圧縮ストリーム、リソースの再利用、オプション構造など)

なぜ PDF の底層構造を理解する必要があるのか?

PDF(Portable Document Format、ポータブル文書形式)は、今日最も人気のあるページ記述言語の一つです。HTML/CSS のように「コンテンツと表示を分離し、再フロー可能」という考え方とは異なり、PDF はレイアウトが固定され、見たまま得られることを強調しています——どのデバイスで開いても、レイアウトは一貫しています。

PDF の底層構造を理解することにはいくつかの実際的な利点があります:

  • PDF 生成の問題をデバッグする:コードライブラリを使って PDF を生成する際にエラーが発生した場合、底層構造を理解していれば問題を迅速に特定できます
  • 自動化処理:テキストの一括抽出、文書の結合、透かしの追加などの操作は、構造を理解した上で正確に行うことができます
  • セキュリティ監査:PDF に埋め込むことができるコンテンツ(JavaScript、添付ファイル、フォームなど)を理解することで、セキュリティ分析に役立ちます
  • ファイル形式設計の学習:PDF の「オブジェクトグラフ + ランダムアクセス」設計は古典的な例であり、学ぶ価値があります

準備作業

この記事では、まず「構造は不完全だが論理的には正しい」hello-broken.pdf を作成し、その後 pdftk を使って重要な構造を自動的に補完し、hello.pdf を出力します。

  • 必要なツールpdftk(無料のコマンドラインツール、Windows/macOS/Linux に対応)
  • 出力ファイルhello-broken.pdf(手書き)、hello.pdf(修正後に開ける)

核心概念:PDF の三層構造

PDF を理解する上で最も重要なのは、三層のメンタルモデルを構築することです:

PDF 三層メンタルモデル

1. オブジェクト層(Document Content)

PDF 文書は多くのオブジェクトで構成されており、オブジェクト同士は間接参照(例:2 0 R)で接続されています。一般的なオブジェクトタイプ:

タイプ説明
Name/Page/ で始まる名前
整数/実数5036.0数値
文字列(Hello, World!)丸括弧で囲まれた文字列
配列[0 0 612 792]順序付き集合
辞書<< /Type /Page >>キーと値のペアの集合
間接参照2 0 Rオブジェクト 2(生成番号 0)を参照
ストリームstream...endstreamバイナリデータ(描画命令、画像など)

2. コンテンツ層(Page Content)

実際に「テキスト/グラフィックスをページに描く」命令のシーケンスは、通常 stream ... endstream の中に書かれます。形式は:オペランドが前、オペレーターが後です。

/F0 36 Tf          ← オペランド: /F0, 36  オペレーター: Tf(フォントを設定)
(Hello, World!) Tj ← オペランド: 文字列   オペレーター: Tj(テキストを描画)

3. ファイル構造層(File Structure)

リーダーが任意のオブジェクトに迅速にランダムアクセスできるようにするために、ファイルの先頭から最後まで読む必要がありません:

要素役割
%PDF-1.xファイルヘッダー、PDF バージョンを識別
xrefクロスリファレンステーブル:オブジェクト番号 → バイトオフセット
trailerテール辞書:ルートオブジェクト /Root を指す
startxrefxref テーブルの開始位置を示す
%%EOFファイル終了マーク

最小 PDF に必要なオブジェクトは?

「最小限だが文字を表示できる」PDF のオブジェクト間の参照関係は以下の通りです:

PDF 最小オブジェクト関係図

最小オブジェクトリスト

オブジェクト役割重要なフィールド
Catalogルートオブジェクト、文書のエントリ/Type /Catalog, /Pages
Pagesページツリー/Type /Pages, /Kids, /Count
Page単一ページ/Type /Page, /MediaBox, /Resources, /Contents, /Parent
Resourcesリソースコンテナ/Font(フォント辞書)
Fontフォント定義/Type /Font, /BaseFont, /Subtype
Contentsコンテンツストリーム描画命令のストリーム

実戦:手書き hello-broken.pdf

新しいファイル hello-broken.pdf を作成し、以下の内容を完全に貼り付けます:

%PDF-1.0
1 0 obj
<< /Type /Pages
   /Count 1
   /Kids [2 0 R]
>>
endobj

2 0 obj
<< /Type /Page
   /MediaBox [0 0 612 792]
   /Resources 3 0 R
   /Parent 1 0 R
   /Contents [4 0 R]
>>
endobj

3 0 obj
<< /Font
     << /F0
          << /Type /Font
             /BaseFont /Times-Italic
             /Subtype /Type1 >>
     >>
>>
endobj

4 0 obj
<< >>
stream
1. 0. 0. 1. 50. 700. cm
BT
 /F0 36. Tf
 (Hello, World!) Tj
ET
endstream
endobj

5 0 obj
<< /Type /Catalog
   /Pages 1 0 R
>>
endobj

xref
0 6
trailer
<< /Size 6
   /Root 5 0 R
>>
startxref
0
%%EOF

なぜこのファイルは「壊れている」のか?

以下の内容を故意に省略または誤って記入しました:

欠落/誤り項目説明
xref オフセット各オブジェクトの実際のバイトオフセットが記入されていない
startxref0 と記入されており、xref の実際の位置ではない
/Lengthコンテンツストリームの長さが宣言されていない
バイナリマークヘッダーにバイナリ識別行が欠落している

これらはリーダーに必要な重要な情報であり、欠落すると開けないか、エラーを許容して開くことしかできません。


重要なコンテンツストリーム命令の詳細

コンテンツストリームはオブジェクト 4 0 objstream ... endstream の間にあり、行ごとに説明します:

1. 0. 0. 1. 50. 700. cm   ← 変換行列を設定(注意:1. は浮動小数点数 1.0 を表す)
BT                         ← テキストオブジェクトの開始
 /F0 36. Tf                ← フォント F0 を選択し、フォントサイズ 36pt を設定
 (Hello, World!) Tj        ← 文字列を描画
ET                         ← テキストオブジェクトの終了

変換行列 cm オペレーター

1 0 0 1 50 700 cm は 6 要素の変換行列 [a b c d e f] に対応し、次のようになります:

| a  b  0 |     | 1  0  0 |
| c  d  0 |  =  | 0  1  0 |
| e  f  1 |     | 50 700 1 |

a=1, b=0, c=0, d=1 の場合、これは純粋な平行移動行列であり、座標系の原点(後の描画操作の (0,0) 点)を (50, 700) に移動します。移動しない場合、デフォルトの原点はページの左下隅にあります。

テキストオペレーター

オペレーター意味
BTBegin Text、テキストオブジェクトの開始BT
ETEnd Text、テキストオブジェクトの終了ET
Tfフォントとフォントサイズを設定/F0 36 Tf
Tj文字列を描画(Hello!) Tj

pdftk を使って開ける PDF に修正する

hello-broken.pdf があるディレクトリで次のコマンドを実行します:

pdftk hello-broken.pdf output hello.pdf

任意の PDF リーダーで hello.pdf を開くと、ページ上に "Hello, World!"(Times-Italic フォント、36pt、ページの左上に位置)が表示されるはずです。

pdftk が補完したものは?

補完項目説明
バイナリマーク行%PDF-1.0 の後に印刷できない文字を追加し、バイナリファイルとして認識されるようにします
/Lengthコンテンツストリームのバイト長を計算して追加します
xref テーブル各オブジェクトのバイトオフセットを計算して記入します
startxrefxref テーブルの実際の開始位置を記入します

なぜ xref / trailer / startxref が必要なのか?

核心目的:ランダムアクセス

500 ページの PDF を想像してみてください。xref がなければ、リーダーは 450 ページを表示するために最初から 449 ページまで解析しなければなりません——これは非常に遅いです。

xref があれば、リーダーは次のようにできます:

  1. まず startxref を読み取る → xref の位置を見つける
  2. trailer を読み取る → ルートオブジェクト /Root を見つける
  3. ルートオブジェクトから順にたどる → 直接 450 ページオブジェクトにジャンプ
  4. xref を使ってそのオブジェクトのバイトオフセットを確認 → 直接シークして読み取る

時間計算量が O(n) から O(1) に減少します


本篇の練習

実際に hello-broken.pdf を修正し、再度 pdftk で修正して効果を観察することをお勧めします:

練習修正内容観察点
A(Hello, World!) を他の英語の短文に変更テキストの変化
B3612 または 72 に変更フォントサイズの変化
C50 70050 100 に変更位置が下に移動(PDF 座標系の原点は左下)
D/Times-Italic/Helvetica または /Courier に変更フォントの変化
E/MediaBox [0 0 612 792][0 0 595 842] に変更用紙が US Letter から A4 に変わる

ヒント:PDF 座標系の原点はページの左下隅にあり、Y 軸は上向きです。(50, 700) は左から 50pt、下から 700pt の位置を示します。


よくある質問

Q: なぜ Type1 内蔵フォントを使用するのか? TrueType ではなく?

A: Type1 の 14 種類の標準フォント(Times、Helvetica、Courier など)は PDF リーダーに必ず内蔵されており、フォントファイルを埋め込む必要がなく、最も簡単です。実際のシナリオでは、プラットフォーム間の一貫性を保証するためにフォントを埋め込む必要があります。

Q: /MediaBox [0 0 612 792] のこれらの数字は何ですか?

A: 単位はポイント(1 ポイント = 1/72 インチ)です。612 × 792 ポイント = 8.5 × 11 インチ = US Letter 用紙。A4 は 595 × 842 ポイントです。

Q: 生成番号(例:2 0 R0)とは何ですか?

A: 増分更新に使用されます。オブジェクトが変更されると、生成番号が 1 増加します。新しく作成された PDF では、すべてのオブジェクトの生成番号は通常 0 です。


次回予告

第 02 篇では、「手書きのコンテンツストリーム」の方法を引き続き使用し、最も基本的なグラフィックパス操作を追加します:

  • m(moveto)、l(lineto):パスを定義
  • S(stroke):ストローク
  • re(rectangle)、f(fill):矩形を描画して塗りつぶす

同じページに同時に描画できるようにします:タイトルテキスト + 水平分割線 + 矩形ボックス、「文字が書ける」から「図形が描ける」へと進化します。

Copyright © 2026 Doclingo. All Rights Reserved.
特定商取引法に基づく表記
製品
ドキュメント翻訳
その他のツール
API
法人版
リソース
料金プラン
アプリ
について
ヘルプ
サービス条件
プライバシーポリシー
バージョンアップデート
ブログ
連絡先情報
メール:support@doclingo.ai
日本語
Copyright © 2026 Doclingo. All Rights Reserved.
特定商取引法に基づく表記