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¶
ImageRecompressor 是 PdfOptimizer 的核心子模組,負責解析 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); // 簽章失效!