Qt pdf分割成png格式
记录下最近在Qt5.6.3 32位QtCreator环境下,使用MuPDF库将pdf分割成png。下面贴下相关文件内容
库目录
先在“..\PDFSplit\3rdparty\mupdf\platform\win32”目录下编译mupdf.sln生成“libmupdf.lib”,
下面是pro文件内容
QT += core gui printsupport
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
CONFIG += c++11
# You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
SOURCES += \
main.cpp \
mainwindow.cpp
HEADERS += \
mainwindow.h
FORMS += \
mainwindow.ui
UI_DIR=temp
MOC_DIR=temp
OBJECTS_DIR=temp
RCC_DIR=temp
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
win32: LIBS += -L$$PWD/3rdparty/lib/ -llibmupdf
INCLUDEPATH += $$PWD/3rdparty/include
DEPENDPATH += $$PWD/3rdparty/include
下面是mainWindow.cpp内容
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QFileDialog>
#include <QThreadPool>
#include <QDebug>
#include <QFileInfo>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
,ui(new Ui::MainWindow)
,m_pdfPageToPngTask(nullptr)
{
ui->setupUi(this);
this->setWindowTitle(QStringLiteral("PDF拆分成png"));
m_pdfPageToPngTask=new PdfPageToPngTask;
connect(m_pdfPageToPngTask,&PdfPageToPngTask::fileSaved,this,&MainWindow::onFileSaved,Qt::BlockingQueuedConnection);
connect(m_pdfPageToPngTask,&PdfPageToPngTask::sigFinished,this,[=](){
QMessageBox::information(this,QStringLiteral("提示"),QStringLiteral("拆分完成!!!"));
},Qt::BlockingQueuedConnection);
}
void MainWindow::onFileSaved(QString fileName)
{
ui->listWidget->addItem(fileName);
}
void MainWindow::on_pushButtonLoad_clicked()
{
auto temp = QFileDialog::getOpenFileName(this, "", qApp->applicationDirPath(), "*.pdf");
if(temp.isEmpty())
{
return;
}
m_pdfPath=temp;
ui->listWidget->addItem(m_pdfPath);
}
void MainWindow::on_pushButtonCreatPng_clicked()
{
if (m_pdfPath.isEmpty())
return;
// 弹出文件夹选择对话框,获取用于保存图片的文件夹路径
auto dir = QFileDialog::getExistingDirectory(this, "", qApp->applicationDirPath());
if (dir.isEmpty())
return;
if(m_pdfPageToPngTask->isRunning())
{
m_pdfPageToPngTask->m_lock.lock();
m_pdfPageToPngTask->m_isStop=true;
m_pdfPageToPngTask->m_pdfPath.clear();
m_pdfPageToPngTask->m_lock.unlock();
m_pdfPageToPngTask->quit();
m_pdfPageToPngTask->wait();
}
m_pdfPageToPngTask->m_pdfPath=m_pdfPath;
m_pdfPageToPngTask->m_saveDir=dir;
m_pdfPageToPngTask->start();
}
MainWindow::~MainWindow()
{
if(m_pdfPageToPngTask!=nullptr)
{
if(m_pdfPageToPngTask->isRunning())
{
m_pdfPageToPngTask->m_lock.lock();
m_pdfPageToPngTask->m_isStop=true;
m_pdfPageToPngTask->m_lock.unlock();
m_pdfPageToPngTask->quit();
m_pdfPageToPngTask->wait();
}
delete m_pdfPageToPngTask;
m_pdfPageToPngTask=nullptr;
}
delete ui;
}
void MainWindow::on_listWidget_itemSelectionChanged()
{
auto item=ui->listWidget->currentItem();
auto path = item->text();
if(path.isEmpty())return;
QImage *image=new QImage;
image->load(path);
ui->label->setPixmap(QPixmap::fromImage(*image));
ui->label->setAlignment(Qt::AlignCenter);
QFileInfo fileInfo(path);
ui->groupBox->setTitle(QStringLiteral("预览-")+fileInfo.baseName());
}
void MainWindow::on_pushButtonLoadPNG_clicked()
{
auto temp = QFileDialog::getOpenFileNames(this, "", qApp->applicationDirPath(), "*.png");
if(temp.isEmpty())
{
return;
}
for(const QString& filePath:temp)
{
QFileInfo fileInfo(filePath);
if(fileInfo.suffix()!="png")continue;
ui->listWidgetPNG->addItem(fileInfo.filePath());
}
}
void MainWindow::on_pushButtonCreatPDF_clicked()
{
auto temp = QFileDialog::getSaveFileName(this, "", qApp->applicationDirPath(), "*.pdf");
if(temp.isEmpty())
{
return;
}
QPrinter *printer = new QPrinter;
QPainter *painter = new QPainter;
QString PdfName = temp;
printer->setPageSize(QPrinter::A4);
printer->setOutputFormat(QPrinter::PdfFormat);
printer->setOutputFileName(PdfName);
painter->begin(printer);
if(printer == NULL)
return;
// A4纸尺寸相关参数(单位:像素,这里假设了一定的分辨率来转换,可根据实际情况调整)
const int a4Width = 2480; // 示例宽度,对应常见的分辨率转换后的宽度,可按需改
const int a4Height = 3508; // 示例高度,对应常见的分辨率转换后的高度,可按需改
for(int i = 0; i < ui->listWidgetPNG->count(); ++i)
{
QFileInfo fileInfo(ui->listWidgetPNG->item(i)->text());
qDebug() << fileInfo.suffix();
if(fileInfo.suffix()!= "png")
continue;
QImage *image = new QImage;
image->load(fileInfo.filePath());//读取图片
// 调整图片大小以适应A4纸尺寸(这里简单按照等比例缩放,可根据需求完善比如拉伸等处理)
if (image->width()!= a4Width || image->height()!= a4Height)
{
image = new QImage(image->scaled(a4Width, a4Height, Qt::KeepAspectRatio));
}
// 绘制图片到PDF页面,左上角坐标(0, 0)开始绘制,铺满整个页面
painter->drawImage(0, 0, *image);
// 每绘制完一张图片,开启新的页面用于放置下一张图片
printer->newPage();
}
painter->end();
QMessageBox::information(this, QStringLiteral("提示"), QStringLiteral("合成pdf完成!!!"));
}
下面是mainWindow.h内容
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QListWidgetItem>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
#include <QRunnable>
#include <QObject>
#include <QDebug>
#include <QFileInfo>
#include <QThread>
#include "mupdf/fitz.h"
#include <QMessageBox>
#include <QPainter>
#include <QPrinter>
class PdfPageToPngTask : public QThread
{
Q_OBJECT
public:
// 重写run函数,该函数内定义了具体要在线程中执行的任务逻辑
void run() override
{
while (true)
{
if(m_isStop)
{
break;
}
if(m_pdfPath.isEmpty())
{
QThread::msleep(10);
}
else
{
// MuPDF库的上下文结构体指针,用于管理整个文档处理的相关资源和状态
fz_context *context;
// MuPDF库的文档结构体指针,用于表示打开的PDF文档
fz_document *document;
// MuPDF库的矩阵结构体,用于定义页面的变换(如缩放、旋转等)
fz_matrix matrix;
// 用于存储页面缩放比例,初始化为100,表示100%缩放
float zoom = 100;
// 用于存储旋转角度,初始化为0
float rotate = 0;
// 创建一个新的MuPDF上下文,参数NULL表示使用默认的内存分配器等设置,FZ_STORE_UNLIMITED表示内存使用无限制(通常用于测试等场景)
context = fz_new_context(NULL, NULL, FZ_STORE_UNLIMITED);
if (!context)
{
qDebug() << QString::asprintf("cannot create mupdf context\n");
continue;
}
// 尝试在创建的上下文中注册文档处理相关的各种处理器(例如不同类型PDF的解析等相关处理)
fz_try(context)
fz_register_document_handlers(context);
// 如果在上述尝试过程中出现错误(例如内存不足等原因导致注册失败)
fz_catch(context)
{
qDebug() << QString::asprintf("cannot register document handlers: %s\n", fz_caught_message(context));
fz_drop_context(context);
continue;
}
// 尝试使用之前创建的上下文打开指定的PDF文档(通过pdfPath路径指定)
fz_try(context)
document = fz_open_document(context, m_pdfPath.toLocal8Bit().data());
// 如果打开文档过程中出现错误
fz_catch(context)
{
// 在标准错误输出打印错误信息,提示无法打开文档以及具体的错误消息
qDebug() << QString::asprintf("cannot open document: %s\n", fz_caught_message(context));
// 释放之前创建的上下文资源
fz_drop_context(context);
continue;
}
// 获取打开的PDF文档的总页数
int page_count = fz_count_pages(context, document);
// 如果获取总页数过程中出现错误
if (page_count < 0)
{
qDebug() << QString::asprintf("cannot count number of pages: %s\n", fz_caught_message(context));
fz_drop_document(context, document);
fz_drop_context(context);
continue;
}
// 根据指定的缩放比例创建一个缩放矩阵,将缩放比例转换为适合MuPDF库内部使用的格式(例如100%缩放在这里就是1.0的比例)
matrix = fz_scale(zoom / 100, zoom / 100);
// 根据指定的旋转角度对之前创建的矩阵进行旋转操作,得到最终用于页面渲染的变换矩阵
matrix = fz_pre_rotate(matrix, rotate);
for (int page_number = 0; page_number < page_count; ++page_number)
{
if(m_isStop)break;
fz_pixmap *pix;
// 尝试根据指定的页码、变换矩阵等参数从打开的PDF文档中渲染出对应页面的像素图(以RGB颜色模式,参数0可能表示一些默认的渲染选项等)
fz_try(context)
pix = fz_new_pixmap_from_page_number(context, document, page_number, matrix, fz_device_rgb(context), 0);
// 如果渲染页面过程中出现错误
fz_catch(context)
{
// 在标准错误输出打印错误信息,提示无法渲染页面以及具体的错误消息
fprintf(stderr, "cannot render page %d: %s\n", page_number, fz_caught_message(context));
continue;
}
QFileInfo fileInfo(m_pdfPath);
// 构建保存当前页面的PNG文件路径,这里简单地在原文件名基础上添加页码信息,可根据实际需求调整更合理的命名方式
QString saveFileName = QString("%1/%2_page_%3.png").arg(m_saveDir).arg(fileInfo.baseName()).arg(page_number + 1);
// 将渲染得到的像素图保存为PNG图片
fz_save_pixmap_as_png(context, pix, saveFileName.toLocal8Bit().data());
// 在主线程中更新UI,将生成的图片文件名添加到QListWidget中显示(通过信号槽机制)
emit fileSaved(saveFileName);
// 释放渲染得到的像素图资源
fz_drop_pixmap(context, pix);
}
// 释放打开的文档资源
fz_drop_document(context, document);
// 释放创建的上下文资源
fz_drop_context(context);
m_isStop=true;
emit sigFinished();
break;
}
}
}
signals:
// 自定义信号,用于通知主线程有文件保存成功,传递文件名参数
void fileSaved(QString fileName);
void sigFinished();
public:
QString m_pdfPath;
QString m_saveDir;
QMutex m_lock;
bool m_isStop=false;
};
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void onFileSaved(QString fileName);
void on_pushButtonLoad_clicked();
void on_pushButtonCreatPng_clicked();
void on_listWidget_itemSelectionChanged();
void on_pushButtonLoadPNG_clicked();
void on_pushButtonCreatPDF_clicked();
private:
Ui::MainWindow *ui;
QString m_pdfPath;
PdfPageToPngTask *m_pdfPageToPngTask;
};
#endif // MAINWINDOW_H
效果展示