コンテンツへスキップ
100% ローカル · アップロード 0 KBPDF を圧縮

PDF がテキストと画像をどう格納するか(内部構造)

PDF は内部にバイナリの塊を持つテキストファイルです。構造は驚くほど読みやすい:ヘッダー、番号付きオブジェクトのリスト、相互参照テーブル、トレーラーです。本ページはその構造を順に追い、テキスト・フォント・画像が実際にどこに存在するかを示します。PDF ツールがファイルを読み書きする際に何をしているかを理解したい開発者や皆さん向けの内容です。

概要

PDF の 4 つの部分

すべての PDF ファイルは同じ 4 部構成のレイアウトを持ちます:

  1. ヘッダー — 1 行、例えば %PDF-1.7、バージョンを宣言します。
  2. ボディ — 番号付き間接オブジェクトの列。すべてがここに存在します:ページ、フォント、画像、メタデータ。
  3. 相互参照テーブル(xref) — 各オブジェクトのバイトオフセット。リーダーはファイル全体を走査せずにオブジェクトを見つけられます。
  4. トレーラー — ルートオブジェクト(文書カタログ)、xref の位置、任意の暗号化情報を指します。リーダーはまずトレーラーを読み、次にルートへジャンプします。

トレーラー先読み設計こそが、PDF をファイル全体を読み込むことなく定数時間で読める理由です — 巨大文書やストリーミングリーダーにとって重要です。

ボディ

間接オブジェクト

ボディ内の各オブジェクトは番号を持ち、次のような形をしています:

3 0 obj
<< /Type /Page /MediaBox [0 0 612 792] /Contents 4 0 R /Resources << /Font << /F1 5 0 R >> >> >>
endobj

3 0 obj は「これはオブジェクト 3、世代 0」を意味します。<< ... >> 内のボディは辞書 — キー/値ペアのリスト — です。値は数値、文字列、名前(/ 接頭辞)、配列、他の辞書、または 4 0 R(「オブジェクト 4 を見よ」)のような参照です。これが、ページがコンテンツストリームやフォントをインライン化せずに指す仕組みです。

世代番号は通常 0 です。差分更新でオブジェクトを削除して書き直す際に増えます — これにより PDF はファイル全体を書き直さず末尾に変更を追加して編集できます。ほとんどの現代ライタはファイル全体を書き直すため、世代は 0 のままです。

コンテンツ

テキストの格納方法

ページコンテンツはコンテンツストリームに存在します — 値が(多くの場合 Flate 圧縮された)バイトストリームであり、PDF 描画演算子を含むオブジェクトです。テキストは BT(テキスト開始)、Tf(フォントとサイズ設定)、Td(位置移動)、Tj(テキスト表示)、ET(テキスト終了)のような演算子で描画します。単純な「Hello」は概ね次のようになります:

BT
/F1 24 Tf
100 700 Td
(Hello) Tj
ET

文字列 (Hello) はフォントのエンコーディングを使います。単純なラテンテキストでは WinAnsiEncoding やフォント組み込みのエンコーディングがよく使われます。Unicode テキストでは、文字列は 16 ビットエンコードのバイト文字列で、フォントは /ToUnicode マッピングを持ち、リーダーはコピー&ペーストや検索のために実際のコードポイントを復元できます。PDF でコピー&ペーストが文字化けするのに表示テキストは正しい場合、/ToUnicode マップが欠落または誤り — よくあるエクスポートバグです。

テテキストは HTML や段落として格納されません。意味的な「段落」オブジェクトは存在しません。行、改行、位置はすべて明示的な描画コマンドです。これが、異なるページ幅に PDF をリフローするのが難しい理由です:リーダーは描画コマンドからレイアウトを逆推定しなければなりません。

フォント

フォントの埋め込み

PDF のフォントオブジェクトは 2 つの部分を持ちます:フォントに名前を付けそのデータを指すフォント辞書と、ストリームとして埋め込まれたフォントプログラム自体(Type 1、TrueType、または OpenType)です。PDF 仕様は文書がどこでも同一に描画されるためフォントの埋め込みを要求しますが、強制はしません — これが一部の PDF がフォントを持たないマシンで代替フォントを使う理由です。

サブセット化は文書で実際に使われるグリフのみを埋め込みます。1 つの見出しに使われた 250 KB のフォントが 8 KB のサブセットになります。ほとんどの現代エクスポータは既定でサブセット化します。古いエクスポータや一部の「PDF に印刷」パスはフォント全体を埋め込むことがあり、ファイル肥大化のよくある原因です。完全な内訳はファイルサイズガイド を参照してください。

画像

画像の格納方法

画像は XObject(外部オブジェクト)として格納されます — 生または圧縮されたピクセルデータのストリームと、幅・高さ・カラースペース・成分あたりビット数・フィルタを記述する辞書です。ページコンテンツストリームは Do 演算子で画像を描画します。

PDF は複数の画像フィルタ(圧縮)をサポートします:

  • DCTDecode — JPEG。非可逆、写真に適します。画像中心の PDF でサイズの最も一般的な源です。
  • FlateDecode — zlib/deflate。非可逆圧縮ではなく、ラインアートや色数の少ない画像に適します。
  • JPXDecode — JPEG2000。高品質では JPEG より良い圧縮ですが、遅く、汎用性に劣ります。
  • JBIG2Decode — 2 値(1 ビット)スキャンテキスト向け。スキャンページを劇的に縮小できます。
  • CCITTFaxDecode — 2 値画像向けの古典的 FAX 圧縮。

画像中心の PDF は主に圧縮画像ストリームの列です。より攻撃的な JPEG 品質でそれらのストリームを再エンコードするのが、スキャン文書で最大のサイズ削減手段です。

索引

xref テーブルとトレーラー

xref テーブルは各間接オブジェクトのバイトオフセットを列挙します。リーダーはファイルを開き、トレーラー(ファイル末尾からの既知オフセットに位置)を読み、xref を見つけ、ファイル全体を解析せずに任意のオブジェクトへ直接ジャンプするために使います。これが PDF をランダムアクセス可能にする仕組みです。

PDF 1.5 は xref テーブル自体を圧縮する相互参照ストリームを追加しました。オブジェクトストリーム(多数の小さなオブジェクトを 1 つの圧縮ストリームに詰め込む)と組み合わせることで、これが現代 PDF を 1.4 相当より小さくする構造的圧縮です。実用上の効果は圧縮の解説 を参照してください。

トレーラーは /Root(ページツリーを列挙する文書カタログ)、/Info 辞書(Title、Author、CreationDate — メタデータ)、ファイルが暗号化されている場合は任意で /Encrypt 辞書も指します。

圧縮

ストリームとフィルタ

値がバイナリデータである任意のオブジェクト(コンテンツストリーム、画像、埋め込みフォント)はストリームです:辞書、stream、生バイト、endstream の順です。辞書の /Filter エントリがリーダーにバイトの復号方法を伝えます — zlib なら FlateDecode、JPEG なら DCTDecode など。複数のフィルタは連鎖できます。例:復号されてからダウンサンプリングされる画像。

これが「PDF の圧縮」が単一の操作ではない理由です。各ストリームは独立に独自のフィルタで圧縮されます。オブジェクトストリームで再保存する圧縮ツールは構造的オーバーヘッドを縮小し、より攻撃的な JPEG 品質で画像を再エンコードすると画像ストリームを縮小します。これらは独立したレバーです。

実践

PDF ツールにとって何が重要か

IXPDF のようなツールが PDF を結合・分割・回転する際、xref を解析し、ページツリーを走査し、ページオブジェクトをコピーまたは並べ替え、xref とトレーラーを書き直します。コンテンツストリーム(テキストと画像)はバイト単位でコピーされます — 再エンコードなし、品質劣化なし。これが構造的操作が高速で非可逆圧縮を伴わない理由です:オブジェクト参照をシャッフルするだけで、ページを再描画しません。

圧縮は例外です:オブジェクトストリームで構造を再直列化します。それでも、ユーザーが明示的に再エンコードを求めない限り、画像とフォントのストリームはそのままコピーされます。

これらはすべて Web Worker 経由でブラウザ内で行われます — ファイルはローカルで解析・変更・再直列化されます。あなたの PDF はデバイスを離れません。

関連記事

関連リソース