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/popplerwin32: LIBS += -L$$PWD/poppler -llibpopplerwin32: LIBS += -L$$PWD/poppler -llibpoppler-qt5

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

(1)pdfutils.h

#ifndef PDFUTILS_H#define PDFUTILS_H#include <QObject>#include <QImage>#include <QSize>#include <QDebug>#include "poppler-qt5.h"class PdfUtils{public:  explicit PdfUtils(QString filePath);  ~PdfUtils();  //获取指定页pdf图像(页码从0开始)  QImage getPdfImage(int pageNumber);  //获取pdf总页码  int getNumPages();  //获取pdf页面大小  QSize getPageSize();private:  QString filePath;  int numPages;  QSize pageSize;  void getPdfInfo();};#endif // PDFUTILS_H

(2)pdfutils.cpp

#include "pdfutils.h"PdfUtils::PdfUtils(QString filePath) {  this->filePath = filePath;  getPdfInfo();}PdfUtils::~PdfUtils() {}QImage PdfUtils::getPdfImage(int pageNumber) {  QImage image;  Poppler::Document* document = Poppler::Document::load(filePath);  if (!document || document->isLocked()) {    // ... error message ....    delete document;    return image;  }  // Document starts at page 0  Poppler::Page* pdfPage = document->page(pageNumber);  if (pdfPage == 0) {    // ... error message ...    return image;  }  // Generate a QImage of the rendered page  image = pdfPage->renderToImage(72, 72, -1, -1, -1, -1);  if (image.isNull()) {    // ... error message ...    return image;  }  // after the usage, the page must be deleted  delete pdfPage;  delete document;  return image;}int PdfUtils::getNumPages() {  return numPages;}QSize PdfUtils::getPageSize() {  return pageSize;}void PdfUtils::getPdfInfo() {  numPages = 0;  Poppler::Document* document = Poppler::Document::load(filePath);  if (!document || document->isLocked()) {    // ... error message ....    delete document;    return;  }  numPages = document->numPages();  Poppler::Page* pdfPage = document->page(0);  pageSize = pdfPage->pageSize();  qDebug()<<pageSize;  delete pdfPage;  delete document;}

4、pdf显示类(pdf的右侧显示滚动条,①拖动滚动条翻页 ②鼠标拖动pdf到最上或最底时翻页)

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

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

#ifndef MYPDFCANVAS_H#define MYPDFCANVAS_H#include <QWidget>#include <QVector>#include <QMouseEvent>#include <QPaintEvent>#include <QPainter>#include <QPaintEvent>#include <QMap>#include <QPalette>#include <QResizeEvent>#include "pdfutils.h"class MyPdfCanvas : public QWidget{  Q_OBJECTpublic:  explicit MyPdfCanvas(QWidget *parent = 0);  ~MyPdfCanvas();  void resizeEvent(QResizeEvent* e);  void paintEvent(QPaintEvent *e);  void mousePressEvent(QMouseEvent *e);  void mouseReleaseEvent(QMouseEvent *e);  void mouseMoveEvent(QMouseEvent *e);  void setMaxCachedNum(int maxCachedNum);  //如果不能解析pdf返回false  bool setPath(QString pdfPath);  //页码从0开始  bool setPage(int pageNumber);  //获取页数  int getNumPages();  float getScaledRatio();  //显示裁剪后的图片  bool showClipImage(int pageNumber, int x, int y, int w, int h);  //取消显示裁剪图片,恢复正常显示  void cancelClip();  //实际获取的pdf宽高度  QSize pdfActualSize;signals:  void pageChanged(int currentPage);private:  PdfUtils* pdfUtils;  QString pdfPath;  //最大缓存图片数量  int maxCachedNum;  //用来缓存pdf的每一个页面的图像(从0开始)  QMap<int, QImage> cachedImageMap;  //用来存储已缓存的pdf页面序号(从0开始)//  QQueue<int> cachedPageQueue;  //当前页码(从0开始)  int currentPage;  //总页码(从0开始)  int numPages;  bool isMouseDown;  int lastMouseY;  //当前pdf页面的图像  QImage image;  int imageX;  int imageY;  int imageMinY;  //是否是剪裁状态  bool isClip;  //获取指定页的图片  bool getPdfImage(int pageNumber);  void reachTop();  void reachBottom();  //判断是否需要发送重定位签名框的信号  void needLocateSignArea();};#endif // MYPDFCANVAS_H

(2)pdfcanvas.cpp

#include "mypdfcanvas.h"MyPdfCanvas::MyPdfCanvas(QWidget *parent) : QWidget(parent) {  pdfUtils = NULL;  imageX = 0;  imageY = 0;  isClip = false;  setAutoFillBackground(true);}MyPdfCanvas::~MyPdfCanvas() {  if(pdfUtils != NULL) delete pdfUtils;}void MyPdfCanvas::resizeEvent(QResizeEvent *e) {  image = this->cachedImageMap[currentPage];  if(!image.isNull()) {    float radio = (float)e->size().width()/(float)e->oldSize().width();    int imageHeight = image.height()* e->size().width()/image.width();    image = image.scaled(e->size().width(), imageHeight);    if(imageHeight < this->height()) {      imageY = (this->height()-imageHeight)/2;      //如果图片高度小于控件高度,则图片居中//      imageMinY = imageY;      imageMinY = 0;      imageY = imageMinY;    } else {      if(radio>0) {        imageY = (int)(imageY*radio);        if(imageY > 0) {          imageY = 0;        }      } else {        imageY = 0;      }    }  }}void MyPdfCanvas::paintEvent(QPaintEvent *e) {  QPainter* painter = new QPainter(this);  if(image.isNull()) {    painter->fillRect(this->rect(), Qt::transparent);    return;  }  painter->drawImage(0, imageY, image);  delete painter;}void MyPdfCanvas::mousePressEvent(QMouseEvent *e) {  isMouseDown = true;  lastMouseY = e->y();}void MyPdfCanvas::mouseReleaseEvent(QMouseEvent *e){  isMouseDown = false;}void MyPdfCanvas::mouseMoveEvent(QMouseEvent *e){  if(!isMouseDown || image.isNull()) {    return;  }  int distance = e->y() - lastMouseY;  lastMouseY = e->y();  imageY += distance;  if(imageY > 0) {    imageY = 0;    reachTop();    return;  } else if(imageY < imageMinY) {    imageY = imageMinY;    reachBottom();    return;  }  update();}void MyPdfCanvas::setMaxCachedNum(int maxCachedNum) {  this->maxCachedNum = maxCachedNum;}bool MyPdfCanvas::setPath(QString pdfPath) {  this->pdfPath = pdfPath;  if(pdfUtils != NULL) delete pdfUtils;  pdfUtils = new PdfUtils(pdfPath);  numPages = pdfUtils->getNumPages();  if(numPages > 0) {    isClip = false;    pdfActualSize = pdfUtils->getPageSize();  }  cachedImageMap.clear();  currentPage = 0;  imageY = 0;  lastMouseY = 0;  return numPages > 0;}bool MyPdfCanvas::setPage(int pageNumber) {  if(!getPdfImage(pageNumber)) {    return false;  }  isClip = false;  isMouseDown = false;  image = image.scaledToWidth(this->width());  imageMinY = this->height() - image.height();  if(image.height() < this->height()) {    //如果图片高度小于控件高度,则图片居中//    imageMinY /= 2;    imageMinY = 0;    imageY = imageMinY;  } else {    imageY = 0;  }  update();  return true;}int MyPdfCanvas::getNumPages() {  return numPages;}float MyPdfCanvas::getScaledRatio() {  int pdfWidth = pdfUtils->getPageSize().width();  return (float)this->width()/(float)pdfWidth;}bool MyPdfCanvas::showClipImage(int pageNumber, int x, int y, int w, int h) {  if(!getPdfImage(pageNumber)) {    return false;  }  isClip = true;  imageY = 0;  image = image.copy(x, y, w, h).scaled(this->size());  update();}void MyPdfCanvas::cancelClip() {  isClip = false;  setPage(currentPage);}bool MyPdfCanvas::getPdfImage(int pageNumber) {  if(pageNumber<0 || pageNumber >= numPages) {    return false;  }  if(cachedImageMap.contains(pageNumber)) {    image = cachedImageMap.value(pageNumber);  } else {    image = pdfUtils->getPdfImage(pageNumber);    if(!image.isNull()) {      cachedImageMap[pageNumber] = image;      pdfActualSize = image.size();    }  }  if(image.isNull()) {    return false;  }  currentPage = pageNumber;  return true;}void MyPdfCanvas::reachTop() {  if(currentPage > 0) {    emit pageChanged(currentPage-1);  }}void MyPdfCanvas::reachBottom() {  if(currentPage < numPages-1) {    emit pageChanged(currentPage+1);  }}

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

(1)mainwindow.h

#ifndef MAINWINDOW_H#define MAINWINDOW_H#include <QMainWindow>#include <QScrollBar>#include "mypdfcanvas.h"#define SCROLLBAR_WIDTH 30class MainWindow : public QMainWindow{  Q_OBJECTpublic:  explicit MainWindow(QWidget *parent = 0);  ~MainWindow();  void resizeEvent(QResizeEvent* e);  bool setPdfPath(QString path);  //重新调整pdf界面大小  void resizeCanvas();  void setWidgetVisible(bool pdfCanvasVisible, bool scrollbarVisible);public slots:  //当拖动pdf上滑到顶(或下滑到底)时触发该方法  onPageChange(int currentPage);  //当滑动条的滑块被滑动时,会调用该方法  onScrollBarValueChange();private:  MyPdfCanvas *pdfCanvas;  QScrollBar *scrollbar;};#endif // MAINWINDOW_H

(2)mainwindow.cpp

#include "mainwindow.h"MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) {  pdfCanvas = new MyPdfCanvas(this);  scrollbar = new QScrollBar(Qt::Vertical, this);  setWidgetVisible(false, false);  connect(pdfCanvas, SIGNAL(pageChanged(int)), this, SLOT(onPageChange(int)));  connect(scrollbar, SIGNAL(valueChanged(int)), this, SLOT(onScrollBarValueChange()));}MainWindow::~MainWindow() {}void MainWindow::resizeEvent(QResizeEvent *e) {  resizeCanvas();}bool MainWindow::setPdfPath(QString path) {  bool result = pdfCanvas->setPath(path);  if(result) {    int numPages = pdfCanvas->getNumPages();    if(numPages>1) {      scrollbar->setMaximum(numPages-1);      scrollbar->setValue(0);    }    pdfCanvas->setPage(0);  }  resizeCanvas();  return result;}void MainWindow::resizeCanvas() {  qDebug()<<"resize "<<this->rect()<<", "<<pdfCanvas->rect();  int numPages = pdfCanvas->getNumPages();  if(numPages == 1) {    pdfCanvas->setGeometry(this->rect());    setWidgetVisible(true, false);  } else if(numPages > 1) {    pdfCanvas->setGeometry(0, 0, this->width()-SCROLLBAR_WIDTH, this->height());    scrollbar->setGeometry(this->width()-SCROLLBAR_WIDTH, 0, this->width()-SCROLLBAR_WIDTH, this->height());    setWidgetVisible(true, true);  } else {    //numPages <= 0    setWidgetVisible(false, false);  }}void MainWindow::setWidgetVisible(bool pdfCanvasVisible, bool scrollbarVisible) {  pdfCanvas->setVisible(pdfCanvasVisible);  scrollbar->setVisible(scrollbarVisible);}MainWindow::onPageChange(int currentPage) {  pdfCanvas->setPage(currentPage);}MainWindow::onScrollBarValueChange() {  pdfCanvas->setPage(scrollbar->value());}

6、调用方式

(1)main.cpp

#include "mainwindow.h"#include <QApplication>int main(int argc, char *argv[]){  QApplication a(argc, argv);  MainWindow w;  w.resize(500, 500);  w.show();  QString path = "D://test.pdf";  w.setPdfPath(path);  w.setWindowTitle(path);  return a.exec();}

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是可以正常打开的。

以上这篇Qt 使用Poppler实现pdf阅读器的示例代码就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持。

在向山靠近一点,才发现这座山,好象一位诗人遥望远方,

Qt 使用Poppler实现pdf阅读器的示例代码

相关文章:

你感兴趣的文章:

标签云: