开发环境 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
- #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_OBJECT
- public:
- 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 30
- class MainWindow : public QMainWindow
- {
- Q_OBJECT
- public:
- 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是可以正常打开的。
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请
点击举报。