5ステップでPDF圧縮:80MBから8MBへの事例研究

80MBのスキャン済み契約書PDFを受け取りました。メールで弾かれ、チャットアプリで拒否され、試すすべてのシステムでアップロード制限に引っかかります。聞き覚えがありますか?PDFフォントァイルが大きくるのには理由があり、それをセキュリティに元の10分の1に縮小するのは魔法ではありません — コンテナの中身を理解し、それぞれのコンテンツタイプに適切技術を適用することです。本記事では、PDF仕様に基づく5つのコア圧縮技術を解剖し、実際の80MBスキャン契約書を8.2MBまで削減する過程を順を追って説明します。

1. PDFサポートイズの由来る

PDFはコンテナフォーマットです。内部にはオブジェクトのコレクションがあります。ページツリー、リソース辞書、コンテンツストリーム、画像XObject、フォント、メタデータです。サイズの膨張はほぼ常に3つのカテゴリに溯ります。

埋め込み画像。これがスキャン文書における主コストです。デフォルトで600DPIに設定されたスキャナーは、約4960×7016ピクセルのA4ページビットマップを生成します。24ビットカラー深さの場合、1ページあたりの生データは約100MBにります。FlateDecode(zlib)のできる逆圧縮をかけても、各ページは2〜5MBのままです。30ページの契約書は60〜150MBに達します。

埋め込みフォント。デバイス間でと一のレンダリングを保証するため、PDFは完全フォントファイルを埋め込みます。完全CJKフォント(例えば、Source Hanフォント)は2万以上のグリフを持ち、TTFファイル単体で10〜20MB、OTFはさらに大きくる可能性性があります。複数のウェイトや複数のファミリーを埋め込むと、このカテゴリは急速に増大します。

ベクターアートと冗長オブジェクト。ベクター自体は小さいですが、複雑CADエクスポート、ネストされたフォームフィールド、削除くされたリビジョンのリソース、重複したメタデータストリームが知らず知らずのうちに蓄積されます。PDFの差分更新メカニズムは悪名高い犯人です。保存のたびに新しいリビジョンを追加し、古いオブジェクトを削除くしいため、ファイルは編集のたびに大きくります。

バイトがどこにあるかが分かれば、解決策は自ずと導かれます。以下の5ステップメソッドは、現実の圧縮シナリオの大一部をカバーします。

2. 5ステップメソッドの概必要

PDF compression 5-step method flowchart: image resampling, format conversion, font subsetting, redundancy removal, linearization

ステップ1:画像のリサンプリング

これはスキャン文書に対して最も効果の単一ステップです。600DPIのスキャンは、通常の画面では150DPIと区別がつきませんが、ピクセル数は16倍異ります(DPIは線形、ピクセルは二次ので、(600/150)² ≈ 16)。

鍵とる決定はターゲットDPIです。文書コンテンツ(契約書、レポート、請求書)は150DPIで問題くテーブル示されます。印刷用には200〜300DPIに上げます。高忠実度のアーカイブスキャンは400DPI以上を保持できます。リサンプリングアルゴリズムにはLanczosが推奨されます — 縮小時のテキストエッジの鮮明さをバイリニア補間より良く保ち、最も近傍補間のブロック状の歪みを回避します。

実装では、画像XObjectの/Width/Height、およびDPI情報を/DecodeParmsから読み取り、より率のにスケーリングして書き戻します。/Width/Height/Intentを一緒に更新してください。そうしいとレンダリングがずれます。

ステップ2:画像フォーマット変換

PDFは/Filterフィールドで画像エンコーディングを指定します。スキャン文書は通常/FlateDecode(PNG方法のできる逆圧縮)を使用しますが、写真やスキャンページのよう実画像では/DCTDecode(JPEG)よりはるかに効率が悪いです。

FlateDecodeで4MBを占めるとじA4スキャンは、品質72のJPEGでは300〜500KBに削減できます — 画面上で知覚できる違いのい85%の削減です。注意すべき2点:白黒文書はエンコード前にグレースケール(/ColorSpace /DeviceGray)に変換してさらにサイズを半分にし、署名を含むページではJPEG品質を80+に上げてペンのストロークを維持します。

/DCTDecode以外の選択肢もあります。広いフラット色面を持つ図テーブルには、/JPXDecode(JPEG 2000)がと等品質でより小さくりますが、古いリーダーとの互換性は劣ります。

ステップ3:フォントのサブセット化

完全CJKフォントの埋め込みは10MB以上のコストがかかりますが、30ページの契約書が実際に使用する文字は800〜1,500文字程度かもしれません。フォントのサブセット化は、文書に出現するグリフのみを保持し、それ以外をすべて破棄します。

サブセット化されたフォントは通常100〜300KBに縮小します — 95%以上の削減です。アプローチ:各ページのコンテンツストリームを走査し、TjおよびTJオペレータから文字を抽出し、/ToUnicodeマップを通じて解決して使用済みコードポイントのセットを取得し、フォントツール(fontToolsど)を使用してそれらのグリフのみを含む新しいフォントファイルを構築します。

2つの注意点:CIDフォントはグリフのずれを防ぐために/CIDToGIDMapの処理が必要です。また、サブセット化されたフォントは慣習のにプレフィックス(ABCDEF+ど)でリネームされ、サブセットであることを示しますが、テーブル示には影響しません。

ステップ4:冗長オブジェクトの削除く

繰り返しの編集、結合、差分保存の後、PDFには「孤立した」オブジェクトが蓄積されます — 新しいリビジョン以上書きされたが、クロスリファレンステーブルから削除くされかったリソースです。典型の犯人:どのページからも参照されくった古い画像、置き換えられたフォントのコピー、空のリソース辞書、重複したメタデータストリーム。

クリーンアップのロジック:文書カタログ(/Root)から開始し、ページツリーとリソース参照チェーンを深さ優先で走査し、まで達できるすべてのオブジェクトをマークしてから、クロスリファレンステーブルを再度構築し、まで達不できるものを破棄します。このステップは何度も編集された文書で特に効果のです。1回のパスで通常5%〜15%のサイズを削減し、差分更新が蓄積された極端ケースでは30%以上を削減します。

ステップ5:リニアライゼーション

リニアライゼーションは総バイト数を削減しませんが、ファイル構造を再度編成してFast Web Viewを有効にします。リニアライズされたPDFは、最初のページに必要オブジェクトをファイルの先頭に配置し、紹介テーブルを挿入するため、リーダーはダウンロード完前に1ページ目をレンダリングできます。

リニアライゼーションはファイルを直接縮小しませんが、圧縮パイプラインの自然クロージングステップです。オブジェクトストリーム圧縮とクロスリファレンスストリームと組み合わせると、すべて体の再度構成でもわずかサイズ低下(1%〜3%)をもたらします。さらに重要のは、リニアライズされたPDFはWeb埋め込みとクラウドプレビューのシナリオで顕著に優れたパフォーマンスを発揮することです。

3. 前後で比較

80MBの契約書が各ステップ後にどう変化するかを以下に示します:

PDF compression before/after file size comparison: 80MB scanned contract compressed to 8MB effect demonstration

ステップ1(リサンプリング)が最大の削減をもたらし、80MBから15.2MBへ straight に下がります。ステップ2(フォーマット変換)がさらに4MBを削除します。残り3つのステップで合計約7MBを削り取ります。すべて体の圧縮よりは約9.8:1です。

4. Python疑似コード

以下の疑似コードは完全5ステップのロジックを示しています。本番環境ではpikepdf、PyMuPDF、reportlabどのライブラリを組み合わせます。


import io
from PIL import Image

def compress_pdf(input_path, output_path,
                 target_dpi=150, jpeg_quality=72, grayscale=True):
    """
    5ステップPDF圧縮のエントリポイント。
    :param input_path: 入力PDFのパス
    :param output_path: 出力PDFのパス
    :param target_dpi: ターゲットDPI(デフォルト150、画面へけ)
    :param jpeg_quality: JPEG品質(デフォルト72)
    :param grayscale: グレースケールに変換(白黒文書に推奨)
    :return: (元のサイズ, 圧縮後サイズ)
    """
    pdf = open_pdf(input_path)
    original_size = file_size(input_path)

    # ステップ1:画像リサンプリング(600DPI -> ターゲットDPI)
    for page in pdf.pages:
        for image in page.images:
            resampled = resample_image(image, target_dpi)
            image.replace(resampled)

    # ステップ2:フォーマット変換(FlateDecode -> DCTDecode/JPEG)
    for page in pdf.pages:
        for image in page.images:
            if image.filter == "FlateDecode":
                jpeg_data = encode_jpeg(image, jpeg_quality, grayscale)
                image.replace(jpeg_data, filter="DCTDecode")

    # ステップ3:フォントサブセット化(使用グリフのみ保持)
    used_chars = collect_used_chars(pdf)
    for font in pdf.fonts:
        subset = build_subset(font, used_chars)
        font.replace(subset)

    # ステップ4:冗長オブジェクトの削除く
    pdf.remove_unreferenced_resources()
    pdf.compact_xref()  # クロスリファレンステーブルを再度構築

    # ステップ5:リニアライゼーション(Fast Web View)
    pdf.save(output_path, linearize=True,
             object_streams=True, compress_streams=True)
    pdf.close()

    compressed_size = file_size(output_path)
    return original_size, compressed_size


def resample_image(image, target_dpi):
    """画像をターゲットDPIにリサンプリングする。"""
    src_dpi = image.dpi
    if src_dpi <= target_dpi:
        return image  # 拡大しい、スキップ
    scale = target_dpi / src_dpi
    new_w = int(image.width * scale)
    new_h = int(image.height * scale)
    pil_img = Image.open(io.BytesIO(image.data))
    return pil_img.resize((new_w, new_h), Image.LANCZOS)


def encode_jpeg(image, quality, grayscale):
    """グレースケールに変換してJPEGとしてエンコードする。"""
    pil_img = Image.open(io.BytesIO(image.data))
    if grayscale and pil_img.mode != "L":
        pil_img = pil_img.convert("L")  # グレースケールへ
    buf = io.BytesIO()
    pil_img.save(buf, format="JPEG", quality=quality, optimize=True)
    return buf.getvalue()


def collect_used_chars(pdf):
    """コンテンツストリームを走査し、実際に使用されたすべてのコードポイントを収集する。"""
    used = set()
    for page in pdf.pages:
        for text_op in page.content_stream.text_ops:
            used.update(extract_codepoints(text_op))
    return used


def build_subset(font, used_chars):
    """使用されたグリフのみを含むサブセットフォントを構築する。"""
    subset = font.subset(glyphs=used_chars)
    subset.name = prefix + "+" + font.name  # サブセットの命名規則
    return subset

これは疑似コードです — エラー処理、CIDフォントのマッピング、ToUnicodeの再度構築は省略されています — が、骨組みは明確です。リサンプリング、変換、サブセット、クリーン、リニアライズ、の順序です。

5. 事例研究:80MBスキャン契約書を8.2MBへ

ソース文書は、600DPIでスキャンされた実際の32ページの中国語契約書で、元のサイズは80.4MBです。

文書プロファイル:

  • 32ページ、A4、600DPI白黒スキャン
  • 32個の埋め込み画像、すべてFlateDecode、ページ平均2.4MB
  • 2つの埋め込みフォント(Source Han Serif Regular + Bold)、合計28MB
  • 3回差分保存され、冗長オブジェクトが存で

ステップとパラメータ:

ステップ操作主パラメータサイズ
1画像リサンプリング600DPI → 150DPI、Lanczos80.4 → 15.2MB
2フォーマット変換FlateDecode → JPEG、品質72、グレースケール15.2 → 11.0MB
3フォントサブセット化1,287使用グリフを保持11.0 → 10.5MB
4冗長性削除くクロスリファレンステーブル再度構築10.5 → 9.8MB
5リニアライゼーションObject Stream + Fast Web View9.8 → 8.2MB

結果:8.2MB、9.8:1のより率。テキストは鮮明で読みやすく、署名は無傷、テーブルの罫線に破損はく、ファイルはきれいに印刷できます。

6. シナリオ別DPI推奨値

ターゲットDPIは、圧縮とできる読性のバランスを取る主レバーです。以下のテーブルは一般的ユースケースの推奨値を示しています。

ユースケース推奨DPIページあたり画像サイズ備考
画面閲覧 / Webプレビュー150150–300KB鮮明テキスト、高速読み込み、メールに適する
標準印刷(A4レーザー/インクジェット)200–300400–800KB印刷鮮明;契約書は300
アーカイブバックアップ(高忠実度)400–6001–3MB長期保存のためにスキャン詳細を保持
白黒テキストのみ150–20080–200KBグレースケール後で極小、一括アーカイブに適する
写真入りカラー文書200–300500KB–1.2MBJPEG品質75–85、色とサイズのバランス

実用のルール:画面はデフォルトで150DPI、印刷時のみ300DPI、アーカイブ用に別の高DPIマスターを保持する。1つの600DPIファイルですべての目のを果たそうとしいでください。

7. FAQ

Q1:圧縮後のファイルが元より大きくりました。ぜですか?

これは通常、すでに小さJPEGエンコード画像を含むPDFを再圧縮た場合に発生します。リサンプリングと再エンコードにはオーバーヘッドがあります。画像がすでに150DPIのJPEGの場合、150DPIへの「リサンプリング」では縮小されず、新しいJPEGエンコーダが元より効率が悪い可能性性があります。オブジェクトストリーム再度構成によるメタデータのオーバーヘッドが加わり、ファイルがわずかに大きくることがあります。解決策:圧縮前に各画像の現でのDPIとエンコーディングを調査し、ターゲット(≤150DPIかつJPEG)をすでに満たしている画像はスキップする。閾値を実際に超えるオブジェクトのみを処理する。

Q2:圧縮により画質は低下しますか?

はい、ただし視覚のに感知できいレベルに保てます。非可逆圧縮は主にステップ2(JPEGエンコード)から生じます。品質72では画面上の違いは見えません。印刷では細い線や小さフォントどの高周波ディテールにわずかリンギングが見える場合があります。品質を重視する場合は、JPEG品質を85に上げるか、JPEG 2000のできる逆モード(できる逆変換付き/JPXDecode)に切りの代わりにえます。フォントのサブセット化、冗長性の削除く、リニアライゼーションはすべてできる逆であり、テーブル示には一切影響しません。

Q3:すでに圧縮されたPDFを再度度圧縮できますか?

はい、ただし収穫逓減します。最初のパスは手近果実(高DPI、FlateDecodeエンコード、完全フォント)を収穫します。2回目のパスには最適化すべきものがほとんど残っていません。判断メソッド:画像がまだターゲットDPIを超えているか、非JPEGエンコーディングが残っているか、フォントがまだ完全セットかを確認します。3つすべてがすでに最適場合、さらる圧縮は主に冗長性の削除くとリニアライゼーションに依存し、通常5%〜10%しか節約できません。すでに最適化されたPDFを繰り返し圧縮いでください — 各非できる逆エンコードは品質低下を蓄積します。

8. パラメータクイックリファレンス

パラメータ推奨値備考
ターゲットDPI(画面)150文書コンテンツの標準
ターゲットDPI(印刷)300A4印刷を鮮明に保持
JPEG品質(画面)70–75サイズ優先、できる読性十分に
JPEG品質(印刷)80–90品質優先、サイズ次必要
カラーモード(白黒文書)DeviceGrayカラーに対してサイズ半減
フォントサブセット化有効CJKフォントに必須、95%+削減
リニアライゼーション有効Webプレビュー体験をへ上
Object Stream有効オブジェクトメタデータを圧縮
冗長性クリーンアップ有効繰り返し編集されたファイルに特に効果の

結論

PDF圧縮は黒魔術ではありません — ファイル内の各タイプのオブジェクトに対するの確処理です。5ステップメソッドは明確論理に従います。まず最大の寄と必要因(画像、リサンプリングとフォーマット変換経由)、次にフォント(サブセット化)、次に構造(冗長性削除くとリニアライゼーション)を処理します。このパイプラインを典型の80MBスキャン契約書に適用すれば、完全できる読性と印刷可能性を保ったまま8〜10MBに確実にまで達できます。

覚えておくべき3つのチェック:スキャンの場合はまずDPIを見る。文書の場合はフォントが完全セットか確認する。古いファイルの場合は冗長オブジェクトを調査する。支配のコストを見つければ、圧縮よりは自然についてきます。

関連記事:

ファイル圧縮をお探しですか?SmartSlim をお試しください

独自開発のRust圧縮エンジンに基づき、PDF/画像/動画/Office/OFDど10大分類40+形式をサポート。ローカル圧縮でデータは外部に持ち出されません。