コンテンツにスキップ
getnextpdf.com

PDF ファイルサイズの経済学

Spec: ISO 32000-2, §7.5.7

2 つの PDF は、画面上でピクセル単位で同一に見えながら、ディスク上で 10 倍も異なり得ます。その差は、ほとんど決してあなたが見るコンテンツではありません。それは、ファイルが下でどう組み立てられたかです。このページはサイズ経済学の案内です。PDF のバイトが実際にどこへ行くのか、そして作者がバイトの予算を費やす 4 つのレバーです。

それは ストリームとフィルターフィルタがどうデコードするか を扱う — に対する なぜファイルは大きいのか の伴侶です。こちらは予算に留まります。

ファイルサイズが虚栄の指標であることはめったにありません。それは、すべてのダウンロードにおける帯域幅、すべてのアーカイブにおけるストレージ、そしてすべてのプレビューにおけるレイテンシです。400 KB であるべきだった 12 MB のインボイスは、それを月に 100 万通生成するとき、装飾上の問題ではありません — それは 30 倍の請求書です。

苛立たしいのは、肥大化が通常は不可視であることです。文書は正しくレンダリングされ、問題なく開き、問題なく印刷されます。同じアーティファクトがほんの一部のサイズになり得たことを、何も告げません。無駄なバイトが視覚的ではなく構造的だからです。それらを見つけるには、ページではなく予算を見なければなりません。

PDF を、4 つの費目に費やす予算だと考えてください。

  • オブジェクトごとのオーバーヘッド。 すべての間接オブジェクトは N G obj / endobj のエンベロープを携え、1 つのクロスリファレンスエントリによって追跡されます。ページの多い文書には何千もの小さなオブジェクトがあり、エンベロープは積み上がります。オブジェクトストリーム は、オブジェクトのまるごとのグループについてそのエンベロープを落とします。詰め込まれた各オブジェクトはなお自身のクロスリファレンスエントリを保ちますが、コンパクトな type-2 エントリとしてです。
  • 画像バイト。 写真やスキャンを伴う任意の文書では、画像が支配し、最大のレバーは フィルタの選択 です — 可逆コーデック対非可逆画像コーデックは、メガバイトとキロバイトの差です。
  • フォントバイト。 フル埋め込みフォントは、あなたが決して使わないグリフの何百キロバイトです。サブセット化 は、文書が実際に描くグリフだけを保ちます。
  • インデックス。 リーダーがすべてのオブジェクトを見つけられるようにするクロスリファレンステーブルは、それ自体がプレーンテキストではなく 圧縮ストリーム になり得ます。

4 つすべてを正しくすればファイルは小さい。1 つでも外せば、あなたがうまくやった他のすべてを、それが支配します。

NextPDF のライターは単一パスのストリーミングシリアライザです。各オブジェクトのバイトを生成されるそばから追記し、それぞれについて古典的な使用中のクロスリファレンスエントリを記録します。そのデフォルトは速く、予測可能で、バイト安定なファイルを生成します — しかしそれは 可能なかぎり最小の レイアウトではなく、NextPDF はそれについて正直です。

PDF は間接オブジェクトのグラフです。その大半は小さなディクショナリです。ページノード、注釈ディクショナリ、構造ツリー要素、アウトラインエントリです。それぞれが固定の税 — obj / endobj キーワード、オブジェクト番号と世代番号、そしてそれを位置特定するクロスリファレンスエントリ — を払います。何千もの小さなオブジェクトを持つ文書では、その税はファイルの意味あるひと切れです。

オブジェクトストリーム は、それらの小さな非ストリームオブジェクトの多くを 1 つのストリームに集め、まとめて圧縮します(Spec: ISO 32000-2, §7.5.7)。obj / endobj のエンベロープはグループ全体について落とされ、内側の値はオブジェクトごとのキーワードなしに連続して格納され、それから単一のブロックとして deflate されます — これはまた、よりよく 圧縮します。重複除去する圧縮器が、それら似たディクショナリすべてを一度に見るからです。クロスリファレンスエントリは消えません — 詰め込まれた各オブジェクトはなお 1 つを必要とします — が、クロスリファレンスストリーム内のコンパクトなバイナリ type-2 エントリへ縮みます(これについては Lever 2 でさらに)。

NextPDF では、これは ObjectStreamPacker によって届けられます。完成したクロスリファレンスストリーム PDF を取り、適格なオブジェクトを単一の /Type /ObjStm へ書き換える、自己完結したポストプロセッサです。それが従うルールは、標準からそのまま来ます。適格なオブジェクトとは、世代ゼロの非ストリームオブジェクトであり、直接アドレス可能なまま留まらなければならない特別なオブジェクトを除外した後のものです。§7.5.7 はオブジェクトストリーム内へのストリームオブジェクトの格納を禁じるので、ストリームオブジェクト — コンテンツ、フォント、画像 — は自身のエントリを保ちます。そして ObjectStreamPacker はさらに、文書自身のクロスリファレンスストリームオブジェクト(それは書き換えられる)と /Encrypt ディクショナリ — どちらも直接アドレス可能なまま留まらなければならない — を辞退します。それ以外の世代ゼロで非ストリームのものはすべて詰め込まれます。

Object stream packing (ObjStm) — edition availability
EditionAvailability
CoreFull support in the open-source core via ObjectStreamPacker. It is opt-in: the single-pass writer’s default emits classic in-use entries, so output stays byte-identical unless you enable packing. The packer is deterministic, with its own reproducible golden baseline.
ProNot in this edition
EnterpriseNot in this edition

オプトインは、隠れた限界ではなく意図的な姿勢です。デフォルト出力はバイト安定で、既存のゴールデンベースラインと一致します。パッキングをオンにすることは、異なる、より小さく、等しく決定論的なレイアウトへオプトインすることです。あなたがそのトレードを選び、エンジンが背後でそれを決めることは決してありません。

いったんオブジェクトがオブジェクトストリームの内側に存在すると、それらを指すインデックスは形を変えます。リーダーは、詰め込まれたオブジェクトを 圧縮された クロスリファレンスエントリ — オブジェクトストリームとその中のインデックスを名付ける type-2 エントリ — を通して見つけます(Spec: ISO 32000-2, §7.5.8.3)。クロスリファレンス全体がそれ自体 /Type /XRef ストリームなので、何千ものオブジェクトのインデックスは、プレーンテキストの行として書かれるのではなく、バイナリに詰められ deflate されます。地図は領土とともに縮みます。

ObjectStreamPacker はまさにこれを再構築します。単一のオブジェクトストリームを送出し、それから、保持された各オブジェクトにコンパクトな type-1 エントリを、詰め込まれた各オブジェクトに type-2 エントリを携える書き換えられたクロスリファレンスストリームを送出し、すべてのオブジェクト番号を保存して既存の参照が有効なまま保たれるようにします。

実画像を伴う任意の文書では、このレバーが他を圧倒します。バイトは同じ絵で、コーデックが予算です。フィルタの 名前 は標準のフィルタセットに存在し(Spec: ISO 32000-2, §7.4)、それらの間の選択はサイズの決定です。

  • FlateDecode は可逆です。線画、スクリーンショット、そしてフラットな色を持つあらゆるものに完璧です — そして写真には破滅的です。写真では可逆は 元のあらゆるバイト を意味します。
  • DCTDecode は JPEG です。非可逆で、写真にとっては大差で正しい選択であり、しばしば、誰も気づかない品質低下と引き換えに 10 倍の削減です。
  • JPXDecode は JPEG 2000 です。異なる品質/サイズ曲線を持つウェーブレット圧縮ですが、サポートにムラがあり、一部のアーカイブプロファイルで許されません。

これこそ フィルタがどうデコードするか が重要になる場所で、それは ストリームとフィルター の記事の仕事です。経済学のポイントはより狭いものです。可逆で格納された写真は、不必要に巨大な PDF の最も一般的な単一の原因であり、本当の重みが 1 つの JPEG 化されていないスキャンであるファイルを、どれほどのオブジェクトストリームパッキングも救いません。

埋め込みフォントはプログラムです。フルなものは数百キロバイトになり得ます。書体デザイナーがかつて描いたすべてのグリフ — この文書で決して使わないスクリプトにまたがる何千もの文字 — を携えるからです。サブセット化 は、文書が実際に描くグリフだけを埋め込み、そのプログラムをそれ自身のほんの一部へと変えます。1 ページの手紙は、CJK 対応フォントのまるごとを必要としません。それが組むほんの数十のグリフを必要とします。グリフがどう選ばれ再インデックスされるかの仕組みは、それ自身の主題です — フォント: 難しいところを参照してください。

  1. Image bytesFor media-heavy files, the largest line item. The filter choice — lossless Flate vs a lossy image codec like DCT (or JPX in a lossy mode) — is the dominant size lever.
  2. Font bytesA full embedded font is mostly glyphs you never draw. Subsetting keeps only the used glyphs, often shrinking the program by an order of magnitude.
  3. Per-object overheadThousands of small dictionaries each pay an obj/endobj envelope plus a cross-reference entry. Object streams (ObjStm) drop the envelope for the whole group; each object keeps a compact type-2 cross-reference entry.
  4. The indexA compressed cross-reference stream with type-2 entries binary-packs and deflates the map of every object, replacing plaintext index rows.
Where the bytes go: the four levers an author spends a PDF's size budget on, in the order they typically dominate a real file.

ここで示す捏造された API はありません。最も重要なサイズの決定は、バイトがライターに届く に下されるからです — そして NextPDF が公開する 1 つの構造的レバーは、単一のオプトインです。概念的には、予算はこう読めます。

  • 写真を JPEG として与え、可逆に再エンコードされるのではなく DCTDecode の下に着地するようにする。最大の勝ちは、ライターのフラグではなく ソースデータ についての選択です。
  • ライターに埋め込みフォントをサブセット化させ、描かれるグリフだけが出荷されるようにする。
  • 多くの小さなオブジェクトを持つ文書では、オブジェクトストリームパッキングにオプトインする。これは完成したファイルを ObjectStreamPacker に通し、小さな非ストリームオブジェクトをグループ化してクロスリファレンスストリームを書き換えます。

デフォルトの経路 — 古典的な使用中エントリ、ObjStm なし — が正しいベースラインです。決定論的で、バイト安定で、検証しやすい。パッキングは、画像の重みではなくオブジェクト数がファイルを膨らませているときの、熟慮されたアップグレードです。

罠は、「PDF を圧縮する」ボタンに手を伸ばし、それがすべてを直すと期待することです。圧縮は 1 つのレバーではありません。4 つであり、互いに代替しません。オブジェクトストリームパッキングは写真を縮められません — それは画像フィルタの仕事です。完璧な JPEG は、あなたがサブセット化し忘れたフォントを埋め合わせられません。そして、本当の肥大化が、誰も DCTDecode を選ばなかったために可逆で格納された 10 MB のスキャンであるなら、そのどれも役に立ちません。

第 2 の誤解は、小さいほうが常に厳密に優れているというものです。そうではありません。オブジェクトストリームはリニアライゼーション — 1 ページ目が早くストリーミングされるよう絶対的なオブジェクト配置を固定する fast-web-view のレイアウト — と非互換です。そしていくつかのアーカイブプロファイルは、どの画像フィルタがそもそも許されるかを制限します。サイズは 1 つの軸です。それはストリーミング、アーカイブ適合性、そして再現可能性とトレードオフし、曲線上の正しい点は、その文書が何のためのものかに依存します。

4 つのレバーは、ファイルサイズの 構造的 な経済学です。それらは普遍的な「小さくする」保証ではなく、NextPDF は任意の受信 PDF の再最適化器であるふりをしません。

ObjectStreamPacker は、正しさを決して危険にさらさないベストエフォートの最適化です。それは辞退します — 入力を変更せず返します — ファイルがクロスリファレンスストリーム PDF でないとき、暗号化されているとき、電子署名を携えるとき(オブジェクトを並べ直せば署名が守るバイト範囲がずれる)、すでにオブジェクトストリームを含むとき、あるいは詰め込む適格なオブジェクトがないとき、です。それは文書全体について、ちょうど 1 つのオブジェクトストリームを送出します。/Extends コレクションへの分割はしません。それは範囲外であり、NextPDF が生成する文書サイズには準拠的です。

画像とフォントのレバーは、おおむね 入力 についての決定です。NextPDF のライターは、可逆ビットマップを非可逆画像ストリームへ暗黙に変換しません — 写真を DCTDecodeJPXDecode へ再エンコードすることは、シリアライザが背後でするのではなく、明示的な上流の画像エンコーディングの決定です — また、呼び出し側がフルで埋め込むよう求めたフォントからグリフを取り戻すこともしません。最大のサイズの勝ちはバイトシリアライザの上流で下されます。エンジンの仕事は、あなたが持ち込む予算を無駄にしないことです。

  • Streams and filtersフィルタがどうデコードするか の伴侶。このページは意図的にそれを補完し、複製しません。
  • What a PDF actually is — オブジェクトストリームのレバーがそのオブジェクトごとのオーバーヘッドを減らす、間接オブジェクトモデル。
  • The anatomy of a PDF file — 圧縮ストリームになるクロスリファレンス構造。
  • Fonts: the hard part — サブセット化が、文書が描くグリフをどう選び再インデックスするか。
  • オブジェクトストリーム(ObjStm) — 多くの小さな非ストリーム間接オブジェクトをまとめて圧縮して保持する単一のストリームで、obj / endobj のエンベロープがグループについて落とされる。詰め込まれた各オブジェクトはなお自身のクロスリファレンスエントリを、コンパクトな type-2 エントリとして保つ。NextPDF ではオプトイン。
  • 間接オブジェクト(Indirect object) — PDF のグラフ内の番号付きオブジェクトで、obj / endobj のエンベロープに包まれ、クロスリファレンスインデックスによって追跡される。エンベロープがオブジェクトごとのオーバーヘッド。
  • クロスリファレンスストリーム(Cross-reference stream) — すべてのオブジェクトをインデックス化する /Type /XRef ストリームで、プレーンテキストの行として書かれるのではなくバイナリに詰められ deflate される。
  • 圧縮(type-2)エントリ(Compressed (type-2) entry) — オブジェクトストリームの内側に存在するオブジェクトを指すクロスリファレンスエントリで、ストリームとその中のインデックスを名付ける。
  • フォントのサブセット化(Font subsetting) — フル書体ではなく、文書が実際に描くグリフだけを埋め込み、埋め込みフォントプログラムを縮めること。
  • 可逆対非可逆フィルタ(Lossless vs lossy filter) — 可逆コーデック(FlateDecode)はあらゆるバイトを再現する。非可逆画像コーデック(DCTDecode、または非可逆モードの JPXDecode)は、はるかに小さな結果と引き換えに知覚できない詳細を捨てる。(JPEG 2000 / JPX は可逆にも設定できる。)この選択は、メディアの重いファイルにおける支配的なレバー。