Как работать с PDF-документами в C++
Aspose.PDF FOSS для C++ — это библиотека C++20 с лицензией MIT, не имеющая зависимостей, собираемая и связываемая с помощью CMake — импорт через менеджер пакетов не требуется. Это руководство проходит через основной Document-центрированный рабочий процесс: открыть существующий PDF, просмотреть его страницы и метаданные, извлечь текст и отрисовать страницу в растровое изображение.
Пошаговое руководство
Шаг 1: Установить пакет
Клонируйте репозиторий и добавьте его в ваш CMake-проект как поддиректорию:
git clone https://github.com/aspose-pdf-foss/Aspose.PDF-FOSS-for-Cpp.gitadd_subdirectory(Aspose.PDF-FOSS-for-Cpp)
target_link_libraries(your_app PRIVATE aspose_pdf_foss)Или сконфигурируйте и соберите его автономно:
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build buildПроверьте, что цепочка инструментов работает, скомпилировав минимальную программу с подключенными заголовками:
#include <aspose/pdf/document.hpp>
#include <iostream>
int main() {
Aspose::Pdf::Document doc;
std::cout << "Linked OK - pages: " << doc.Pages().Count() << "\n";
}Успешная сборка, которая выводит pages: 0 для только что созданного, пустого Document, подтверждает, что библиотека связана корректно.
Шаг 2: Импортировать необходимые классы
#include <aspose/pdf/document.hpp>
#include <aspose/pdf/document_info.hpp>
#include <aspose/pdf/page.hpp>
#include <aspose/pdf/page_collection.hpp>
#include <aspose/pdf/text_absorber.hpp>
#include <aspose/pdf/bmp_device.hpp>
#include <aspose/pdf/resolution.hpp>
#include <fstream>
#include <iostream>Шаг 3: Открыть PDF-документ и подсчитать его страницы
Создайте Document из пути к файлу, чтобы открыть существующий PDF. Document::Pages() возвращает PageCollection, а Count() сообщает, сколько страниц он содержит:
Aspose::Pdf::Document doc("input.pdf");
std::cout << "Page count: " << doc.Pages().Count() << "\n";Шаг 4: Считать метаданные документа
Document::Info() возвращает объект DocumentInfo с типизированными аксессорами чтения/записи для стандартных записей словаря /Info:
auto& info = doc.Info();
std::cout << "Title: " << info.Title() << "\n";
std::cout << "Author: " << info.Author() << "\n";
std::cout << "Producer: " << info.Producer() << "\n";Шаг 5: Извлечь текст со страницы
Aspose::Pdf::Text::TextAbsorber проходит по Document или отдельному Page и собирает встречающийся текст; Text() возвращает результат потом. PageCollection начинается с 1, поэтому Pages()[1] — первая страница:
auto page = doc.Pages()[1];
Aspose::Pdf::Text::TextAbsorber absorber;
absorber.Visit(page);
std::cout << absorber.Text() << "\n";Visit() также принимает весь Document напрямую (absorber.Visit(doc)), когда вы хотите, чтобы текст всех страниц был объединён вместе вместо отдельной страницы.
Шаг 6: Отрисовать страницу в растровое изображение
BmpDevice преобразует Page в несжатое BMP-изображение. Его конструктор принимает Resolution, который контролирует вывод DPI, а Process() записывает отрисованные байты в любой std::ostream:
Aspose::Pdf::Devices::BmpDevice device(Aspose::Pdf::Devices::Resolution(150));
std::ofstream imageOut("page1.bmp", std::ios::binary);
device.Process(page, imageOut);Шаг 7: Обновить метаданные и сохранить документ
Сеттеры в DocumentInfo подготавливают изменения, которые будут сброшены при следующем запуске Save() на принадлежащем Document:
doc.Info().Title("Processed by Aspose.PDF FOSS for C++");
doc.Save("output.pdf");Распространённые проблемы и их решения
Accessing Pages()[0] throws std::out_of_range
PageCollection имеет индексацию, начинающуюся с 1, соответствуя канонической семантике Aspose.PDF — первая страница это Pages()[1]. Индекс меньше 1 или больше Count() вызывает ошибку.
TextAbsorber::Text() возвращает пустую строку
Text() возвращает то, что было накоплено последним вызовом Visit(). Убедитесь, что Visit() действительно был вызван перед чтением Text(), и что страница или документ, который вы открыли, содержит извлекаемый текст, а не только отсканированные изображения.
Сгенерированный BMP-файл неожиданно мал или выглядит пустым
Низкое значение Resolution приводит к маленькому растровому изображению. Увеличьте DPI, передаваемый в Resolution — например, Resolution(300) для вывода печатного качества — и проверьте, что индекс страницы, переданный в Process(), соответствует вашему намерению.
DocumentInfo поля читаются как пустые строки
Не каждый производитель PDF записывает стандартные записи /Info. Проверьте Producer() и Creator() на наличие пустых значений, прежде чем предполагать, что извлечение метаданных не удалось — пустая строка является определённым результатом для отсутствующего ключа.
Изменения, внесённые с помощью Info().Title(...) не появляются в сохранённом файле
Мутации DocumentInfo находятся в памяти и сохраняются только при следующем запуске Save() на том же экземпляре Document. Убедитесь, что редактирование метаданных происходит до Save(), а не после.
Часто задаваемые вопросы
Индексация страниц в Aspose.PDF FOSS для C++ начинается с нуля или с единицы?
С единицы. doc.Pages()[1] всегда первая страница.
Может ли TextAbsorber извлекать текст только с одной страницы вместо всего документа?
Да. Visit() перегружена и принимает либо Document—который извлекает текст со всех страниц—либо отдельный Page, который извлекает текст только с этой страницы.
Нужно ли вызывать Save(), если я только читаю данные из документа?
Нет. Save() требуется только после изменений, которые нужно записать обратно в файл, например при редактировании полей DocumentInfo. Чтение количества страниц, извлечение текста или рендеринг страницы не изменяют документ.
Как я могу управлять разрешением отрисованной страницы?
Передайте значение Resolution конструктору устройства — например, Resolution(150) для 150 DPI. Более высокие значения дают более крупные растровые изображения с более высоким качеством.
В каком формате BmpDevice создаёт?
Несжатое BMP-изображение, записанное напрямую в std::ostream, переданный в Process().