如何在 C++ 中加载文档

如何在 C++ 中加载文档

Aspose.PDF FOSS for C++ 通过 Document 类打开已有的 PDF —— 可以使用普通文件路径,或在源文件被加密时提供密码 —— 并通过 DocumentInfo 暴露文件的 /Info 元数据。本文档介绍了加载文档、检测并打开受密码保护的文件、读取和更新标题/作者/生成器元数据,以及保存结果的过程。该库通过 CMake 添加到项目中;没有包管理器的安装步骤。

分步指南

步骤 1:安装包

将库添加为 CMake 子目录,并链接到 aspose_pdf_foss 目标:

add_subdirectory(aspose.pdf-foss-for-cpp)
target_link_libraries(your_app PRIVATE aspose_pdf_foss)

通过编译最小程序验证工具链和头文件是否解析成功:

#include <aspose/pdf/document.hpp>
#include <iostream>

int main() {
    Aspose::Pdf::Document doc;
    std::cout << "Linked OK — pages: " << doc.Pages().Count() << "\n";
}

成功构建并打印 pages: 0 表明库已正确链接。


第2步:导入所需的类

#include <aspose/pdf/document.hpp>
#include <aspose/pdf/document_info.hpp>

using namespace Aspose::Pdf;

Document 和 DocumentInfo 都直接声明在 Aspose::Pdf 命名空间下。


第3步:从文件路径打开 PDF 文档

Document(filename) 构造函数读取文件,解析其交叉引用表和页面树,并构建内存模型。当文件不存在或无法读取时,它会抛出 std::system_error,当文件不是有效的 PDF 时,则抛出 std::runtime_error:

#include <iostream>
#include <stdexcept>

try {
    Document doc("input.pdf");
    std::cout << "Pages: " << doc.Pages().Count() << "\n";
} catch (const std::runtime_error& e) {
    std::cerr << "Not a valid PDF: " << e.what() << "\n";
} catch (const std::system_error& e) {
    std::cerr << "Could not read file: " << e.what() << "\n";
}

第4步:打开受密码保护的文档

第3步中的单参数构造函数从不验证密码,也不检查 trailer 的 /Encrypt 条目,因此通过这种方式打开的文档上的 IsEncrypted() 总是报告 false —— 即使源文件实际上已加密。要检测并打开加密的 PDF,请使用双参数构造函数,它会读取 /Encrypt 并验证您提供的密码:

try {
    Document doc("input.pdf", "user-password");
    if (doc.IsEncrypted()) {
        std::cout << "Opened an encrypted document.\n";
    }
} catch (const std::runtime_error& e) {
    // Thrown when /Encrypt is present and the password matches
    // neither the user nor the owner entry in the trailer.
    std::cerr << "Wrong password or unreadable /Encrypt dictionary: "
              << e.what() << "\n";
}

如果源 PDF 未加密,密码参数将被简单地忽略,文档会正常打开 —— 因此即使您不确定文件是否受保护,也可以安全地使用双参数构造函数。


第5步:使用 DocumentInfo 读取文档元数据

Document.Info() 返回一个绑定到文档的 /Info 字典的 DocumentInfo&。每个预定义字段都有自己的读取访问器,当键在源文件中不存在时返回空字符串:

Document doc("input.pdf");
DocumentInfo& info = doc.Info();

std::cout << "Title: "    << info.Title()    << "\n";
std::cout << "Author: "   << info.Author()   << "\n";
std::cout << "Subject: "  << info.Subject()  << "\n";
std::cout << "Producer: " << info.Producer() << "\n";
std::cout << "Creator: "  << info.Creator()  << "\n";
std::cout << "Keywords: " << info.Keywords() << "\n";

DocumentInfo::IsPredefinedKey 报告给定的 /Info 键名是否是这些内置字段之一,而不是通过 Add 添加的自定义键:

bool builtin = DocumentInfo::IsPredefinedKey("Title");   // true
bool custom  = DocumentInfo::IsPredefinedKey("MyKey");   // false

步骤 6:更新元数据并保存文档

通过相应的 setter 重载或通过 Document 上的 SetTitle 便利方法设置元数据,然后使用 Save 持久化更改:

Document doc("input.pdf");
doc.Info().Author("Report Generator");
doc.SetTitle("Quarterly Report");   // equivalent to doc.Info().Title(title)

doc.Save("output.pdf");

元数据编辑在内存中暂存,仅在下次调用 Save 时写出。调用 Save(outputFileName) 将写入新路径,或使用无参的 Save() 覆盖文档最初打开时的文件:

Document doc("input.pdf");
doc.SetTitle("Updated In Place");
doc.Save();   // writes back to "input.pdf"

常见问题及解决方案

IsEncrypted() returns false 对于我知道受密码保护的 PDF

单参数 Document(path) 构造函数从不针对文件的 /Encrypt 字典进行身份验证,因此使用这种方式打开的文档,IsEncrypted() 总是报告 false,无论源文件是否真的已加密。请改用双参数 Document(path, password) 构造函数打开文件——它是唯一会填充加密状态的重载。

两个参数的构造函数会抛出 std::runtime_error

这意味着源 PDF 在其 trailer 中有一个 /Encrypt 条目,而提供的密码既不匹配记录的用户密码,也不匹配所有者密码。捕获 std::runtime_error 并提示输入正确的密码,或在重新尝试前通过其他渠道确认密码。

Save() 抛出 “no source filename to save to”

无参数 Save() 会写回文档构建时的路径。使用无参数构造函数创建的 Document —— 一个没有源文件、仅在内存中的新文档 —— 没有可写回的路径,因此会抛出 Save()。请改为调用 Save(outputFileName)。

Decrypt() 似乎没有生成未受保护的文件

Decrypt() 会清除内存中的 IsEncrypted() 标志,使后续读取将文档视为明文,但在当前版本中它并不会重写 trailer 的 /Encrypt 字典或重新编码加密的字符串和流。在 Decrypt() 之后调用 Save() 仍会写回原始字节,包括原始的加密——此方法尚未在磁盘上生成明文副本。

Title/Author/etc. 显示为空字符串

DocumentInfo 访问器在源 PDF 中缺少相应的 /Info 键时返回空字符串,而不是错误。空结果表示该字段从未在该文档上设置过,而不是读取失败。

常见问题

Document(path) 能在加密的 PDF 上工作吗?

它通常会解析页面树而不会抛出异常,但从不对密码进行认证,并且总是将 IsEncrypted() 保持在 false。当源文件可能被加密时,请使用带两个参数的构造函数。

如果文件实际上未加密,向 Document(path, password) 传递密码安全吗?

是的。当 trailer 中没有 /Encrypt 条目时,密码参数会被忽略,文档会以单参数构造函数相同的方式打开。

如何辨别自定义的 /Info 键与内置键?

调用静态的 DocumentInfo::IsPredefinedKey(key)。它对 Title、Author、Subject、Keywords、Creator、Producer 和 Trapped 返回 true,对其他所有情况返回 false。

我能覆盖刚刚加载的文件,而不是写入新路径吗?

可以——在使用文件路径构造的 Document 上调用无参的 Save();它会写回同一源路径。对于使用无参构造函数创建且没有源路径的文档,会抛出异常。

在执行 Decrypt() 后保存,是否会移除 PDF 的密码保护?

不,在当前版本中没有——请参阅上面的常见问题。Decrypt() 只影响内容读取器使用的内存状态;由 Save() 写入的文件仍然保留原始的 /Encrypt 字典。

另请参阅

 中文