时间:2021-05-20
开发环境 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-qt53、创建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阅读器的示例代码就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持。
声明:本页内容来源网络,仅供用户参考;我单位不保证亦不表示资料全面及准确无误,也不保证亦不表示这些资料为最新信息,如因任何原因,本网内容或者用户因倚赖本网内容造成任何损失或损害,我单位将不会负任何法律责任。如涉及版权问题,请提交至online#300.cn邮箱联系删除。
在我们阅读PDF格式文件时,需要使用阅读器才能打开,smartPDF阅读器可以帮助我们打开PDF文件进行阅读,用户们可以通过smartPDF阅读器来阅读各类格式
如何用福昕阅读器将PDF文档转换成图片文件?福昕PDF阅读器(FoxitReader)是一款免费小巧的PDF文档阅读器和打印器,福昕阅读器拥有快捷的启动速度和丰
福昕PDF如何拆分文件?福昕阅读器作为一款在全球流行的PDF阅读器,能够快速打开、浏览、审阅、注释、签署及打印PDF文件。在本文中小编为大家介绍的是使用这款软件
福昕pdf阅读器如何将PDF文件转换成TXT文本文件?福昕pdf阅读器是一款完全免费的PDF阅读器,是一款集编辑、转换文档于一体的工具,在接下来的文档中小编为大
如果电脑上没有任何pdf阅读器的。它默认是会用网页打开的。如果不想的话就要再装一个PDF阅读器,比如AdobeReaderX之类的。PDF(PortableDo