Как загрузить документ в C++

Как загрузить документ в C++

Aspose.PDF FOSS for C++ открывает существующий PDF через класс Document — из простого пути к файлу или с паролем, если источник зашифрован — и предоставляет метаданные /Info файла через DocumentInfo. Это руководство охватывает загрузку документа, обнаружение и открытие файлов, защищённых паролем, чтение и обновление метаданных title/author/producer, а также сохранение результата. Библиотека добавляется в проект через 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, если файл не существует или его нельзя прочитать, и std::runtime_error, если файл не является действительным PDF:

#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 никогда не проверяет пароль и не изучает запись /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() возвращает DocumentInfo&, привязанный к словарю /Info документа. Каждое предопределённое поле имеет собственный аксессор чтения, который возвращает пустую строку, когда ключ отсутствует в исходном файле:

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: Обновить метаданные и сохранить документ

Установите метаданные через соответствующие перегрузки сеттера или через удобный метод SetTitle на Document, затем зафиксируйте изменение с помощью 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 есть запись /Encrypt в трейлере, и предоставленный пароль не совпадает ни с пользовательским, ни с паролем владельца, записанными там. Перехватите std::runtime_error и запросите правильный пароль, либо подтвердите пароль вне канала связи перед повторной попыткой.

Save() бросает “no source filename to save to”

Конструктор Save() без аргументов записывает изменения обратно в путь, из которого был построен документ. Document, созданный конструктором без аргументов — новый документ в памяти без исходного файла — не имеет пути для записи, поэтому Save() бросает исключение. Вместо этого вызовите Save(outputFileName).

Decrypt() не кажется, что создаёт незащищённый файл

Decrypt() сбрасывает в памяти флаг IsEncrypted(), поэтому последующие чтения рассматривают документ как обычный текст, но в текущей версии он не переписывает словарь /Encrypt трейлера и не перекодирует зашифрованные строки и потоки. Вызов Save() после Decrypt() всё равно записывает оригинальные байты, включая исходное шифрование — этот метод пока не создает копию в открытом виде на диске.

Title/Author/etc. выводятся как пустые строки

Аксессоры DocumentInfo возвращают пустую строку, а не ошибку, когда соответствующий ключ /Info отсутствует в исходном PDF. Пустой результат означает, что поле никогда не было установлено в этом документе, а не то, что чтение завершилось с ошибкой.

Часто задаваемые вопросы

Работает ли Document(path) с зашифрованным PDF?

Обычно он разбирает дерево страниц без исключений, но никогда не аутентифицирует пароль и всегда оставляет IsEncrypted() на уровне false. Используйте конструктор с двумя аргументами, когда источник может быть зашифрован.

Безопасно ли передавать пароль в Document(path, password) для файла, который окажется незашифрованным?

Да. Когда в трейлере отсутствует запись /Encrypt, аргумент пароля игнорируется, и документ открывается так же, как при использовании конструктора с одним аргументом.

Как отличить пользовательский ключ /Info от встроенного?

Вызовите статический DocumentInfo::IsPredefinedKey(key). Он возвращает true для Title, Author, Subject, Keywords, Creator, Producer и Trapped, а также false для всех остальных.

Могу ли я перезаписать только что загруженный файл вместо записи в новый путь?

Да — вызовите Save() без аргументов у Document, построенного из пути к файлу; он записывает обратно в тот же исходный путь. Он генерирует исключение для документов, созданных конструктором без аргументов, у которых нет исходного пути.

Удаляет ли сохранение после Decrypt() парольную защиту PDF?

Нет, в текущем выпуске — см. раздел «Общие проблемы» выше. Decrypt() влияет только на состояние в памяти, используемое читателями контента; файл, записанный Save(), всё равно содержит оригинальный словарь /Encrypt.

См. также:

 Русский