关于QT Widget 其它文章请点击这里: QT Widget
GitHub 源码: QWidgetLearningPro (暂未更新)
姊妹篇: Qt WORD/PDF(一)使用 QtPdfium库实现 PDF 预览
一、简介
QtPdfium 是基于Pdfium库的一个Qt绑定。Pdfium是一个由Google开发并开源的PDF渲染引擎,它被广泛应用于Chrome浏览器和其他一些Google产品中。QtPdfium则是一个专门将Pdfium引入Qt项目的封装,使得Qt应用程序可以使用Pdfium的PDF渲染功能。
它的主要目标是高效的PDF文档渲染,特别是在Web浏览器等高性能需求的应用场景中。相较于Poppler,Pdfium侧重于简洁、高效的渲染和性能优化。
二、演示
三、代码部分
完整代码
mainwindow.cpp:
cpp
#include "mainwindow.h"
// #include "ui_mainwindow.h"
#include <QVBoxLayout>
#include <QFileDialog>
#include <QPrinter>
#include <QPrintDialog>
#include <QPainter>
#include <QPrinterInfo>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
// , ui(new Ui::MainWindow)
{
// 设置窗口标题
setWindowTitle("PDF Viewer & Printer");
this->initWidget();
// 主控件和布局
QWidget *centralWidget = new QWidget(this);
QVBoxLayout *layout = new QVBoxLayout(centralWidget);
// PDF 预览区域
m_pdfPreview = new QLabel("PDF 预览区域", this);
m_pdfPreview->setFixedSize(700, 880);
m_pdfPreview->setStyleSheet("border: 1px solid gray; background-color: #f0f0f0;");
m_pdfPreview->setAlignment(Qt::AlignCenter);
layout->addWidget(m_pdfPreview);
//增加一行页码相关,页码控制区域
//上一页 当前页/总页码 下一页
QHBoxLayout *pageControlLayout = new QHBoxLayout();
_prevPageButton = new QPushButton("上一页", this);
_pageInfoLabel = new QLabel("0 / 0", this);
_nextPageButton = new QPushButton("下一页", this);
_pageInfoLabel->setAlignment(Qt::AlignCenter);
// 将控件添加到水平布局中
pageControlLayout->addWidget(_prevPageButton);
pageControlLayout->addWidget(_pageInfoLabel);
pageControlLayout->addWidget(_nextPageButton);
layout->addLayout(pageControlLayout);
// 打印设置区域
QHBoxLayout *printerLayout = new QHBoxLayout();
m_printerList = new QComboBox(this);
m_currentPage = new QRadioButton("当前页", this);
m_allPages = new QRadioButton("所有页", this);
m_allPages->setChecked(true); // 默认打印所有页
m_rangeGroup = new QButtonGroup(this);
m_rangeGroup->addButton(m_currentPage);
m_rangeGroup->addButton(m_allPages);
printerLayout->addWidget(new QLabel("打印机:", this));
printerLayout->addWidget(m_printerList);
printerLayout->addWidget(m_currentPage);
printerLayout->addWidget(m_allPages);
layout->addLayout(printerLayout);
// 按钮区域
QHBoxLayout *buttonLayout = new QHBoxLayout();
m_loadButton = new QPushButton("加载PDF", this);
m_printButton = new QPushButton("打印PDF", this);
buttonLayout->addWidget(m_loadButton);
buttonLayout->addWidget(m_printButton);
layout->addLayout(buttonLayout);
setCentralWidget(centralWidget);
// 填充打印机列表
populatePrinters();
// 信号与槽连接
connect(_prevPageButton, &QPushButton::clicked, this, &MainWindow::goToPreviousPage);
connect(_nextPageButton, &QPushButton::clicked, this, &MainWindow::goToNextPage);
connect(m_loadButton, &QPushButton::clicked, this, &MainWindow::loadPdf);
connect(m_printButton, &QPushButton::clicked, this, &MainWindow::printPdf);
}
MainWindow::~MainWindow()
{
m_pdfium = nullptr;
delete m_pdfium;
}
//初始化界面
void MainWindow::initWidget()
{
//初始化变量
_currentPage = 0; // 当前页码
_totalPages = 0; // 总页数
}
void MainWindow::populatePrinters()
{
// 获取系统打印机列表
QList<QPrinterInfo> printers = QPrinterInfo::availablePrinters();
for (const QPrinterInfo &printer : printers) {
m_printerList->addItem(printer.printerName());
}
}
//鼠标滚轮事件,放大缩小
void MainWindow::wheelEvent(QWheelEvent *event)
{
if(!isPdfValid) {
return;
}
//根据鼠标滚轮的方向来缩放图像
const QPoint numDegrees = event->angleDelta() / 8;
const int numSteps = numDegrees.y() / 15;
if(numSteps != 0)
{
//更新缩放比例
if(numSteps > 0) {
//放大
zoomScale *= 1.1;
}
else {
//缩小,但要确保缩放比例不会小于某个最小值(比如0.1)
zoomScale /= 1.1;
zoomScale = qMax(zoomScale, 0.3);
}
//更新缩放后的图像并显示
updateScaledPixmap();
}
}
//更新缩放图像
void MainWindow::updateScaledPixmap()
{
//计算缩放后的尺寸
QSize scaledSize = _showPixmap.size() * zoomScale;
//缩放图像
QPixmap scaledPixmap = _showPixmap.scaled(scaledSize,Qt::IgnoreAspectRatio,Qt::SmoothTransformation);
//更新QLabel的Pixmap
m_pdfPreview->setPixmap(scaledPixmap);
}
void MainWindow::loadPdf()
{
// 打开文件对话框加载 PDF
QString fileName = QFileDialog::getOpenFileName(this, "选择PDF文件", "D:/23_Outproject/32_ZNPicture/Src/QtPdfiumTest2", "PDF Files (*.pdf)");
if (fileName.isEmpty()) {
isPdfValid = false;
return;
}
m_pdfium = new QPdfium();
// 加载 PDF 文件
m_pdfium->loadFile(fileName);
isPdfValid = m_pdfium->isValid();
if (!isPdfValid) {
m_pdfPreview->setText("加载PDF失败!");
return;
}
_totalPages = m_pdfium->pageCount(); // 更新总页数
_currentPage = 0; // 当前页重置为第一页
_pageInfoLabel->setText(QString("%1 / %2").arg(_currentPage+1).arg(_totalPages));
//预览第一页
QPdfiumPage page = m_pdfium->page(_currentPage);
if (page.isValid()) {
QImage image = page.image(1.0); // 渲染为 QImage
_showPixmap = QPixmap::fromImage(image).scaled (
m_pdfPreview->size(),
Qt::KeepAspectRatio,
Qt::SmoothTransformation);
m_pdfPreview->setPixmap(_showPixmap);
}
}
//上一页
void MainWindow::goToPreviousPage()
{
if (_currentPage > 0) {
_currentPage--;
_pageInfoLabel->setText(QString("%1 / %2").arg(_currentPage+1).arg(_totalPages));
QPdfiumPage page = m_pdfium->page(_currentPage);
if (page.isValid()) {
QImage image = page.image(1.0);
_showPixmap = QPixmap::fromImage(image).scaled (
m_pdfPreview->size(),
Qt::KeepAspectRatio,
Qt::SmoothTransformation);
m_pdfPreview->setPixmap(_showPixmap);
}
}
}
//下一页
void MainWindow::goToNextPage()
{
if (_currentPage < _totalPages-1) {
_currentPage++;
_pageInfoLabel->setText(QString("%1 / %2").arg(_currentPage+1).arg(_totalPages));
QPdfiumPage page = m_pdfium->page(_currentPage);
if (page.isValid()) {
QImage image = page.image(1.0);
_showPixmap = QPixmap::fromImage(image).scaled (
m_pdfPreview->size(),
Qt::KeepAspectRatio,
Qt::SmoothTransformation);
m_pdfPreview->setPixmap(_showPixmap);
}
}
}
void MainWindow::printPdf()
{
// 调用打印功能
if (!m_pdfium || !m_pdfium->isValid() || m_pdfium->pageCount() == 0) {
m_pdfPreview->setText("未加载有效PDF,无法打印!");
return;
}
// 创建打印机对象
QPrinter printer(QPrinter::HighResolution);
printer.setPrinterName(m_printerList->currentText());
//是用来设置打印页面的方向,指定打印内容的布局是 纵向 (Portrait) 还是 横向 (Landscape)。
printer.setOrientation(QPrinter::Portrait);
printer.setPageSize(QPrinter::A4);
// 设置打印范围
int fromPage = 1;
int toPage = m_pdfium->pageCount();
if (m_currentPage->isChecked()) {
fromPage = toPage = 1; // 当前页为第一页
}
// 打印 PDF
QPainter painter(&printer);
for (int i = fromPage; i <= toPage; ++i) {
QPdfiumPage page = m_pdfium->page(i);
if (!page.isValid()) continue;
QImage image = page.image(1.0);
QRect targetRect = printer.pageRect();
QPixmap pixmap = QPixmap::fromImage(image).scaled (
targetRect.size(),
Qt::KeepAspectRatio,
Qt::SmoothTransformation);
painter.drawPixmap(0, 0, pixmap);
if (i < toPage)
printer.newPage();
}
painter.end();
}
mainwindow.h:
cpp
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QFileDialog>
#include <QDesktopServices>
#include <QTime>
#include <QImage>
#include <QPixmap>
#include <QWheelEvent>
#include <QDebug>
#include "qpdfium.h"
#include <QPrinter>
#include <QPainter>
#include <QPrintDialog>
#include <QFileDialog>
//Item
#include <QWheelEvent>
#include <QLabel>
#include <QPushButton>
#include <QComboBox>
#include <QRadioButton>
#include <QPrinter>
#include <QButtonGroup>
QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
void initWidget(); // 初始化参数
void showPdfData(int curPage);
void updateScaledPixmap();
protected:
void wheelEvent(QWheelEvent *event) override;
private slots:
void goToPreviousPage(); // 上一页槽函数
void goToNextPage(); // 下一页槽函数
void loadPdf();
void printPdf();
private:
Ui::MainWindow *ui;
int m_curPage; //当前页
int m_totalPage; //总页数
double zoomScale; //缩放比例
bool isPdfValid = false; //打开pdf是否有效
QString m_saveImagePath; //图像保存路径
QPixmap _showPixmap; //界面显示图像
//Item1
QPushButton *_prevPageButton; // 上一页按钮
QPushButton *_nextPageButton; // 下一页按钮
QLabel *_pageInfoLabel; // 当前页/总页码显示
//Item2
QPdfium *m_pdfium = nullptr; //QPdfium对象
QLabel *m_pdfPreview; // 用于显示PDF预览
QPushButton *m_loadButton; // 加载PDF按钮
QPushButton *m_printButton; // 打印按钮
//基本参数
int _currentPage; // 当前页码
int _totalPages; // 总页数
QComboBox *m_printerList; // 打印设备列表
QRadioButton *m_currentPage; // 打印范围:当前页
QRadioButton *m_allPages; // 打印范围:所有页
QButtonGroup *m_rangeGroup; // 打印范围按钮组
void populatePrinters(); // 获取系统打印机
};
#endif // MAINWINDOW_H
pro 中需要增加对打印的支持
bash
QT += core gui printsupport
简要分析
1. 初始化界面 ( MainWindow::MainWindow
)
- 创建并配置了主界面控件,包括 PDF 预览区域、页码控制、打印设置、按钮区域。
- 使用
QVBoxLayout
和QHBoxLayout
布局管理器来组织控件。 QLabel
用于显示 PDF 预览,QPushButton
控件用于加载 PDF 文件和打印 PDF。QComboBox
和QRadioButton
用于选择打印机和打印范围(当前页或所有页)。- 设置了信号与槽连接:控制按钮的点击事件(加载 PDF、打印 PDF、翻页等)。
2. 控件的布局和显示
- PDF 预览区域使用
QLabel
来显示 PDF 页面图像,用户可以通过上下页按钮查看不同页。 - 页码信息通过
QLabel
显示(例如 "1 / 10" 表示当前第 1 页,总共有 10 页)。 - 打印区域提供了选择打印机和打印选项(当前页或所有页)。
- 通过
QPushButton
控件加载和打印 PDF 文件。
3. 加载 PDF 文件 ( MainWindow::loadPdf
)
- 使用
QFileDialog::getOpenFileName
打开文件对话框,让用户选择 PDF 文件。 - 通过
QPdfium
类(可能是一个自定义类)加载 PDF 文件。若加载成功,更新总页数并显示第一页。 QPdfiumPage
用于获取指定页的图像,将其转换为QPixmap
后显示在QLabel
中。
4. 翻页功能
goToPreviousPage
和goToNextPage
用于上一页和下一页功能,页面切换时更新页码并渲染该页图像。
5. 打印功能 ( MainWindow::printPdf
)
- 使用
QPrinter
设置打印机、页面大小、打印方向等。 QPainter
用于将 PDF 页面的图像绘制到打印机上。- 如果选择打印当前页,则仅打印当前页;如果选择打印所有页,则打印整个 PDF 文件的所有页面。
- 在每一页打印完后,调用
printer.newPage()
使打印机跳转到下一页。
6. 鼠标滚轮放大缩小
wheelEvent
捕获鼠标滚轮事件,根据滚动方向来缩放 PDF 页面的显示。缩放比例由zoomScale
控制,放大时乘以 1.1,缩小时除以 1.1。 调整后更新显示;
7. 打印机列表填充
populatePrinters
方法列出系统中的打印机,将其添加到QComboBox
控件中
注意:
- 使用 QtPdfium 偶尔无法读中文路径的PDF,不知道是不是库本身的问题;
- PDF 渲染效率:每次翻页时都重新加载页面并进行图像渲染,若 PDF 页数较多或者页面较复杂时可能会影响性能。可以考虑缓存已经渲染过的页面,减少重复渲染
参考:
商务合作请加我: 19976699725
关于QT Widget 其它文章请点击这里: QT Widget