如何在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;elsereturn 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.mzph.cn/bicheng/54520.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ARM驱动学习之7 驱动模块传参数

ARM驱动学习之7 驱动模块传参数 1.02_DriverModule_01例程的基础上编写简单模块module_param Makefile 文件名字修改为module_param clean部分&#xff1a;rm -rf *.mod.c *.o *.order *.ko *.mod.o *.symvers2.修改module_param.c (1):添加头文件 //定义module_param module…

Qt Linguist 短语书批量导入翻译.ts导入.qph

文章目录 前言一、准备工作 将ts文件和python文件放到一个根目录下二、执行如下代码 比如这个python文件名字叫test.py三、在根目录下打开powershell之类的控制台&#xff0c;执行python test.py总结 前言 在开发Qt软件过程中&#xff0c;不免需要涉及到翻译软件界面的工作。于…

华为OD机试 - 分解正整数 - 数学推导(Python/JS/C/C++ 2024 D卷 100分)

华为OD机试 2024E卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试真题&#xff08;Python/JS/C/C&#xff09;》。 刷的越多&#xff0c;抽中的概率越大&#xff0c;私信哪吒&#xff0c;备注华为OD&#xff0c;加入华为OD刷题交流群&#xff0c;…

线程调优——调整线程池参数提升程序执行效率

先抛出一个问题&#xff0c;程序开发真的是线程越多效率越高吗&#xff1f;多线程是我们程序开发中必不可少的手段&#xff0c;线程就像“孙悟空”开启了分身术一样&#xff0c;每个分身都在“打妖怪”&#xff0c;那是不是分身越多&#xff0c;“打妖怪”的效率就越高&#xf…

Redisson实现分布式锁(看门狗机制)

目录 可重入锁&#xff1a; 锁重试和看门狗机制&#xff1a; 主从一致性&#xff1a; 首先引入依赖&#xff0c;配置好信息 3.使用Redisson的分布式锁 可重入锁&#xff1a; 可重入锁实现是通过redsi中的hash实现的&#xff0c;key依旧是业务名称加id&#xff0c;然后第一个…

如何成立一家自己的等级保护测评机构?需要哪些条件?有哪些要求?

给大家的福利&#xff0c;点击下方蓝色字 即可免费领取↓↓↓ &#x1f91f; 基于入门网络安全/黑客打造的&#xff1a;&#x1f449;黑客&网络安全入门&进阶学习资源包 前言 各省、自治区、直辖市公安厅、局网络安全保卫总队&#xff0c;新疆生产建设兵团公安局网络安…

【高分系列卫星简介——高分一号(GF-1)】

高分一号卫星&#xff08;GF-1&#xff09; 高分一号&#xff08;GF-1&#xff09;是中国高分辨率对地观测系统&#xff08;简称“高分专项”&#xff09;的第一颗卫星&#xff0c;具有里程碑式的意义。以下是对高分一号卫星的详细介绍&#xff1a; 一、基本信息 发射时间&…

2024华为杯研究生数学建模竞赛(研赛)选题建议+初步分析

提示&#xff1a;C君认为的难度&#xff1a;DE<C<F&#xff0c;开放度&#xff1a;CDE>F。 华为专项的题目&#xff08;A、B题&#xff09;暂不进行选题分析&#xff0c;不太建议大多数同学选择&#xff0c;对自己专业技能有很大自信的可以选择华为专项的题目。后续会…

MyBatis-config.xml核心配置

MyBatis-config.xml 包含了会深深影响MyBatis行为的设置和属性信息&#xff0c;配置文档的顶层结构如下 environments&#xff08;环境配置&#xff09; environments用于配置数据库的URL信息&#xff0c;MyBatis-config可以动态配置多个数据源&#xff0c;用于连生产、预发、…

用 HTML + JavaScript DIY 一个渐进式延迟法定退休年龄测算器

为减轻社会和个人因退休年龄变化带来的冲击&#xff0c;近日&#xff0c;全国人民代表大会常务委员会正式发布了关于实施渐进式延迟法定退休年龄的重要决定。 根据该决定&#xff0c;我国将同步启动对男、女职工法定退休年龄的延迟计划。这一调整将采取渐进式的方式进行&#…

RabbitMQ 高级特性——发送方确认

文章目录 前言发送方确认confirm 确认模式return 退回模式 常见面试题 前言 前面我们学习了 RabbitMQ 中交换机、队列和消息的持久化&#xff0c;这样能够保证存储在 RabbitMQ Broker 中的交换机和队列中的消息实现持久化&#xff0c;就算 RabbitMQ 服务发生了重启或者是宕机&…

Nginx实用篇:实现负载均衡、限流与动静分离

Nginx实用篇&#xff1a;实现负载均衡、限流与动静分离 | 原创作者/编辑&#xff1a;凯哥Java | 分类&#xff1a;Nginx学习系列教程 Nginx 作为一款高性能的 HTTP 服务器及反向代理解决方案&#xff0c;在互联网架构中扮演着至关重要的角色。它…

C++ 知识要点:变量

一.变量 1. 全局变量与 static 变量&#xff08;作用域、生存周期&#xff09; 全局变量 作用域&#xff1a;全局变量的作用域从它被定义的地方开始&#xff0c;一直到程序结束。在定义它的文件内部以及通过 extern 关键字在其他文件中都可以访问。生存周期&#xff1a;全局…

TCP 和 UDP 协议的区别?

参考TCP 和 UDP的区别_tcp和udp的区别-CSDN博客

Acwing DFS

DFS&#xff1a;深度优先搜索 DFS与BFS的对比 DFS使用栈来实现&#xff0c;BFS使用队列来实现 DFS所需要的空间是 O ( h ) O(h) O(h),而BFS需要的空间是 O ( 2 h ) O(2^h) O(2h),其中h是树的高度&#xff1b; DFS不具有最短路的特性&#xff0c;BFS有最短路的特性 DFS回溯…

102.SAPUI5 sap.ndc.BarcodeScannerButton调用摄像头时,localhost访问正常,使用IP访问失败

目录 原因 解决办法 1.修改谷歌浏览器的setting 2.在tomcat中配置https访问 参考 使用SAPUI5的sap.ndc.BarcodeScannerButton调用摄像头时&#xff0c;localhost访问正常&#xff0c;使用IP访问时&#xff0c;一直打不开摄像头&#xff0c;提示getUserMedia()问题。 原因…

2024 “华为杯” 中国研究生数学建模竞赛(D题)深度剖析|大数据驱动的地理综合问题|数学建模完整代码+建模过程全解全析

当大家面临着复杂的数学建模问题时&#xff0c;你是否曾经感到茫然无措&#xff1f;作为2022年美国大学生数学建模比赛的O奖得主&#xff0c;我为大家提供了一套优秀的解题思路&#xff0c;让你轻松应对各种难题&#xff01; CS团队倾注了大量时间和心血&#xff0c;深入挖掘解…

elasticsearch同步mysql方案

文章目录 1、1. 使用数据库触发器2. 使用定时任务3. 监听MySQL二进制日志&#xff08;binlog&#xff09;4. 使用数据管道5. 使用第三方工具或服务6. 编写自定义脚本注意事项 2、1. 使用Logstash步骤&#xff1a;示例配置&#xff1a; 2. 使用Debezium步骤&#xff1a; 3. 自定…

828华为云征文 | 解锁企业级邮件服务,在华为云fFlexus x实例上部署Mailcow开源方案

前言 华为云Flexus X实例携手Mailcow开源邮件方案&#xff0c;为企业打造了一个既高效又安全的邮件服务解决方案。Flexus X实例的柔性算力与高性能&#xff0c;是这一方案的坚实基石。它提供CPU内存的灵活定义&#xff0c;以经济型价格实现旗舰级性能&#xff0c;确保邮件服务的…

【Python实战】---- 爬取 CSDN 专栏文章列表

1. 场景 需求就是专栏中文章随着时间写的越多,如果后边需要去查找的时候比较麻烦,比如一些不常用的 git 命令,或者有些开发场景的细节,在之前已经开发完了,现在忘记部分细节,需要在之前的输出文章中去查找,当几十几百篇文章时,查找就比较麻烦,但是如果没发布一篇文章,…