利用Poppler-Qt6在C++中高效解析PDF内容(二)

1. 从PDF中精准提取文本:不只是search()

上次我们聊了怎么用Poppler-Qt6把PDF文件“打开”,拿到页面对象。今天咱们深入一步,聊聊怎么把PDF里的文字“抠”出来。很多朋友第一反应就是用Poppler::Pagesearch()方法,这没错,它能帮你找到特定关键词的位置。但如果你想把一整页、甚至整个文档的文字都提取出来,做成可搜索的数据库,或者进行文本分析,search()就显得力不从心了。

Poppler-Qt6提供了更强大的文本提取能力,核心是Poppler::Pagetext()方法。这个方法听起来简单,用起来却有不少门道。直接调用page->text(),它会返回一个QString,里面包含了页面上的所有文本。但你会发现,有时候提取出来的文字顺序是乱的,或者段落结构完全丢失了。这是因为PDF本身是一种“所见即所得”的格式,它记录的是每个字符在页面上的精确坐标,而不是像HTML或TXT那样有明确的段落和行结构。

所以,直接调用text()只是第一步。为了得到结构更清晰、更接近阅读顺序的文本,我们需要用到Poppler::Page的另一个方法:textList()。它返回一个QList<Poppler::TextBox*>,也就是一个文本框的列表。每个TextBox对象不仅包含了文本片段(QString text),还包含了这个文本片段所在的矩形区域(QRectF boundingBox)。有了坐标信息,我们就可以通过算法(比如按Y坐标从上到下、同Y坐标时按X坐标从左到右排序)来重建文本的阅读顺序。

这里有个我踩过的坑:textList()返回的文本框,有时候一个单词会被拆成好几个TextBox,特别是那种用了复杂字体或者有特殊格式的单词。为了解决这个问题,我通常会在排序后,对相邻的TextBox进行合并判断。如果两个框在水平方向上距离很近(比如小于一个字符的平均宽度),并且它们的Y坐标基本对齐,我就会把它们合并成一个文本块。下面是一段示例代码,展示了如何提取并初步整理一页的文本:

#include <poppler-qt6.h>
#include <QList>
#include <algorithm>

QString extractStructuredText(Poppler::Page* page) {
    if (!page) return QString();

    // 获取所有文本框
    QList<Poppler::TextBox*> textBoxes = page->textList();
    if (textBoxes.isEmpty()) return QString();

    // 按位置排序:先按顶部Y坐标,再按左侧X坐标
    std::sort(textBoxes.begin(), textBoxes.end(),
              [](Poppler::TextBox* a, Poppler::TextBox* b) {
                  QRectF rectA = a->boundingBox();
                  QRectF rectB = b->boundingBox();
                  // 允许一定的Y坐标容差,视为同一行
                  const qreal lineTolerance = rectA.height() * 0.5;
                  if (qAbs(rectA.top() - rectB.top()) < lineTolerance) {
                      return rectA.left() < rectB.left();
                  }
                  return rectA.top() < rectB.top();
              });

    QString result;
    QRectF prevBox;
    for (Poppler::TextBox* box : textBoxes) {
        QRectF currentBox = box->boundingBox();
        // 判断是否需要换行或添加空格
        if (!prevBox.isNull()) {
            // 如果当前框和上一个框的Y坐标差距较大,说明换行了
            if (currentBox.top() > prevBox.bottom() + prevBox.height() * 0.3) {
                result += "\n";
            }
            // 如果当前框离上一个框有较大水平间隙,可能是一个单词结束或新段落,加空格
            else if (currentBox.left() > prevBox.right() + prevBox.width() * 0.2) {
                result += " ";
            }
            // 对于距离很近的框(可能是同一个单词被拆分),直接拼接
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值