Skip to content

PDF 压缩

DocumentEditor::save_with_options() 可以一次性完成流重新压缩、孤立对象垃圾回收以及为快速 Web 浏览进行线性化。最简单的方法是 pdf-oxide compress CLI,它把这三项功能封装在一个命令里。

绑定覆盖。 完整的 SaveOptions(含 compress / garbage_collect / linearize)已在 Rusteditor.save_with_options(path, opts))和 Python(已绑定处通过 doc.save(..., compress=True, ...))中提供。在 Node、WASM、Go 和 C# 中,请把 CLI(pdf-oxide compress input.pdf -o out.pdf)作为子进程或构建步骤运行 — 在 SaveOptions 透传到各 FFI 之前,CLI 是这些绑定共享的稳定接口。

CLI(全平台)

# 基本用法:生成 input_compressed.pdf
pdf-oxide compress input.pdf

# 显式输出路径
pdf-oxide compress input.pdf -o smaller.pdf

# 打印大小对比
pdf-oxide compress report.pdf
# → Compressed report.pdf -> report_compressed.pdf (3412901 -> 1847200 bytes)

CLI 默认启用 compress: truegarbage_collect: truelinearize: true — 在保持符合 ISO 32000 的前提下,这种组合生成体积最小的文件。

Rust API

use pdf_oxide::editor::{DocumentEditor, EditableDocument, SaveOptions};

let mut editor = DocumentEditor::open("input.pdf")?;

editor.save_with_options("output.pdf", SaveOptions {
    compress: true,         // 对所有未压缩流应用 FlateDecode
    garbage_collect: true,  // 移除孤立对象
    linearize: true,        // 线性化以支持快速 Web 浏览
    ..Default::default()
})?;

SaveOptions 字段

字段 默认值 作用
compress CLI 中为 true / 默认 false 对以未压缩方式保存的流应用 FlateDecode
garbage_collect CLI 中为 true 丢弃不再被 trailer 引用的间接对象
linearize CLI 中为 true 输出线性化(Fast Web View)布局,让阅读器在文件未下载完时就渲染第 1 页
encryption None 附加 EncryptionConfig 以加密保存 — 参见加密与安全

每个选项的实际行为

  • compress — 遍历每个 content stream、Form XObject、ToUnicode CMap 和元数据流;若流没有 filter 或 filter 不被识别,就用 FlateDecode 重新封装。已压缩的流(JBIG2 图像、CCITT 表格、现有 Flate)保持不动。
  • garbage_collect — 从 /Root/Info 开始做可达性扫描,标记所有活跃的间接对象,只把它们写入输出 xref。remove_pageflatten_formserase_region 等大改动会留下孤立对象,此时很有用。
  • linearize — 重新排序对象,让第 1 页的 content stream 及其资源依赖出现在文件前部,并附带线性化字典和 hint stream。PDF 阅读器按顺序下载文件,在完整文件到达之前即可渲染第 1 页 — 对 CDN 下发的 PDF 来说,这是用户最能感知到的收益。

何时压缩

  • 批量编辑之后 — 如果你执行过 flatten_formsflatten_all_annotationsapply_all_redactions 或删除了大量页面,GC + 重新压缩通常能削减 30–60% 体积。
  • 分发或邮寄之前 — 终端用户在意文件大小,Linearize 让慢速连接下第 1 页也能秒开。
  • 作为 ETL 流水线的最后一步 — 在提取与再生成之后,只在流水线末尾压缩一次;不要在每次变换时解压再压缩。

何时不压缩

  • 频繁增量编辑时 — 完整重写会丢失 xref stream 和 object stream,它们是让增量追加保持小巧的关键。交互式编辑循环请使用 SaveOptions::incremental(),压缩留到最终一步。
  • 已经高度压缩的 PDF — 若流已经采用带良好 predictor 的 FlateDecode,你可能只省下区区几个百分点。先跑一次 pdf-oxide compress 测一下,再决定是否接入流水线。

体积缩减预期

真实 PDF 的典型节省量(基于 3830 个文件的测试语料):

来源 压缩前 压缩后 节省
扫描发票(未压缩流) 2.4 MB 0.8 MB 约 66%
LaTeX 研究论文 1.1 MB 0.95 MB 约 14%
政府表单(flatten 之后) 890 KB 240 KB 约 73%
已优化的营销 PDF 1.8 MB 1.75 MB 约 3%

批量编辑后的文件和 flatten 后的表单收益最大。已优化文件仅靠线性化获得有限节省。

先解密 / 鉴权

DocumentEditor 目前没有密码认证调用,所以加密 PDF 需先解密。用 PdfDocument.open_with_password() 读取,保存一份未加密副本,再在编辑器中打开该副本:

use pdf_oxide::api::Pdf;
use pdf_oxide::editor::{DocumentEditor, EditableDocument, SaveOptions};

let doc = Pdf::open_with_password("protected.pdf", "pw")?;
doc.save("temp-unencrypted.pdf")?;

let mut editor = DocumentEditor::open("temp-unencrypted.pdf")?;
editor.save_with_options("compressed.pdf", SaveOptions {
    compress: true,
    garbage_collect: true,
    linearize: true,
    ..Default::default()
})?;

Python 中同样的思路:PdfDocument(path, password="pw") + save() + CLI。

相关页面