打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
Qt 使用Poppler实现pdf阅读器

开发环境 Qt5.5.1、Qt Creator 3.5.1 

Qt实现pdf阅读器和MFC实现pdf阅读器,其实原理都是差不多的。

需要用到Poppler开源库,下载地址如下 https://poppler.freedesktop.org/

如果只是要在window的gcc下运行的话,可以下载已经编译好的库 https://sourceforge.net/projects/poppler-win32/ 


注意:这个是MinGW版本的Qt,也就是运行在GCC环境下的库,里面只包含 *.dll 和 *.a 。如果是Vistual Studio版本的Qt ,那么很不幸里面没有 *.lib文件。


1、新建项目,在项目的根目录新建一个“poppler”文件夹,将poppler中qt5目录下的文件都丢进去(*.h头文件,另外再将编译好的2个*.a文件和2个*.dll丢进去,我这里多丢了实现的*.cc文件,因为*.cc已经被编译成动态库了,所以可以不用包含在代码中)



2、在项目的pro配置文件中添加以下内容,引用poppler的头文件和库文件(注意:我这里是win32,所以前面加了win32前缀)

INCLUDEPATH += $$PWD/poppler
win32: LIBS += -L$$PWD/poppler -llibpoppler
win32: LIBS += -L$$PWD/poppler -llibpoppler-qt5


3、创建pdf工具类(该类负责与poppler库做对接,主要负责获取pdf的总页数,和每页的图像)

(1)pdfutils.h

  1. #ifndef PDFUTILS_H  
  2. #define PDFUTILS_H  
  3. #include <QObject>  
  4. #include <QImage>  
  5. #include <QSize>  
  6. #include <QDebug>  
  7. #include "poppler-qt5.h"  
  8. class PdfUtils  
  9. {  
  10. public:  
  11.     explicit PdfUtils(QString filePath);  
  12.     ~PdfUtils();  
  13.     //获取指定页pdf图像(页码从0开始)  
  14.     QImage getPdfImage(int pageNumber);  
  15.     //获取pdf总页码  
  16.     int getNumPages();  
  17.     //获取pdf页面大小  
  18.     QSize getPageSize();  
  19. private:  
  20.     QString filePath;  
  21.     int numPages;  
  22.     QSize pageSize;  
  23.     void getPdfInfo();  
  24. };  
  25. #endif // PDFUTILS_H  

(2)pdfutils.cpp
  1. #include "pdfutils.h"  
  2. PdfUtils::PdfUtils(QString filePath) {  
  3.     this->filePath = filePath;  
  4.     getPdfInfo();  
  5. }  
  6. PdfUtils::~PdfUtils() {  
  7. }  
  8. QImage PdfUtils::getPdfImage(int pageNumber) {  
  9.     QImage image;  
  10.     Poppler::Document* document = Poppler::Document::load(filePath);  
  11.     if (!document || document->isLocked()) {  
  12.         // ... error message ....  
  13.         delete document;  
  14.         return image;  
  15.     }  
  16.     // Document starts at page 0  
  17.     Poppler::Page* pdfPage = document->page(pageNumber);  
  18.     if (pdfPage == 0) {  
  19.         // ... error message ...  
  20.         return image;  
  21.     }  
  22.     // Generate a QImage of the rendered page  
  23.     image = pdfPage->renderToImage(72, 72, -1, -1, -1, -1);  
  24.     if (image.isNull()) {  
  25.         // ... error message ...  
  26.         return image;  
  27.     }  
  28.     // after the usage, the page must be deleted  
  29.     delete pdfPage;  
  30.     delete document;  
  31.     return image;  
  32. }  
  33. int PdfUtils::getNumPages() {  
  34.     return numPages;  
  35. }  
  36. QSize PdfUtils::getPageSize() {  
  37.     return pageSize;  
  38. }  
  39. void PdfUtils::getPdfInfo() {  
  40.     numPages = 0;  
  41.     Poppler::Document* document = Poppler::Document::load(filePath);  
  42.     if (!document || document->isLocked()) {  
  43.         // ... error message ....  
  44.         delete document;  
  45.         return;  
  46.     }  
  47.     numPages = document->numPages();  
  48.     Poppler::Page* pdfPage = document->page(0);  
  49.     pageSize = pdfPage->pageSize();  
  50.     qDebug()<<pageSize;  
  51.     delete pdfPage;  
  52.     delete document;  
  53. }  
4、pdf显示类(pdf的右侧显示滚动条,①拖动滚动条翻页 ②鼠标拖动pdf到最上或最底时翻页)

注意:本文省略了页面缓存,如果是真实的项目的话,本着严谨的态度,请务必缓存页面

(1)mypdfcanvas.h(继承父类的resizeEvent是为了 ①当pdf只有1页时不显示滚动条 ②当用户拖动缩放窗口时动态改变pdf显示尺寸)

  1. #ifndef MYPDFCANVAS_H  
  2. #define MYPDFCANVAS_H  
  3. #include <QWidget>  
  4. #include <QVector>  
  5. #include <QMouseEvent>  
  6. #include <QPaintEvent>  
  7. #include <QPainter>  
  8. #include <QPaintEvent>  
  9. #include <QMap>  
  10. #include <QPalette>  
  11. #include <QResizeEvent>  
  12. #include "pdfutils.h"  
  13. class MyPdfCanvas : public QWidget  
  14. {  
  15.     Q_OBJECT  
  16. public:  
  17.     explicit MyPdfCanvas(QWidget *parent = 0);  
  18.     ~MyPdfCanvas();  
  19.     void resizeEvent(QResizeEvent* e);  
  20.     void paintEvent(QPaintEvent *e);  
  21.     void mousePressEvent(QMouseEvent *e);  
  22.     void mouseReleaseEvent(QMouseEvent *e);  
  23.     void mouseMoveEvent(QMouseEvent *e);  
  24.     void setMaxCachedNum(int maxCachedNum);  
  25.     //如果不能解析pdf返回false  
  26.     bool setPath(QString pdfPath);  
  27.     //页码从0开始  
  28.     bool setPage(int pageNumber);  
  29.     //获取页数  
  30.     int getNumPages();  
  31.     float getScaledRatio();  
  32.     //显示裁剪后的图片  
  33.     bool showClipImage(int pageNumber, int x, int y, int w, int h);  
  34.     //取消显示裁剪图片,恢复正常显示  
  35.     void cancelClip();  
  36.     //实际获取的pdf宽高度  
  37.     QSize pdfActualSize;  
  38. signals:  
  39.     void pageChanged(int currentPage);  
  40. private:  
  41.     PdfUtils* pdfUtils;  
  42.     QString pdfPath;  
  43.     //最大缓存图片数量  
  44.     int maxCachedNum;  
  45.     //用来缓存pdf的每一个页面的图像(从0开始)  
  46.     QMap<int, QImage> cachedImageMap;  
  47.     //用来存储已缓存的pdf页面序号(从0开始)  
  48. //    QQueue<int> cachedPageQueue;  
  49.     //当前页码(从0开始)  
  50.     int currentPage;  
  51.     //总页码(从0开始)  
  52.     int numPages;  
  53.     bool isMouseDown;  
  54.     int lastMouseY;  
  55.     //当前pdf页面的图像  
  56.     QImage image;  
  57.     int imageX;  
  58.     int imageY;  
  59.     int imageMinY;  
  60.     //是否是剪裁状态  
  61.     bool isClip;  
  62.     //获取指定页的图片  
  63.     bool getPdfImage(int pageNumber);  
  64.     void reachTop();  
  65.     void reachBottom();  
  66.     //判断是否需要发送重定位签名框的信号  
  67.     void needLocateSignArea();  
  68. };  
  69. #endif // MYPDFCANVAS_H  

(2)pdfcanvas.cpp

  1. #include "mypdfcanvas.h"  
  2. MyPdfCanvas::MyPdfCanvas(QWidget *parent) : QWidget(parent) {  
  3.     pdfUtils = NULL;  
  4.     imageX = 0;  
  5.     imageY = 0;  
  6.     isClip = false;  
  7.     setAutoFillBackground(true);  
  8. }  
  9. MyPdfCanvas::~MyPdfCanvas() {  
  10.     if(pdfUtils != NULL) delete pdfUtils;  
  11. }  
  12. void MyPdfCanvas::resizeEvent(QResizeEvent *e) {  
  13.     image = this->cachedImageMap[currentPage];  
  14.     if(!image.isNull()) {  
  15.         float radio = (float)e->size().width()/(float)e->oldSize().width();  
  16.         int imageHeight = image.height()* e->size().width()/image.width();  
  17.         image = image.scaled(e->size().width(), imageHeight);  
  18.         if(imageHeight < this->height()) {  
  19.             imageY = (this->height()-imageHeight)/2;  
  20.             //如果图片高度小于控件高度,则图片居中  
  21. //            imageMinY = imageY;  
  22.             imageMinY = 0;  
  23.             imageY = imageMinY;  
  24.         } else {  
  25.             if(radio>0) {  
  26.                 imageY = (int)(imageY*radio);  
  27.                 if(imageY > 0) {  
  28.                     imageY = 0;  
  29.                 }  
  30.             } else {  
  31.                 imageY = 0;  
  32.             }  
  33.         }  
  34.     }  
  35. }  
  36. void MyPdfCanvas::paintEvent(QPaintEvent *e) {  
  37.     QPainter* painter = new QPainter(this);  
  38.     if(image.isNull()) {  
  39.         painter->fillRect(this->rect(), Qt::transparent);  
  40.         return;  
  41.     }  
  42.     painter->drawImage(0, imageY, image);  
  43.     delete painter;  
  44. }  
  45. void MyPdfCanvas::mousePressEvent(QMouseEvent *e) {  
  46.     isMouseDown = true;  
  47.     lastMouseY = e->y();  
  48. }  
  49. void MyPdfCanvas::mouseReleaseEvent(QMouseEvent *e){  
  50.     isMouseDown = false;  
  51. }  
  52. void MyPdfCanvas::mouseMoveEvent(QMouseEvent *e){  
  53.     if(!isMouseDown || image.isNull()) {  
  54.         return;  
  55.     }  
  56.     int distance = e->y() - lastMouseY;  
  57.     lastMouseY = e->y();  
  58.     imageY += distance;  
  59.     if(imageY > 0) {  
  60.         imageY = 0;  
  61.         reachTop();  
  62.         return;  
  63.     } else if(imageY < imageMinY) {  
  64.         imageY = imageMinY;  
  65.         reachBottom();  
  66.         return;  
  67.     }  
  68.     update();  
  69. }  
  70. void MyPdfCanvas::setMaxCachedNum(int maxCachedNum) {  
  71.     this->maxCachedNum = maxCachedNum;  
  72. }  
  73. bool MyPdfCanvas::setPath(QString pdfPath) {  
  74.     this->pdfPath = pdfPath;  
  75.     if(pdfUtils != NULL) delete pdfUtils;  
  76.     pdfUtils = new PdfUtils(pdfPath);  
  77.     numPages = pdfUtils->getNumPages();  
  78.     if(numPages > 0) {  
  79.         isClip = false;  
  80.         pdfActualSize = pdfUtils->getPageSize();  
  81.     }  
  82.     cachedImageMap.clear();  
  83.     currentPage = 0;  
  84.     imageY = 0;  
  85.     lastMouseY = 0;  
  86.     return numPages > 0;  
  87. }  
  88. bool MyPdfCanvas::setPage(int pageNumber) {  
  89.     if(!getPdfImage(pageNumber)) {  
  90.         return false;  
  91.     }  
  92.     isClip = false;  
  93.     isMouseDown = false;  
  94.     image = image.scaledToWidth(this->width());  
  95.     imageMinY = this->height() - image.height();  
  96.     if(image.height() < this->height()) {  
  97.         //如果图片高度小于控件高度,则图片居中  
  98. //        imageMinY /= 2;  
  99.         imageMinY = 0;  
  100.         imageY = imageMinY;  
  101.     } else {  
  102.         imageY = 0;  
  103.     }  
  104.     update();  
  105.     return true;  
  106. }  
  107. int MyPdfCanvas::getNumPages() {  
  108.     return numPages;  
  109. }  
  110. float MyPdfCanvas::getScaledRatio() {  
  111.     int pdfWidth = pdfUtils->getPageSize().width();  
  112.     return (float)this->width()/(float)pdfWidth;  
  113. }  
  114. bool MyPdfCanvas::showClipImage(int pageNumber, int x, int y, int w, int h) {  
  115.     if(!getPdfImage(pageNumber)) {  
  116.         return false;  
  117.     }  
  118.     isClip = true;  
  119.     imageY = 0;  
  120.     image = image.copy(x, y, w, h).scaled(this->size());  
  121.     update();  
  122. }  
  123. void MyPdfCanvas::cancelClip() {  
  124.     isClip = false;  
  125.     setPage(currentPage);  
  126. }  
  127. bool MyPdfCanvas::getPdfImage(int pageNumber) {  
  128.     if(pageNumber<0 || pageNumber >= numPages) {  
  129.         return false;  
  130.     }  
  131.     if(cachedImageMap.contains(pageNumber)) {  
  132.         image = cachedImageMap.value(pageNumber);  
  133.     } else {  
  134.         image = pdfUtils->getPdfImage(pageNumber);  
  135.         if(!image.isNull()) {  
  136.             cachedImageMap[pageNumber] = image;  
  137.             pdfActualSize = image.size();  
  138.         }  
  139.     }  
  140.     if(image.isNull()) {  
  141.         return false;  
  142.     }  
  143.     currentPage = pageNumber;  
  144.     return true;  
  145. }  
  146. void MyPdfCanvas::reachTop() {  
  147.     if(currentPage > 0) {  
  148.         emit pageChanged(currentPage-1);  
  149.     }  
  150. }  
  151. void MyPdfCanvas::reachBottom() {  
  152.     if(currentPage < numPages-1) {  
  153.         emit pageChanged(currentPage+1);  
  154.     }  
  155. }  

5、pdf及右侧滑块的装载容器

(1)mainwindow.h

  1. #ifndef MAINWINDOW_H  
  2. #define MAINWINDOW_H  
  3. #include <QMainWindow>  
  4. #include <QScrollBar>  
  5. #include "mypdfcanvas.h"  
  6. #define SCROLLBAR_WIDTH 30  
  7. class MainWindow : public QMainWindow  
  8. {  
  9.     Q_OBJECT  
  10. public:  
  11.     explicit MainWindow(QWidget *parent = 0);  
  12.     ~MainWindow();  
  13.     void resizeEvent(QResizeEvent* e);  
  14.     bool setPdfPath(QString path);  
  15.     //重新调整pdf界面大小  
  16.     void resizeCanvas();  
  17.     void setWidgetVisible(bool pdfCanvasVisible, bool scrollbarVisible);  
  18. public slots:  
  19.     //当拖动pdf上滑到顶(或下滑到底)时触发该方法  
  20.     onPageChange(int currentPage);  
  21.     //当滑动条的滑块被滑动时,会调用该方法  
  22.     onScrollBarValueChange();  
  23. private:  
  24.     MyPdfCanvas *pdfCanvas;  
  25.     QScrollBar *scrollbar;  
  26. };  
  27. #endif // MAINWINDOW_H  

(2)mainwindow.cpp

  1. #include "mainwindow.h"  
  2. MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) {  
  3.     pdfCanvas = new MyPdfCanvas(this);  
  4.     scrollbar = new QScrollBar(Qt::Vertical, this);  
  5.     setWidgetVisible(false, false);  
  6.     connect(pdfCanvas, SIGNAL(pageChanged(int)), this, SLOT(onPageChange(int)));  
  7.     connect(scrollbar, SIGNAL(valueChanged(int)), this, SLOT(onScrollBarValueChange()));  
  8. }  
  9. MainWindow::~MainWindow() {  
  10. }  
  11. void MainWindow::resizeEvent(QResizeEvent *e) {  
  12.     resizeCanvas();  
  13. }  
  14. bool MainWindow::setPdfPath(QString path) {  
  15.     bool result = pdfCanvas->setPath(path);  
  16.     if(result) {  
  17.         int numPages = pdfCanvas->getNumPages();  
  18.         if(numPages>1) {  
  19.             scrollbar->setMaximum(numPages-1);  
  20.             scrollbar->setValue(0);  
  21.         }  
  22.         pdfCanvas->setPage(0);  
  23.     }  
  24.     resizeCanvas();  
  25.     return result;  
  26. }  
  27. void MainWindow::resizeCanvas() {  
  28.     qDebug()<<"resize "<<this->rect()<<", "<<pdfCanvas->rect();  
  29.     int numPages = pdfCanvas->getNumPages();  
  30.     if(numPages == 1) {  
  31.         pdfCanvas->setGeometry(this->rect());  
  32.         setWidgetVisible(true, false);  
  33.     } else if(numPages > 1) {  
  34.         pdfCanvas->setGeometry(0, 0, this->width()-SCROLLBAR_WIDTH, this->height());  
  35.         scrollbar->setGeometry(this->width()-SCROLLBAR_WIDTH, 0, this->width()-SCROLLBAR_WIDTH, this->height());  
  36.         setWidgetVisible(true, true);  
  37.     } else {  
  38.         //numPages <= 0  
  39.         setWidgetVisible(false, false);  
  40.     }  
  41. }  
  42. void MainWindow::setWidgetVisible(bool pdfCanvasVisible, bool scrollbarVisible) {  
  43.     pdfCanvas->setVisible(pdfCanvasVisible);  
  44.     scrollbar->setVisible(scrollbarVisible);  
  45. }  
  46. MainWindow::onPageChange(int currentPage) {  
  47.     pdfCanvas->setPage(currentPage);  
  48. }  
  49. MainWindow::onScrollBarValueChange() {  
  50.     pdfCanvas->setPage(scrollbar->value());  
  51. }  

6、调用方式

(1)main.cpp

  1. #include "mainwindow.h"  
  2. #include <QApplication>  
  3. int main(int argc, char *argv[])  
  4. {  
  5.     QApplication a(argc, argv);  
  6.     MainWindow w;  
  7.     w.resize(500, 500);  
  8.     w.show();  
  9.     QString path = "D://test.pdf";  
  10.     w.setPdfPath(path);  
  11.     w.setWindowTitle(path);  
  12.     return a.exec();  
  13. }  

7、实际效果图


更新于2016-08-03

8、项目下载地址(使用当前最新的库poppler-0.45.0、poppler-0.39.0-win32)

http://download.csdn.net/detail/chy555chy/9593364

该项目在win7(Qt5.1)、win10(Qt5.7)下测试过了,均可正常运行。

下图为项目目录中的poppler文件夹(已经删去所有.cc文件),因为只用库和头文件,Qt便可隐式调用dll中的函数了。

更新于2016-08-22

你们评论中遇到的加载库的时候就奔溃现象我还真没遇到过。

下面是测试情况:

(1)当PDF文件未找到的情况,会输出错误日志,但是并不会崩溃。


(2)当路径中包含”中文“,且包含"空格"的情况,poppler是可以正常打开的。






本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
ubuntu 下pdf乱码完美解决方案
PDF中文显示
零代码编程:用ChatGPT批量识别图片PDF中的文字
PDF文档的乱码问题 - Ubuntu中文
evince pdf 中文乱码
如何防止QScrollArea缩放过于摇摇晃晃?
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服