流星雨爱编程头像
Poppler:一个好用的开源 PDF 解析库封面图

Poppler:一个好用的开源 PDF 解析库

目录

1.简介

2.安装与集成

2.1.Windows 平台(推荐 vcpkg 一键安装)

2.2.Linux 平台

2.3.macOS 平台

2.4.CMake 工程集成

3.核心 API 体系

3.1.poppler::document —— 文档对象

3.2.poppler::page —— 页面对象

3.3.poppler::ustring —— Unicode 字符串

3.4.poppler::text_box —— 带坐标的文本块

3.5.poppler::image —— 渲染 / 提取的图像

4.使用示例

5.常见问题

6.与其它库对比


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.hQt 工程,可直接对接 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.与其它库对比

库开源特点推荐场景
PopplerGPL成熟、CJK 中文支持好,有纯 C++ 接口 + Qt 接口✅ 首选,提取文本、获取文字坐标
MuPDFAGPL轻量、速度快,同时支持渲染图片、提取文字需要同时渲染 PDF + 取文字
PDFiumBSDChrome 内置 PDF 引擎,稳定;编译比较麻烦商业项目、Chrome 同款解析逻辑
PoDoFoLGPL底层 PDF 结构读写,文本提取偏弱改 PDF 结构、签名,不推荐单纯读文字

转载自 CSDN-专业IT技术社区

原文链接:https://blog.csdn.net/haokan123456789/article/details/169068201

文章来源转载

评论

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--