当前位置: 首页 > article >正文

如何在C++中使用Poppler库读取PDF文件(一)

如果只是读取、渲染pdf文件,除了mupdf以外,在Linux系统中还可以使用Poppler库。Poppler的历史非常古老,X窗口系统中的pdf查看工具xpdf,使用的就是poppler。

Poppler在Linux各发行版,以及BSD族系统之中,都有现成的二进制安装包。

而且,Poppler还提供了glib、Qt5、Qt6等多种上层库的支持。本文中的示例,就采用Qt6的接口。

开发环境

Poppler按照上层接口的不同,需要不同的头文件与库文件。

原生API

比如,如果使用原生的C++接口,就需要使用poppler-devel安装包,包含Poppler底层的头文件,头文件目录是/usr/include/poppler,连接/usr/lib64/libpoppler.so动态库。

为了简化这一操作,可以使用pkg-config文件。

如:

~/$ pkg-config --cflags --libs poppler  
-I/usr/include/poppler -lpoppler

GLIB API

如果在glib程序中使用Poppler,就可以使用Poppler的glib绑定。

还是使用pkg-config:

~/$ pkg-config --cflags --libs poppler-glib  
-I/usr/include/poppler/glib -I/usr/include/cairo -I/usr/include/freetype2 -I/usr/include/glib-2.0 -I/usr/li  
b64/glib-2.0/include -I/usr/include/libxml2 -I/usr/include/libpng16 -DWITH_GZFILEOP -I/usr/include/harfbuzz  
-I/usr/include/sysprof-6 -pthread -I/usr/include/pixman-1 -I/usr/include/poppler -lpoppler-glib -lgobject-  
2.0 -lglib-2.0 -lcairo

我们可以看到,头文件目录是/usr/include/poppler/glib,而连接的库也大量使用了glib的底层动态库。

如果我们真的使用Poppler的glib绑定,就会发现把PDF的页面导出成图片的时候,是使用的GdkPixbuf。

Qt API

如果使用Qt的绑定,就根据Qt的版本,还有不同的二进制库。对于Qt5、Qt6的分别是poppler-qt5-devel与poppler-qt6-devel。

我们后文全都使用poppler-qt6-devel举例。

还是使用pkg-config看一下:

~/$ pkg-config --cflags --libs poppler-qt6  
-I/usr/include/poppler/qt6 -I/usr/include/poppler -lpoppler-qt6

如果使用CMake构建,就可以使用CMake的PkgConfig来获取相应的变量,不再赘述。

打开PDF

使用poppler-qt6的时候,PDF相关的类位于Poppler名字空间,PDF文档的类是Document,即我们需要把PDF文档解析成Poppler::Document。

这个解析过程是load()静态方法。

需要注意的是,Poppler的Qt绑定,大量使用了智能指针来方便内存的管理。

比如上文提到的Poppler::Document::load()方法,返回的就是一个unique_ptrPoppler::Document。

所以,我们需要使用unique_ptrPoppler::Document来保存加载的PDF文件,在智能指针的作用域超出以后,Poppler::Document被自动释放。

另外,load函数的第一个参数,是一个QString。如果我们是C++的std::string,需要使用QString::fromLocal8Bit来转化成QString。

比如我们定义一个PDF类:

using namespace Poppler;

class PDF {
public:
    // 加载
    bool load(const std::string &filename);
    // 总页数
    int pageCount();
    // 页面大小
    QSizeF pageSizeF(int pagenum);

    // 搜索
    QList<QRectF> pageSearch (int pagenum, const string &str);

    // 渲染
    QImage pageRender (int pagenum, int ix, int iy, double zoom, int degree);

private:
    // 智能指针
    unique_ptr<Document> m_doc;
};

加载方法:

bool PDF::load(const std::string &filename)
{
    m_doc = Document::load (QString::fromLocal8Bit (filename));  
    if (m_doc == nullptr)  
    {      
         // 如果加载失败,尝试使用密码解锁
        auto text = QInputDialog::getText (nullptr, "password", "input password");  
        auto pass = QByteArray::fromStdString (text.toStdString ());  
        m_doc = Document::load (QString::fromStdString (filename), pass, pass);
    }

    if (m_doc)
        return true;
    else
        return false;

上面的过程,返回的doc就是一个unique_ptrPoppler::Document。

取得页面信息

通过unique_ptrPoppler::Document的numPages()方法,可以取得总页数。

int  
PDF::pageCount ()  
{  
  return m_doc->numPages ();  
}  

另外通过page(int pagenum)方法,可以取得一个unique_ptrPoppler::Page,这个Page支持的方法比较多,可以做各种操作。

比如,可以通过Poppler::Page的pageSizeF()方法,取得页面的大小。

QSizeF  
PDF::pageSize (int pagenum)  
{  
  auto page = m_doc->page (pn);  
  auto size = page->pageSizeF ();  
  return size;
}  

再比如,可以通过Poppler::Page的search()方法,搜索页面的文本:

QList<QRectF> 
PDF::pageSearch (int pn, const string &str)  
{  
  auto page = mDoc->page (pn);  
  auto results = page->search (QString::fromLocal8Bit(str));  
  return results;
}

返回的是一个模板类QList<QRectF>,每一个QRectF都是一个矩形。

渲染PDF

还可以通过Poppler::Page的renderToImage()方法,把一个页面渲染成一个图片。

但是renderToImage()方法,比前面介绍的稍微复杂一点儿。

它的原型是:

 QImage renderToImage(double xres = 72.0, double yres = 72.0, int x = -1, int y = -1, int w = -1, int h  
= -1, Rotation rotate = Rotate0) const;
  • 其中,xres、yres分别是横竖两个方向的字符大小。默认都是72.0。如果我们要缩放页面,就需要根据缩放比例调整这个值。
  • 而x、y是页面的左上角坐标,w是页面宽度,h是页面高度。如果我们要渲染页面的一部分,就可以灵活调整这4个数值。
  • 而Rotation是控制渲染的方向,默认是原始方向,即Rotate0,还可以是Rotate90、Rotate180、Rotate270,可以通过字面意思猜出来,这是页面旋转的角度。

所以,以下代码可以根据输出页面大小、缩放以及方向来渲染一个PDF页面为一个QImage。

QImage
ApvlvPDF::pageRender (int pagenum, int ix, int iy, double zoom, int degree)  
{  
  auto xres = 72.0, yres = 72.0;  
  xres *= zoom;  yres *= zoom;
  auto width = zoom * ix, height = zoom * iy;  
  
  auto prot = Poppler::Page::Rotate0;  
  if (degree == 90)  
    prot = Poppler::Page::Rotate90;  
  if (degree == 180)  
    prot = Poppler::Page::Rotate180;  
  if (degree == 270)  
    prot = Poppler::Page::Rotate270;  
  
  auto page = mDoc->page (pagenum);  
  auto image = page->renderToImage (xres, yres, 0, 0, width, height, prot);  
  
  return image;  
}  

http://www.kler.cn/news/318132.html

相关文章:

  • 解决方案 | 镭速助力动漫游戏行业突破跨网文件交换瓶颈
  • JUC并发编程_四大函数式接口
  • provide,inject父传子
  • Git使用教程-将idea本地文件配置到gitte上的保姆级别教程
  • 3.postman脚本语言、接口关联(json引用(变量)、脚本用正则表达式)、断言封装、自动化构造接口请求(Postman工具)
  • 如何在云端使用 Browserless 进行网页抓取?
  • 数据结构_1.1、数据结构的基本概念
  • 高校心理辅导系统:Spring Boot技术实现指南
  • Linux usb core阅读
  • 安卓沉浸式状态栏遇到的问题
  • 【Linux实践】实验三:LINUX系统的文件操作命令
  • 【强化学习系列】Gym库使用——创建自己的强化学习环境3:矢量化环境+奖励函数设计
  • 【30天玩转python】单元测试与调试
  • 制造企业MES系统委外工单管理探析
  • js中正则表达式中【exec】用法深度解读
  • 【Finetune】(五)、transformers之LORA微调
  • JetLinks物联网学习(前后端项目启动)
  • 学习编程利器《西蒙学习法》
  • 边学英语边学 Java|Synchronization in java
  • vite配置将es6打包成es5
  • Java-ArrayList和LinkedList区别
  • 速通LLaMA3:《The Llama 3 Herd of Models》全文解读
  • Ubuntu中常用的操作指令
  • vsomeip客户端/服务端大致运行流程
  • STL之vector篇(上)还在为学习vector而感到烦恼吗?每次做算法题都要回忆很久,不如来看看我的文章,精简又易懂,帮你快速掌握vector的相关用法
  • kafka 生产者拦截器
  • yum 安装gcc 时,提示glibc错误依赖
  • LeetCode题练习与总结:二叉树的最近公共祖先--236
  • 读书笔记——DDIA-v2 设计数据密集型应用(第二版)
  • 卷积神经网络——手写数字识别