qpdf/qpdf
GitHub: qpdf/qpdf
qpdf 是一个内容保留式的 PDF 文档变换工具,提供命令行和 C++ 库两种形式,用于对 PDF 文件进行结构级的拆分、合并、线性化和加密等底层操作。
Stars: 5277 | Forks: 392
[](https://qpdf.sourceforge.io)
[](https://github.com/qpdf/qpdf/actions)
[](https://qpdf.readthedocs.io/en/latest/?badge=latest)
qpdf 是一个命令行工具和 C++ 库,用于对 PDF 文件执行保留内容的转换。它支持线性化、加密和许多其他功能。它还可用于拆分和合并文件、创建 PDF 文件(但您必须自己提供所有内容),以及检查文件以进行研究或分析。qpdf 不渲染 PDF 或执行文本提取,也不包含用于处理页面内容的更高级接口。它是一个用于处理 PDF 文件结构的底层工具,对于任何想要通过编程或基于命令行的方式来操作 PDF 文件的人来说,都是一个极具价值的工具。
[qpdf 手册](https://qpdf.readthedocs.io) 托管在 https://qpdf.readthedocs.io。项目网站是 https://qpdf.sourceforge.io。源代码仓库托管在 GitHub:https://github.com/qpdf/qpdf。
# 验证发行版
官方 qpdf 发行版使用 [cosign](https://docs.sigstore.dev/quickstart/quickstart-cosign/) 进行签名。每个版本都包含一个 `sha256` 文件,其中包含所有发行版文件的 sha256 校验和。要验证某个发行版,请使用 `sha256sum file` 或类似命令生成要验证的文件的校验和,并检查以确保它与 sha256 文件中的内容匹配。您可以使用 gpg 或 `cosign verify-blob` 来验证 sha256 文件本身。示例:
```
cosign verify-blob qpdf-x.y.z.sha256 --bundle qpdf-x.y.z.sha256.sigstore \
--certificate-identity=signer-identity@qpdf.org \
--certificate-oidc-issuer=https://github.com/login/oauth
```
身份 `signer-identity@qpdf.org` 应替换为签署该发行版的人员姓名。这将在发行说明中注明。有效的签署者是
* Jay Berkenbilt
* Manfred Holger
您还可以使用 Jay Berkenbilt 的 GPG 密钥验证 qpdf 发行版,其指纹为 `C2C9 6B10 011F E009 E6D1 DF82 8A75 D109 9801 2C7E`,可在 https://q.ql.org/pubkey.asc 找到或从公共密钥服务器下载。
# 版权,许可证
qpdf 版权所有 (c) 2005-2021 Jay Berkenbilt,2022-2026 Jay Berkenbilt 和 Manfred Holger
根据 Apache License, Version 2.0(“许可证”)获得许可;除非遵守许可证,否则您不得使用此文件。您可以在以下网址获取许可证副本:
https://www.apache.org/licenses/LICENSE-2.0
除非适用法律要求或书面同意,否则根据许可证分发的软件均按“原样”分发,不附带任何明示或暗示的担保或条件。请参阅许可证以了解管辖许可证下的权限和限制的具体语言。
您也可以在源代码发行版的 [LICENSE.txt](LICENSE.txt) 文件中查看许可证。
7 之前的 qpdf 版本是根据 Artistic License 2.0 版的条款发布的。您可以选择继续将 qpdf 视为在这些条款下获得许可。请查阅手册以获取更多信息。源代码发行版的 [Artistic-2.0](Artistic-2.0) 文件中包含了 Artistic License。
# 前置条件
要构建和测试 qpdf,需要支持 C++20 的 C++ 编译器。要与 qpdf 进行链接,兼容 C++17 的编译器就足够了。
要将某些内容与 qpdf 进行编译和链接,您可以使用 `pkg-config`(包名为 `libqpdf`)或 `cmake`(包名为 `qpdf`)。以下是一个使用 qpdf 库构建程序的 `CMakeLists.txt` 文件示例:
```
cmake_minimum_required(VERSION 3.16)
project(some-application LANGUAGES CXX)
find_package(qpdf)
add_executable(some-application some-application.cc)
target_link_libraries(some-application qpdf::libqpdf)
```
qpdf 依赖于外部库 [zlib](https://www.zlib.net/) 和 [jpeg](https://www.ijg.org/files/)。
[libjpeg-turbo](https://libjpeg-turbo.org/) 库也是可用的,因为它与常规的 jpeg 库兼容,并且 qpdf 不使用纯正的 jpeg8 API 中不存在的任何接口。这些库是每个 Linux 发行版的一部分,并且很容易获得。下载信息请参见文档。对于 Windows,您可以下载适用于某些编译器的这些库的预编译二进制版本;有关更多详细信息,请参阅 [README-windows.md](README-windows.md)。
根据启用的加密提供程序,可能还需要 [GnuTLS](https://www.gnutls.org/) 和 [OpenSSL](https://openssl.org)。这将在下文的[加密提供程序](#crypto-providers)中进行更多讨论。
详细信息请参见[手册](https://qpdf.readthedocs.io/en/latest/installation.html)。
## Zopfli
如果构建 qpdf 时启用了 [zopfli](https://github.com/google/zopfli) 支持,并且 `QPDF_ZOPFLI` 环境变量被设置为 `disabled` 以外的任何值,qpdf 将使用 zopfli 压缩库而不是 zlib 来生成 flate 压缩流。根据其官方网站所述,zopfli 算法比 zlib 慢得多(大约 100 倍),但生成的输出略小,这使得它非常适合诸如生成归档 PDF(文件大小比速度更重要)等场景。要构建带有 zopfli 支持的版本,您必须已安装 zopfli 库和头文件。
环境变量 `QPDF_ZOPFLI` 可以设置为以下值:
* `disabled`(或未设置):不使用 zopfli
* `force`:使用 zopfli;如果未编译入 zopfli 则失败
* `silent`:如果可用则使用 zopfli;否则静默回退到 zlib
* 任何其他值:如果可用则使用 zopfli,如果不可用则发出警告
# 内嵌软件的许可条款
qpdf 利用 zlib 和 jpeg 库来实现其功能。这些包可以从它们各自的下载位置单独下载。如果启用了可选的 GnuTLS 或 OpenSSL 加密提供程序,则还需要 GnuTLS 和/或 OpenSSL。
有关内嵌软件的许可证信息,请参阅 [NOTICE](NOTICE.md) 文件。
# 加密提供程序
qpdf 可以使用不同的加密实现。这些可以在编译时或运行时进行选择。在 9.1.0 之前的所有版本中使用的原生加密实现仍然存在,但如果在构建时存在任何外部提供程序,默认情况下它们不会被构建到 qpdf 中。
提供以下提供程序:
* `gnutls`:一种使用 GnuTLS 库提供加密功能的实现;会导致 libqpdf 与 GnuTLS 库链接
* `openssl`:一种可以使用 OpenSSL(或 BoringSSL)库提供加密功能的实现;会导致 libqpdf 与 OpenSSL 库链接
* `native`:一种原生实现,所有源代码都内嵌在 qpdf 中,不需要任何外部依赖项
默认行为是,cmake 会根据可用的外部库发现还可以支持哪些其他加密提供程序,构建所有可用的外部加密提供程序,并默认使用外部提供程序而不是原生提供程序。默认情况下,只有在没有任何外部提供程序可用时,才会使用原生加密提供程序。可以使用各种 cmake 选项更改此行为,如[手册中所述](https://qpdf.readthedocs.io/en/latest/installation.html#build-time-crypto-selection)。
## 关于弱加密算法的说明
PDF 文件格式过去依赖 RC4 进行加密。使用 256 位密钥时始终会改用 AES,而使用 128 位密钥时,您可以选择使用 AES。当有人正在写入包含弱加密算法的文件时,qpdf 会尽最大努力发出警告,但 qpdf 必须始终保留读取甚至写入具有弱加密的文件的支持,以便能够完全支持较旧的 PDF 文件和较旧的 PDF 阅读器。
# 在 UNIX/Linux 上从源代码发行版构建
从版本 11 开始,qpdf 使用 cmake 进行构建。使用 cmake 的默认配置适用于大多数系统。在 Windows 上,您可以使用 Visual Studio 通过 cmake 构建 qpdf,而无需安装任何额外的工具。但是,要运行测试套件,您需要 MSYS2,而且您还需要 MSYS2 才能使用 mingw 进行构建。
UNIX/Linux 构建示例:
```
cmake -S . -B build -DCMAKE_BUILD_TYPE=RelWithDebInfo
cmake --build build
```
从 MSYS2 mingw shell 进行 mingw 构建的示例:
```
cmake -S . -B build -G 'MSYS Makefiles' -DCMAKE_BUILD_TYPE=RelWithDebInfo
cmake --build build
```
从 MSYS shell 或在路径中包含 Visual Studio 命令行工具的 Windows 命令 shell 进行 MSVC 构建的示例:
```
cmake -S . -B build
cmake --build build --config Release
```
可以使用 `cmake --install` 进行安装(您可能需要在 `.bashrc` 或类似文件中设置 `LD_LIBRARY_PATH` 变量,例如使用 `export LD_LIBRARY_PATH=/usr/local/lib64:$LD_LIBRARY_PATH` 命令)。可以使用 `cpack` 制作包。
测试使用 `qtest`,测试驱动程序由 `ctest` 调用。要查看真正底层的测试,请运行 `ctest --verbose`,以便您可以看到 `qtest` 的输出。如果您需要关闭 qtest 的彩色输出,请将 `-DQTEST_COLOR=0` 传递给 cmake。
有关更多信息,请参阅[手册](https://qpdf.readthedocs.io/en/latest/installation.html)。
# 在 Windows 上构建
已知 qpdf 可以使用 mingw 和 Microsoft Visual C++ 构建并通过其测试套件。32 位和 64 位版本均可运行。除手册外,有关如何在 Windows 下构建的更多详细信息,请参阅 [README-windows.md](README-windows.md)。
# 构建文档
qpdf 手册采用 reStructured Text 格式编写,并使用 [sphinx](https://www.sphinx-doc.org) 构建。用户手册的源文件可以在 `manual` 目录中找到。有关更详细的信息,请查阅[手册的“构建和安装 qpdf”部分](https://qpdf.readthedocs.io/en/latest/installation.html)或查阅 [build-doc 脚本](build-scripts/build-doc)。
# 关于构建的补充说明
qpdf 提供 cmake 配置文件和 pkg-config 文件。它们支持静态和动态链接。通常,_使用_ qpdf 的构建不需要依赖 qpdf 的头文件可用。唯一的例外是,如果您包含 `Pl_DCT.hh`,则需要 `libjpeg` 的头文件。由于这种情况很少见,因此 qpdf 的 cmake 和 pkg-config 文件不会自动将 JPEG 包含路径添加到构建中。如果您显式使用 `Pl_DCT`,您可能已经在构建中对其进行了配置。
要了解如何使用该库,请阅读 [include/qpdf](include/qpdf/) 目录中头文件里的注释,尤其是 [QPDF.hh](include/qpdf/QPDF.hh)、[QPDFObjectHandle.hh](include/qpdf/QPDFObjectHandle.hh) 和 [QPDFWriter.hh](include/qpdf/QPDFWriter.hh)。这些是关于 API 的最佳文档来源。您还可以研究 [QPDFJob.cc](libqpdf/QPDFJob.cc) 的代码,该代码实践了大部分公共接口。在 [examples](examples/) 目录中还有额外的示例程序。
# 关于测试套件的补充说明
默认情况下,慢速测试和需要超出构建 qpdf 所需依赖项的测试已被禁用。慢速测试包括图像比较测试和大文件测试。可以通过将 `QPDF_TEST_COMPARE_IMAGES` 环境变量设置为 `1` 来启用图像比较测试。可以通过将 `QPDF_LARGE_FILE_TEST_PATH` 环境变量设置为具有至少 11 GB 可用空间且可以处理超过 4 GB 大小文件的目录的绝对路径来启用大文件测试。在 Windows 上,这应该是一个 Windows 路径(例如 `C:\LargeFileTemp`,即使构建是在 MSYS2 环境中运行的也是如此)。即使没有这些测试,测试套件也几乎提供了完全的覆盖率。除非您正在对库进行可能影响生成的 PDF 文件内容的深度更改,或者是首次在新的平台上进行测试,否则没有真正的理由运行这些测试。如果您只是运行测试套件以确保 qpdf 适用于您的构建,那么默认测试就已经足够了。
如果您正在为某个发行版打包 qpdf,并准备由 autobuilder 运行构建,您可能需要将 `-DSHOW_FAILED_TEST_OUTPUT=1` 传递给 `cmake`,并使用 `--verbose` 或 `--output-on-failure` 选项运行 `ctest`。这样,如果测试套件失败,测试失败的详细信息将包含在构建输出中。否则,您将必须有权访问构建中的 `qtest.log` 文件才能查看测试失败情况。qpdf 的 Debian 包启用了此选项。有关打包者的更多说明可以在[手册中](https://qpdf.readthedocs.io/en/latest/packaging.html)找到。
# 随机数生成
默认情况下,qpdf 使用加密提供程序来生成随机数。本节的其余内容仅在您使用原生加密提供程序时适用。
如果正在使用原生加密提供程序,那么当 `qpdf` 检测到 Windows 加密 API 或 `/dev/urandom`、`/dev/arandom` 或 `/dev/random` 存在时,它会使用它们来生成加密安全的随机数。如果这些条件均不成立,构建将失败并报错。可以通过多种方式修改此行为:
* 如果您使用 cmake 选项 `SKIP_OS_SECURE_RANDOM` 或定义 `SKIP_OS_SECURE_RANDOM` 预处理程序符号,qpdf 将不会尝试使用 Windows 加密或随机设备。您必须提供自己的随机数据提供程序或允许使用不安全的随机数。
* 如果您开启 cmake 选项 `USE_INSECURE_RANDOM` 或定义 `USE_INSECURE_RANDOM` 预处理程序符号,qpdf 将禁用操作系统提供的安全随机数的情况下尝试使用不安全的随机数。这不是一种回退方案。为了使用不安全的随机数,您还必须禁用操作系统的安全随机数,因为否则,找不到操作系统的安全随机数将是一个编译错误。不安全的随机数来源是标准库的 `random()` 或 `rand()` 调用。这些随机数不是加密安全的,但 qpdf 库在使用它们时仍可完全正常运行。使用非安全随机数意味着在某些情况下更容易猜出加密密钥。
* 在任何情况下,您都可以提供自己的随机数据提供程序。为此,请从 `qpdf/RandomDataProvider`(自版本 5.1.0 起)派生一个类,并在创建任何 `QPDF` 对象之前调用 `QUtil::setRandomDataProvider`。如果您提供了自己的随机数据提供程序,即使编译了对其他随机数据提供程序的支持,也将始终使用它。如果您希望避免您的 qpdf 构建使用用户提供的随机数据提供程序以外的任何内容,您可以定义 `SKIP_OS_SECURE_RANDOM` 并且不定义 `USE_INSECURE_RANDOM`。在这种情况下,如果尝试生成随机数且未提供随机数据提供程序,qpdf 将抛出运行时错误。
# 致谢
qpdf 项目通过其[开源项目](https://www.jetbrains.com/community/opensource/#support)拥有 JetBrains 许可证。我们对该计划表示感谢,并且一直在享受他们高质量产品带来的益处。
标签:Bash脚本, C++, PDF处理, 安全测试工具, 数据擦除, 文件合并与拆分, 文档加密, 文档转换