跳轉到

PDF 最佳化器

PDF 文件在生成過程中往往含有大量可精簡的冗餘資料:重複嵌入的字型、多次引用的相同影像物件、未壓縮的串流資料。NextPDF Pro 的 PdfOptimizer 提供三段式最佳化策略,在品質與檔案大小之間取得精準平衡。


三個最佳化等級

等級 策略組合 典型壓縮比 視覺差異 適用場景
Light 物件去重 + FlateDecode 壓縮 + 移除未使用物件 15–25% 無損 法律文件、需精確還原的档案
Medium Light + JPEG 重壓縮(Q85)+ 字型子集精簡 + 圖層合併 35–55% 人眼幾乎無差異 一般商業文件、電子郵件附件
Aggressive Medium + 圖片降解析度(96 DPI)+ 移除詮釋資料 + 移除縮圖 + 移除 JavaScript 50–75% 螢幕可見,列印可接受 行動端預覽、網頁嵌入

快速開始

use NextPDF\Pro\Optimizer\PdfOptimizer;
use NextPDF\Pro\Optimizer\OptimizationLevel;
use NextPDF\Pro\Optimizer\OptimizationOptions;

// 讀取輸入 PDF(可來自 Core 生成或既有檔案)
$inputPdf = file_get_contents('/uploads/annual-report.pdf');
$originalSize = strlen($inputPdf);

// 建立最佳化器(Medium 等級)
$optimizer = new PdfOptimizer(
    level: OptimizationLevel::Medium,
);

$optimizedPdf = $optimizer->optimize($inputPdf);
$optimizedSize = strlen($optimizedPdf);

printf(
    "Original: %.1f KB → Optimized: %.1f KB (%.1f%% reduction)\n",
    $originalSize / 1024,
    $optimizedSize / 1024,
    (1 - $optimizedSize / $originalSize) * 100,
);

file_put_contents('/output/annual-report-optimized.pdf', $optimizedPdf);

進階選項設定

use NextPDF\Pro\Optimizer\PdfOptimizer;
use NextPDF\Pro\Optimizer\OptimizationLevel;
use NextPDF\Pro\Optimizer\OptimizationOptions;
use NextPDF\Pro\Optimizer\ImageQuality;

$options = OptimizationOptions::create(
    level: OptimizationLevel::Medium,
)
->withImageRecompression(
    jpegQuality: 85,           // JPEG 品質 1–100
    convertPngToJpeg: false,   // 保留 PNG 透明度
    maxImageDpi: 150,          // 最高解析度(僅 Aggressive 生效)
)
->withFontOptimization(
    subsetFonts: true,         // 僅嵌入文件實際使用的字符
    deduplicateFonts: true,    // 合併重複嵌入的字型
    removeUnusedFonts: true,
)
->withMetadataHandling(
    removeAuthorInfo: false,   // 保留作者資訊
    removeCreationDate: false, // 保留建立日期(法律文件需要)
    removeCustomProperties: true,
)
->withStructureOptimization(
    deduplicateObjects: true,  // ObjectDeduplicator
    compressObjectStreams: true,
    linearize: true,           // PDF 線性化(Fast Web View)
);

$optimizer = new PdfOptimizer($options);
$result = $optimizer->optimizeWithReport($inputPdf);

// 取得詳細最佳化報告
$report = $result->getReport();
echo 'Objects deduplicated: ' . $report->getDeduplicatedObjectCount();
echo 'Images recompressed: ' . $report->getRecompressedImageCount();
echo 'Fonts subsetted: ' . $report->getSubsettedFontCount();

ImageRecompressor

ImageRecompressorPdfOptimizer 的核心子模組,負責解析 PDF 中嵌入的影像物件並以最佳參數重新壓縮:

use NextPDF\Pro\Optimizer\Image\ImageRecompressor;
use NextPDF\Pro\Optimizer\Image\ImageRecompressorOptions;
use NextPDF\Pro\Optimizer\Image\ImageFormat;

$recompressor = new ImageRecompressor(
    ImageRecompressorOptions::create(
        targetFormat: ImageFormat::Jpeg,
        quality: 80,
        maxWidth: 1920,   // 超過此寬度的影像會被縮小
        maxHeight: 1080,
        preserveAlpha: true,  // 含透明度的影像保持 PNG 格式
    )
);

// 可獨立使用,不依賴完整 PdfOptimizer
$recompressedPdf = $recompressor->recompress($inputPdf);

支援的影像格式

來源格式 可輸出格式 備注
JPEG JPEG 重新壓縮降品質
PNG(無透明) JPEG 或 PNG 轉 JPEG 通常更小
PNG(有透明) PNG 保持透明度
TIFF JPEG 或 PNG 自動偵測透明度
BMP JPEG 或 PNG 無損轉換
JBIG2 保持原格式 黑白影像,不重壓縮

ObjectDeduplicator

PDF 規範允許多個頁面引用同一個物件。然而,某些 PDF 生成工具(包含部分版本的 NextPDF Core 早期版本)可能產生重複的物件副本:

use NextPDF\Pro\Optimizer\Object\ObjectDeduplicator;
use NextPDF\Pro\Optimizer\Object\DeduplicationStrategy;

$deduplicator = new ObjectDeduplicator(
    strategy: DeduplicationStrategy::ContentHash, // 以內容雜湊識別重複物件
);

$result = $deduplicator->deduplicate($inputPdf);

echo 'Original object count: ' . $result->getOriginalObjectCount();
echo 'After dedup object count: ' . $result->getDeduplicatedObjectCount();
echo 'Bytes saved: ' . $result->getBytesSaved();

效能基準數據

以下為三類典型文件的實測壓縮數據(測試環境:PHP 8.5.0、AMD EPYC 7742、16 GB RAM):

年度報告(含圖表與影像)

場景 原始大小 Light Medium Aggressive
年度報告(40 頁,含圖片) 8.2 MB 6.8 MB (-17%) 4.1 MB (-50%) 2.5 MB (-70%)
電子發票(1 頁,純文字) 42 KB 36 KB (-14%) 28 KB (-33%) 22 KB (-48%)
合約文件(20 頁,多字型) 1.8 MB 1.4 MB (-22%) 980 KB (-46%) 650 KB (-64%)
政府表單(5 頁,含條碼) 580 KB 490 KB (-16%) 320 KB (-45%) 210 KB (-64%)
技術手冊(200 頁,混合) 45 MB 38 MB (-16%) 22 MB (-51%) 13 MB (-71%)

處理速度

文件大小 Light Medium Aggressive
< 1 MB < 50ms < 120ms < 200ms
1–10 MB 50–300ms 120–800ms 200–1,500ms
10–50 MB 300ms–2s 800ms–5s 1.5–10s
> 50 MB 建議非同步佇列 建議非同步佇列 建議非同步佇列

批次最佳化

use NextPDF\Pro\Optimizer\PdfOptimizer;
use NextPDF\Pro\Optimizer\OptimizationLevel;
use NextPDF\Pro\Optimizer\BatchOptimizationResult;

$optimizer = new PdfOptimizer(level: OptimizationLevel::Medium);

$files = glob('/uploads/*.pdf');
$totalSaved = 0;

foreach ($files as $file) {
    $input = file_get_contents($file);
    $output = $optimizer->optimize($input);
    $saved = strlen($input) - strlen($output);
    $totalSaved += $saved;

    file_put_contents(
        str_replace('/uploads/', '/optimized/', $file),
        $output,
    );
}

printf("Total bytes saved: %.2f MB\n", $totalSaved / 1024 / 1024);

PDF 線性化(Fast Web View)

線性化(Linearization)重組 PDF 檔案結構,使瀏覽器能夠在下載完整檔案前即開始渲染第一頁:

use NextPDF\Pro\Optimizer\PdfLinearizer;

$linearizer = new PdfLinearizer();
$linearizedPdf = $linearizer->linearize($optimizedPdf);

// 驗證線性化結果
$isLinearized = $linearizer->isLinearized($linearizedPdf); // true

與已簽署文件的相容性

重要:最佳化操作會修改 PDF 位元組結構,必須在簽章之前執行。對已簽署 PDF 執行最佳化會使所有現有簽章失效:

// 正確順序
$raw = $doc->render();
$optimized = $optimizer->optimize($raw);    // 先最佳化
$signed = $appender->sign($optimized);      // 再簽章

// 錯誤順序(勿使用)
// $signed = $appender->sign($raw);
// $optimized = $optimizer->optimize($signed); // 簽章失效!

相關資源