目录
3.3.poppler::ustring —— Unicode 字符串
3.4.poppler::text_box —— 带坐标的文本块
3.5.poppler::image —— 渲染 / 提取的图像
1.简介
Poppler 是目前 C++ 生态中最成熟、中文支持最好的开源 PDF 解析库之一,基于 Xpdf 项目衍生,广泛用于 Linux 桌面、开源 PDF 阅读器和文档处理场景。它提供纯 C++ 接口、Qt 绑定接口两套 API,支持文本提取、页面渲染、元数据读取、图片提取、加密文档处理等核心能力。
核心能力:
- 文本提取:支持全页文本、带坐标的文本块提取,可还原阅读顺序
- 页面渲染:将 PDF 页面渲染为位图(PNG/JPEG),支持自定义 DPI、缩放
- 文档元数据:读取标题、作者、关键词、创建时间、页数、页面尺寸
- 加密支持:支持打开带用户 / 所有者密码的加密 PDF
- 资源提取:提取 PDF 内嵌图片、字体、附件
- 表单与注释:读取 / 填写 PDF 表单、解析注释(进阶功能)
接口分类:
| 接口类型 | 头文件前缀 | 适用场景 |
|---|---|---|
| 原生 C++ 接口 | poppler/cpp/*.h | 纯 C++ 工程、无 Qt 依赖,跨平台通用 |
| Qt 绑定接口 | poppler-qt5.h / poppler-qt6.h | Qt 工程,可直接对接 QImage、QString |
2.安装与集成
2.1.Windows 平台(推荐 vcpkg 一键安装)
这是最省心的方式,自动处理依赖(freetype、libpng、zlib 等),支持 VS+CMake。
安装指令:
# 64位动态库版本(默认推荐)
vcpkg install poppler:x64-windows
# 64位静态库版本
vcpkg install poppler:x64-windows-static
# 如需Qt5绑定,安装带Qt特性的版本
vcpkg install poppler[qt5]:x64-windows
安装完成后,头文件位于 poppler/cpp/ 目录,库文件自动被 vcpkg 集成。
手动编译(不推荐)
需要自行编译 freetype、libjpeg、libpng、zlib、openjpeg 等依赖,配置复杂,容易出现编译错误,仅建议定制化需求使用。
2.2.Linux 平台
Ubuntu / Debian
# 安装C++开发包
sudo apt install libpoppler-cpp-dev poppler-utils
# 安装Qt5绑定
sudo apt install libpoppler-qt5-dev
CentOS / RHEL
sudo yum install poppler-cpp-devel poppler-utils
2.3.macOS 平台
brew install poppler poppler-qt5
2.4.安装验证(命令行工具)
安装后系统会自带 poppler-utils 工具集,可先验证 PDF 能否正常解析:
# 提取PDF文本到文件
pdftotext input.pdf output.txt
# 查看PDF元信息
pdfinfo input.pdf
# PDF转图片
pdftocairo -png input.pdf output
命令行能正常提取文本,说明库本身没问题,后续代码出问题可快速定位是代码还是 PDF 本身的问题。
2.4.CMake 工程集成
基础 CMakeLists.txt 示例
cmake_minimum_required(VERSION 3.15)
project(poppler_demo)
set(CMAKE_CXX_STANDARD 17)
# 查找Poppler库
find_package(poppler REQUIRED)
add_executable(poppler_demo main.cpp)
# 链接C++原生接口
target_link_libraries(poppler_demo PRIVATE poppler::poppler-cpp)
注意:vcpkg 集成时,需要配置
CMAKE_TOOLCHAIN_FILE指向 vcpkg.cmake。
Qt 工程集成
find_package(poppler REQUIRED COMPONENTS qt5)
target_link_libraries(your_qt_app PRIVATE poppler::poppler-qt5)
3.核心 API 体系
3.1.poppler::document —— 文档对象
PDF 文档的入口,负责加载文档、获取页数、读取元数据、解锁加密文档。
核心方法:
load_from_file(path, owner_password, user_password):从文件加载load_from_data(byte_array, ...):从内存字节流加载pages():返回总页数is_locked():是否加密锁定create_page(index):创建第 index 页的页对象(从 0 开始)info_key("Title")/info_key("Author"):读取元数据字段page_mode()/page_layout():文档显示模式
3.2.poppler::page —— 页面对象
单页 PDF 的操作入口,是最常用的类。
核心方法:
text():返回整页文本(poppler::ustring)text_list():返回带坐标的文本块列表(std::vector<text_box>)render_to_image(xres, yres, x, y, w, h):渲染页面为图片page_rect():返回页面尺寸(点单位,1 点 = 1/72 英寸)images():返回页面内嵌图片列表
3.3.poppler::ustring —— Unicode 字符串
to_utf8():转为 UTF-8 编码的std::string(中文场景必用)to_latin1():转为 Latin1(中文会乱码,禁止使用)size()/empty():字符串操作
3.4.poppler::text_box —— 带坐标的文本块
个文本块包含文本内容和 bounding box 坐标,用于还原阅读顺序、定位文字位置。
text():文本内容rect():返回poppler::rectf,包含x()/y()/width()/height()坐标
3.5.poppler::image —— 渲染 / 提取的图像
save(path, format):保存为图片文件(支持 png/jpeg 等)width()/height():尺寸const_bits():获取原始像素数据
4.使用示例
所有示例均使用纯 C++ 原生接口,无 Qt 依赖,可直接编译运行。
示例 1:提取全文档文本(基础入门)
#include <iostream>
#include <string>
#include <memory>
#include <poppler/cpp/poppler-document.h>
#include <poppler/cpp/poppler-page.h>
std::string extract_all_text(const std::string& pdf_path, const std::string& password = "")
{
std::string result;
// 加载PDF文档
std::unique_ptr<poppler::document> doc(
poppler::document::load_from_file(pdf_path, "", password)
);
if (!doc) {
return "[错误] 无法打开PDF文件";
}
if (doc->is_locked()) {
return "[错误] PDF加密,密码错误或缺失";
}
int total_pages = doc->pages();
result += "总页数:" + std::to_string(total_pages) + "\n\n";
// 逐页提取
for (int i = 0; i < total_pages; ++i) {
std::unique_ptr<poppler::page> page(doc->create_page(i));
if (!page) continue;
result += "===== 第 " + std::to_string(i + 1) + " 页 =====\n";
poppler::ustring page_text = page->text();
result += page_text.to_utf8();
result += "\n\n";
}
return result;
}
int main()
{
std::string text = extract_all_text(R"(C:\test\demo.pdf)");
std::cout << text << std::endl;
return 0;
}
示例 2:带坐标文本提取 + 还原阅读顺序
PDF 内部文字按绘制顺序存储,直接提取常出现顺序错乱。通过text_list()获取坐标后,按「行从上到下、同行从左到右」排序,可还原正常阅读顺序。
#include <vector>
#include <algorithm>
#include <poppler/cpp/poppler-text-box.h>
std::string extract_ordered_text(poppler::page* page)
{
std::vector<poppler::text_box> boxes = page->text_list();
if (boxes.empty()) return "";
// 按Y坐标分组(行),同行按X坐标排序
const float line_threshold = 2.0f; // 行容差,单位:点
std::vector<std::vector<poppler::text_box>> lines;
for (auto& box : boxes) {
float y = box.rect().y();
bool found = false;
for (auto& line : lines) {
if (std::abs(line[0].rect().y() - y) < line_threshold) {
line.push_back(box);
found = true;
break;
}
}
if (!found) {
lines.push_back({box});
}
}
// 行从上到下排序
std::sort(lines.begin(), lines.end(), [](const auto& a, const auto& b) {
return a[0].rect().y() < b[0].rect().y();
});
// 每行内从左到右排序
for (auto& line : lines) {
std::sort(line.begin(), line.end(), [](const auto& a, const auto& b) {
return a.rect().x() < b.rect().x();
});
}
// 拼接文本
std::string result;
for (auto& line : lines) {
for (auto& box : line) {
result += box.text().to_utf8();
}
result += "\n";
}
return result;
}
示例 3:渲染 PDF 页面为图片并保存
#include <poppler/cpp/poppler-image.h>
bool render_page_to_png(const std::string& pdf_path, int page_index,
const std::string& output_png, int dpi = 150)
{
std::unique_ptr<poppler::document> doc(
poppler::document::load_from_file(pdf_path)
);
if (!doc || page_index < 0 || page_index >= doc->pages()) {
return false;
}
std::unique_ptr<poppler::page> page(doc->create_page(page_index));
if (!page) return false;
// 按DPI渲染:默认72DPI,150DPI更清晰
poppler::image img = page->render_to_image(dpi, dpi);
if (!img.is_valid()) return false;
return img.save(output_png, "png");
}
// 调用示例
// render_page_to_png("demo.pdf", 0, "page_1.png", 200);
示例 4:读取 PDF 元数据
void print_pdf_info(const std::string& pdf_path)
{
std::unique_ptr<poppler::document> doc(
poppler::document::load_from_file(pdf_path)
);
if (!doc) return;
std::cout << "标题: " << doc->info_key("Title").to_utf8() << "\n";
std::cout << "作者: " << doc->info_key("Author").to_utf8() << "\n";
std::cout << "主题: " << doc->info_key("Subject").to_utf8() << "\n";
std::cout << "关键词: " << doc->info_key("Keywords").to_utf8() << "\n";
std::cout << "页数: " << doc->pages() << "\n";
std::cout << "加密: " << (doc->is_encrypted() ? "是" : "否") << "\n";
}
示例 5:提取 PDF 内嵌图片
#include <poppler/cpp/poppler-image.h>
void extract_page_images(const std::string& pdf_path, int page_idx, const std::string& out_dir)
{
std::unique_ptr<poppler::document> doc(
poppler::document::load_from_file(pdf_path)
);
std::unique_ptr<poppler::page> page(doc->create_page(page_idx));
auto images = page->images();
for (size_t i = 0; i < images.size(); ++i) {
std::string save_path = out_dir + "/img_" + std::to_string(i) + ".png";
images[i].save(save_path, "png");
}
}
5.常见问题
1.中文乱码
- 必用
to_utf8():禁止使用to_latin1()或强制转本地编码 - 渲染为图片时出现方块:系统缺少对应中文字体,安装宋体、黑体等常用中文字体即可
- 部分生僻字提取为空:PDF 中文字使用了自定义编码、转曲或图片化,无法直接提取,需 OCR
2.文本顺序错乱
- 根本原因:PDF 是「页面描述语言」,文字按绘制指令顺序存储,不等于人类阅读顺序
- 解决方案:使用
text_list()获取带坐标文本块,自行按行列排序(示例 2 已给出实现)
3.加密 PDF 处理
load_from_file第二参数是所有者密码,第三参数是用户密码- 仅知道用户密码也可打开文档,但部分操作(如打印、修改)会受限
- 未知密码无法暴力破解,Poppler 不提供破解功能
4.Windows 编译冲突
- 大 PDF 不要一次性加载全部页文本,逐页处理、逐页释放
poppler::document非线程安全,多线程场景建议每个线程独立加载文档,或全局加锁- 高 DPI 渲染会占用大量内存,按需设置 DPI(150DPI 适合多数场景)
5.扫描版 PDF(图片 PDF)
Poppler 无法提取扫描版 PDF 的文字,只能渲染为图片后,搭配 Tesseract OCR 做文字识别。
6.与其它库对比
| 库 | 开源 | 特点 | 推荐场景 |
|---|---|---|---|
| Poppler | GPL | 成熟、CJK 中文支持好,有纯 C++ 接口 + Qt 接口 | ✅ 首选,提取文本、获取文字坐标 |
| MuPDF | AGPL | 轻量、速度快,同时支持渲染图片、提取文字 | 需要同时渲染 PDF + 取文字 |
| PDFium | BSD | Chrome 内置 PDF 引擎,稳定;编译比较麻烦 | 商业项目、Chrome 同款解析逻辑 |
| PoDoFo | LGPL | 底层 PDF 结构读写,文本提取偏弱 | 改 PDF 结构、签名,不推荐单纯读文字 |
转载自 CSDN-专业IT技术社区
原文链接:https://blog.csdn.net/haokan123456789/article/details/169068201




