PDFの構造と、結合してもサイズが減らない理由
PDFを結合すると、ファイルサイズは素直に足し算になる。圧縮されることも、重複がまとめられることもない。
実際に測った。同じロゴ画像を載せた3つのPDFを作り、結合して中身を数えている。使ったのはこのサイトのツールと同じ pdf-lib で、結合の手順も同じだ。
| サイズ | ページ | 画像オブジェクト | |
|---|---|---|---|
| a.pdf | 1,596,969 B | 3 | 1 |
| b.pdf | 1,596,225 B | 2 | 1 |
| c.pdf | 1,597,711 B | 4 | 1 |
| 合計 | 4,790,905 B | 9 | 3 |
| merged.pdf | 4,789,255 B | 9 | 3 |
100.0%。 減ったのは1,650バイトだけで、これはファイルの先頭と末尾にある管理情報が1つにまとまった分にすぎない。
そして注目したいのは右端の列だ。まったく同じ画像が、3つ入っている。
PDFは「オブジェクトの入れ物」
PDFの中身は、番号の付いたオブジェクトの集まりになっている。ページ、フォント、画像、テキストの描画命令が、それぞれ別のオブジェクトとして並び、互いを番号で参照し合う。
ここで大事なのは、1つのファイルの中では、同じものが共有されることだ。
上の表で a.pdf は3ページあるのに、画像オブジェクトは1個しかない。3ページとも同じロゴを表示しているが、実体は1つで、各ページが「N番のオブジェクトをここに描け」と参照しているだけだ。だから a.pdf は3ページでも1.6MB弱で済んでいる。
結合は「ページを継ぎ足す」だけ
結合するとき、ツールがやっているのは次のことだ。
1. 空のPDFを作る2. 元のファイルからページを取り出し、そのページが参照しているオブジェクトごとコピーする3. コピーしたページを新しいファイルに追加する
「そのページが参照しているオブジェクトごと」がすべてだ。a.pdf のページはa.pdf内の画像を参照しているので、その画像も一緒に運ばれてくる。b.pdf のページも、同じ見た目の画像を運んでくる。c.pdf も同じだ。
結果、元は1ファイルに1つだった画像が、3ファイル分そのまま並ぶ。中身が1バイトも違わなくても、別々のオブジェクトとして扱われる。
ファイルをまたいだ重複の検出は行われない。同じ画像かどうかを判定するには、全オブジェクトの中身を突き合わせる必要があり、結合という操作の範囲を超える。
ツールによっては少し縮む
別の実験もした。同じ構成のPDFを3つ用意し、3種類のツールで結合して比べた(こちらは上とは別に作ったファイルで、元の合計は1,692,108バイト)。
| 結合に使ったもの | 出力サイズ | 元の合計比 |
|---|---|---|
| pdftk | 1,690,582 B | 99.9% |
| qpdf | 1,353,015 B | 80.0% |
qpdf は2割小さくなった。 ただしこれは重複をまとめたからではない。結合後の画像オブジェクトは3個のままで、中に入っているデータを圧縮し直している。
つまり「結合したから小さくなった」のではなく、「ついでに圧縮をかけ直したから小さくなった」だけだ。結合と圧縮は別の操作で、たまたま同じツールが両方やっているに過ぎない。
このサイトのツールは再圧縮をしない。元のページをそのまま運ぶので、出力は上の表のとおり合計と同じになる。中身を勝手に作り変えないほうが、結合ツールとしては素直だと考えている。
実務で効いてくるところ
スキャンした書類を大量に結合するときに、この性質が出る。
1枚あたり数百KBのスキャンPDFを50個まとめれば、出力は単純にその合計になる。「まとめたのに軽くならない」のではなく、まとめても軽くなる要素がない。
小さくしたいなら、結合とは別に圧縮をかける必要がある。そして圧縮は、画像の解像度や画質を落とすことで効く。つまり中身が変わる操作で、結合のように「そのまま運ぶ」わけにはいかない。
この2つを混ぜないほうが、後で困らないと思う。
計測の条件
pdf-lib(このサイトのツールが使っているもの)で、900×600のノイズ画像を埋め込んだA4のPDFを3つ作り、copyPages で結合した。ノイズ画像にしたのは、圧縮が効きにくく、画像データの量がそのまま出るようにするため。オブジェクトの数は、生成されたPDFのバイト列から /Subtype /Image を数えた。
ツールの比較には reportlab で作ったPDFを使い、pdftk と qpdf のそれぞれで結合した。