qt用c语言编程 pdf下载文件,Qt 使用Poppler实现pdf阅读器的示例代码

开发环境 Qt5.5.1、Qt Creator 3.5.1

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

b3772e68ed43b5d4376c4ae780a92238.png

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

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

082da10c50849db3360d8bd41afb6ff2.png

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

#include

#include

#include

#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()<

delete pdfPage;

delete document;

}

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

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

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

#ifndef MYPDFCANVAS_H

#define MYPDFCANVAS_H

#include

#include

#include

#include

#include

#include

#include

#include

#include

#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 cachedImageMap;

//用来存储已缓存的pdf页面序号(从0开始)

// QQueue 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

#include

#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()<rect()<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

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、实际效果图

99050ec404ead8cabd985e3d79669b8c.gif

更新于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中的函数了。

126234770c5a894c7de1d351a39c220e.png

更新于2016-08-22

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

下面是测试情况:

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

b857697a37f8dde757680b6bd5c0aeac.png

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

f72749dcf786ec76c1652fb84e242a7c.png

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/571368.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

redis 分布式锁 看门狗_分布式锁Redisson的使用,看门狗机制

Redisson简介Redisson是一个在Redis的基础上实现的Java驻内存数据网格(In-Memory Data Grid)。它不仅提供了一系列的分布式的Java常用对象&#xff0c;还提供了许多分布式服务。其中包括(BitSet, Set, Multimap, SortedSet, Map, List, Queue, BlockingQueue, Deque, BlockingD…

BZOJ 1018: [SHOI2008]堵塞的交通traffic

二次联通门 : BZOJ 1018: [SHOI2008]堵塞的交通traffic /*BZOJ 1018: [SHOI2008]堵塞的交通traffic麻麻这题玩我这题简直消磨人的意志写了一天了写一段玩一段直接写不下去什么时候恢复一下心情再写*/ #include <cstdio> #include <iostream>#define rg register in…

c语言二叉树的头文件叫什么,西安交大朱站立数据结构——使用C语言》头文件系列——二叉树.doc...

西安交大朱站立数据结构——使用C语言》头文件系列——二叉树本文为二叉链存储结构的二叉树操作实现&#xff0c;实现了二叉树的定义、插入数据、删除数据、撤销以及二叉树的打印、前序遍历、中序遍历、后序遍历等。本项目工程包含2个头文件(BiTree.h、BiTreeTraverse.h)和一个…

rfcv函数实现_OpenSSL AES_ctr128_encrypt()作为伪随机函数在RFC3711(SRTP)

128位的主密钥&#xff1a;E1F97A0D3E018BE0D64FA32C06DE4139112位输入矢量&#xff1a;0EC675AD498AFEEBB6960B3AABE616位计数器&#xff1a;0000喂奶时的级联将112位输入向量和16位计数器转换为AES_ctr128_encrypt()我是不是得到预期的输出附录B.3的RFC3711列出了。(根据RFC3…

linux/npm/git的简单操作

下面是命令操作的详细步骤&#xff0c;每一步都是相连接的&#xff1b;对于git的相关命令&#xff0c;包括了团队开发时候如何操作&#xff0c;以及QA如何测试的&#xff1b;都挺详细的&#xff0c;可能有注释的理解不太正确&#xff0c;可以进行补充&#xff1b; 也可以看着之…

C语言工厂方法模式,工厂函数模式 (C语言实现)

工厂模式属于创建型模式&#xff0c;大致可以分为三类&#xff0c;简单工厂模式、工厂方法模式、抽象工厂模式。二. 工厂方法模式所谓工厂方法模式&#xff0c;是指定义一个用于创建对象的接口&#xff0c;让子类决定实例化哪一个类。Factory Method使一个类的实例化延迟到其子…

c语言代码 linux 关机_android linux 关机流程

To power off an Android phone, keep pressing power bottom then shutdown menu is appeard. Then choose ‘Power OFF’ to power off actually.I looked for the source code to see how to do these sequence.(Japanese version of this page)Showing shutdown menuHere i…

光绘文件 c语言 解析,AltiumDesigner输出光绘文件

1、钻孔信息生成设置打开PCB文件&#xff0c;放置钻孔位图符号对应的孔大小的列表&#xff1a;切换到DrillDrawing层放置字符串&#xff0c;Place->string(PS)&#xff0c;此时按Tab键&#xff0c;在出现的Text文本框中点击下拉箭头&#xff0c;选择.Legend&#xff0c;OK。…

logback日志pattern_Logback pattern transactionid 中如何自定义灵活的日志过滤规则

当我们需要对日志的打印要做一些范围的控制的时候&#xff0c;通常都是通过为各个Appender设置不同的Filter配置来实现。在Logback中自带了两个过滤器实现&#xff1a;ch.qos.logback.classic.filter.LevelFilter和ch.qos.logback.classic.filter.ThresholdFilter&#xff0c;用…

曼哈顿距离最小生成树与莫队算法(总结)

曼哈顿距离最小生成树与莫队算法&#xff08;总结&#xff09; 1 曼哈顿距离最小生成树 曼哈顿距离最小生成树问题可以简述如下&#xff1a; 给定二维平面上的N个点&#xff0c;在两点之间连边的代价为其曼哈顿距离&#xff0c;求使所有点连通的最小代价。 朴素的算法可以用O(N…

功能引导 android,Flutter实现App功能引导页

App功能介绍页&#xff0c;主要是由介绍app功能的几张图片和当前页指示符组成&#xff0c;如下效果我们来一步一步实现上面的界面,左右滑动切换显示功能页,这个可以通过PageView来实现,底部的指示符半透明覆盖在PageView上,开发过Android同学知道可以用Framelayout布局来实现&a…

vue项目调用jssip_JsSIP和FreeSWITCH整合

写在前面&#xff1a;FreeSWITCH作为服务器&#xff0c;通过SIP协议&#xff0c;Web端使用jssipwebrtc和其他软电话进行通信一、先配置FreeSWITCH(用的版本1.6.20)的配置&#xff1a;1 、修改vars.xml文件&#xff0c;找到下面字段&#xff0c;并设置2、修改 autoload_configs/…

ios禁止页面下拉

document.querySelector(body).addEventListener(touchmove, function(e) { e.preventDefault();})此类事件是手机touchmove默认事件行为&#xff0c;可以通过js代码禁止默认事件&#xff1a;转载于:https://www.cnblogs.com/zhouyx/p/8145182.html

android中虚拟程序停止,为什么我的在虚拟机运行后出现应用程序停止运行

源自&#xff1a;9-2 使用ToggleButton按钮实现开关效果为什么我的在虚拟机运行后出现应用程序停止运行package com.example.sss;import com.example.android4_0.R;import android.app.Activity;import android.os.Bundle;import android.view.Menu;import android.view.MenuIt…

android layout(l, t, r, b);,服务器里的a,t,l,r,b是什么意思? Android编程中关于layout(l,t,r,b)函数的问题...

导航&#xff1a;网站首页 >服务器里的a,t,l,r,b是什么意思? Android编程中关于layout(l,t,r,b)函数的问题服务器里的a,t,l,r,b是什么意思? Android编程中关于layout(l,t,r,b)函数的问题相关问题:匿名网友:我们知道&#xff0c;屏幕的分辨率往往以“像素数 x 像素数”的格…

python里else中文意思_Python循环语句中else的用法总结

前言本文讨论Python的for…else和while…else等语法&#xff0c;这些是Python中最不常用、最为误解的语法特性之一。Python中的for、while等循环都有一个可选的else分支(类似if语句和try语句那样)&#xff0c;在循环迭代正常完成之后执行。换句话说&#xff0c;如果我们不是以除…

go语言 方法

go 语言中没有类&#xff0c;但是是有方法。 方法的定义&#xff1a; func (recevier type) methodName(参数列表)(返回值列表){ } type School struct {Name stringAddr string }func NewSchool(name, addr string) *School {return &School {Name:name,Addr:addr,} }fun…

android 获取加速度传感器值,Android开发获取传感器数据的方法示例【加速度传感器,磁场传感器,光线传感器,方向传感器】...

本文实例讲述了Android开发获取传感器数据的方法。分享给大家供大家参考&#xff0c;具体如下&#xff1a;package mobile.android.sensor;import java.util.List;import android.app.Activity;import android.hardware.Sensor;import android.hardware.SensorEvent;import and…

ocp oracle 考试报名_获得Oracle认证对拓展职业前景的影响

知识就是力量通过考试和考试成绩固然很重要&#xff0c;但您在学习过程中所掌握的知识才是您职业成功的坚实基础。在备考过程中&#xff0c;获得的知识和经验才是您真正的财富。Oracle Certified Associate (OCA)&#xff1a;专员级别的认证能够证明学员具备基本技能&#xff0…

SpringMVC(2)—SpringMVC整合Spring的HelloWorld

一、这是一个SpringMVC框架的案例HelloWorld 功能&#xff1a;HelloWorld 二、SpringMVC运行流程 1.流程 请求——>springDispatcherServlet的url-pattern检查SpringMVC中是否存在对应的映射无&#xff1a;检查是否配置的<mvc:default-servlet-handler/>无&#xff…