您好,登錄后才能下訂單哦!
開發(fā)環(huán)境 Qt5.5.1、Qt Creator 3.5.1
Qt實(shí)現(xiàn)pdf閱讀器和MFC實(shí)現(xiàn)pdf閱讀器,其實(shí)原理都是差不多的。
需要用到Poppler開源庫,下載地址如下 https://poppler.freedesktop.org/
如果只是要在window的gcc下運(yùn)行的話,可以下載已經(jīng)編譯好的庫 https://sourceforge.net/projects/poppler-win32/
注意:這個(gè)是MinGW版本的Qt,也就是運(yùn)行在GCC環(huán)境下的庫,里面只包含 *.dll 和 *.a 。如果是Vistual Studio版本的Qt ,那么很不幸里面沒有 *.lib文件。
1、新建項(xiàng)目,在項(xiàng)目的根目錄新建一個(gè)“poppler”文件夾,將poppler中qt5目錄下的文件都丟進(jìn)去(*.h頭文件,另外再將編譯好的2個(gè)*.a文件和2個(gè)*.dll丟進(jìn)去,我這里多丟了實(shí)現(xiàn)的*.cc文件,因?yàn)?.cc已經(jīng)被編譯成動(dòng)態(tài)庫了,所以可以不用包含在代碼中)
2、在項(xiàng)目的pro配置文件中添加以下內(nèi)容,引用poppler的頭文件和庫文件(注意:我這里是win32,所以前面加了win32前綴)
INCLUDEPATH += $$PWD/poppler win32: LIBS += -L$$PWD/poppler -llibpoppler win32: LIBS += -L$$PWD/poppler -llibpoppler-qt5
3、創(chuàng)建pdf工具類(該類負(fù)責(zé)與poppler庫做對(duì)接,主要負(fù)責(zé)獲取pdf的總頁數(shù),和每頁的圖像)
(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的右側(cè)顯示滾動(dòng)條,①拖動(dòng)滾動(dòng)條翻頁 ②鼠標(biāo)拖動(dòng)pdf到最上或最底時(shí)翻頁)
注意:本文省略了頁面緩存,如果是真實(shí)的項(xiàng)目的話,本著嚴(yán)謹(jǐn)?shù)膽B(tài)度,請(qǐng)務(wù)必緩存頁面
(1)mypdfcanvas.h(繼承父類的resizeEvent是為了 ①當(dāng)pdf只有1頁時(shí)不顯示滾動(dòng)條 ②當(dāng)用戶拖動(dòng)縮放窗口時(shí)動(dòng)態(tài)改變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); //獲取頁數(shù) int getNumPages(); float getScaledRatio(); //顯示裁剪后的圖片 bool showClipImage(int pageNumber, int x, int y, int w, int h); //取消顯示裁剪圖片,恢復(fù)正常顯示 void cancelClip(); //實(shí)際獲取的pdf寬高度 QSize pdfActualSize; signals: void pageChanged(int currentPage); private: PdfUtils* pdfUtils; QString pdfPath; //最大緩存圖片數(shù)量 int maxCachedNum; //用來緩存pdf的每一個(gè)頁面的圖像(從0開始) QMap<int, QImage> cachedImageMap; //用來存儲(chǔ)已緩存的pdf頁面序號(hào)(從0開始) // QQueue<int> cachedPageQueue; //當(dāng)前頁碼(從0開始) int currentPage; //總頁碼(從0開始) int numPages; bool isMouseDown; int lastMouseY; //當(dāng)前pdf頁面的圖像 QImage image; int imageX; int imageY; int imageMinY; //是否是剪裁狀態(tài) bool isClip; //獲取指定頁的圖片 bool getPdfImage(int pageNumber); void reachTop(); void reachBottom(); //判斷是否需要發(fā)送重定位簽名框的信號(hào) 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及右側(cè)滑塊的裝載容器
(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); //重新調(diào)整pdf界面大小 void resizeCanvas(); void setWidgetVisible(bool pdfCanvasVisible, bool scrollbarVisible); public slots: //當(dāng)拖動(dòng)pdf上滑到頂(或下滑到底)時(shí)觸發(fā)該方法 onPageChange(int currentPage); //當(dāng)滑動(dòng)條的滑塊被滑動(dòng)時(shí),會(huì)調(diào)用該方法 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、調(diào)用方式
(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、實(shí)際效果圖
更新于2016-08-03
8、項(xiàng)目下載地址(使用當(dāng)前最新的庫poppler-0.45.0、poppler-0.39.0-win32)
http://download.csdn.net/detail/chy555chy/9593364
該項(xiàng)目在win7(Qt5.1)、win10(Qt5.7)下測(cè)試過了,均可正常運(yùn)行。
下圖為項(xiàng)目目錄中的poppler文件夾(已經(jīng)刪去所有.cc文件),因?yàn)橹挥脦旌皖^文件,Qt便可隱式調(diào)用dll中的函數(shù)了。
更新于2016-08-22
你們?cè)u(píng)論中遇到的加載庫的時(shí)候就奔潰現(xiàn)象我還真沒遇到過。
下面是測(cè)試情況:
(1)當(dāng)PDF文件未找到的情況,會(huì)輸出錯(cuò)誤日志,但是并不會(huì)崩潰。
(2)當(dāng)路徑中包含”中文“,且包含"空格"的情況,poppler是可以正常打開的。
以上這篇Qt 使用Poppler實(shí)現(xiàn)pdf閱讀器的示例代碼就是小編分享給大家的全部?jī)?nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持億速云。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。