Qt WORD/PDF(二)使用 QtPdfium库实现 PDF操作、打印等


关于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 预览区域、页码控制、打印设置、按钮区域。
  • 使用 QVBoxLayoutQHBoxLayout 布局管理器来组织控件。
  • QLabel 用于显示 PDF 预览,QPushButton 控件用于加载 PDF 文件和打印 PDF。
  • QComboBoxQRadioButton 用于选择打印机和打印范围(当前页或所有页)。
  • 设置了信号与槽连接:控制按钮的点击事件(加载 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. 翻页功能

  • goToPreviousPagegoToNextPage 用于上一页和下一页功能,页面切换时更新页码并渲染该页图像。

5. 打印功能 (MainWindow::printPdf)

  • 使用 QPrinter 设置打印机、页面大小、打印方向等。
  • QPainter 用于将 PDF 页面的图像绘制到打印机上。
  • 如果选择打印当前页,则仅打印当前页;如果选择打印所有页,则打印整个 PDF 文件的所有页面。
  • 在每一页打印完后,调用 printer.newPage() 使打印机跳转到下一页。

6. 鼠标滚轮放大缩小

  • wheelEvent 捕获鼠标滚轮事件,根据滚动方向来缩放 PDF 页面的显示。缩放比例由 zoomScale 控制,放大时乘以 1.1,缩小时除以 1.1。 调整后更新显示;

7. 打印机列表填充

  • populatePrinters 方法列出系统中的打印机,将其添加到 QComboBox 控件中

注意:

  1. 使用 QtPdfium 偶尔无法读中文路径的PDF,不知道是不是库本身的问题;
  2. PDF 渲染效率:每次翻页时都重新加载页面并进行图像渲染,若 PDF 页数较多或者页面较复杂时可能会影响性能。可以考虑缓存已经渲染过的页面,减少重复渲染

参考:

Qt下使用QtPdfium处理PDF文档


商务合作请加我: 19976699725

关于QT Widget 其它文章请点击这里: QT Widget

相关推荐
coder_Alaric2 小时前
[笔记]Qt下使用SendMessage、PostMessage和接收window消息
笔记·qt
鲁班相信爱情2 小时前
QT中静态变量无法翻译的问题
开发语言·qt
ymchuangke3 小时前
Pyside6 --Qt设计师--简单了解各个控件的作用之:Item Views
python·qt·ui·pyside6
淼淼7633 小时前
qt中tr的使用
开发语言·qt
雅典没有娜4 小时前
QT/C++与LUA交互过程中,利用ZeroBraneStudio对LUA脚本进行仿真调试
c++·qt·lua·调试·仿真·zerobranestudio
小灰灰搞电子4 小时前
QT使用HTTP上传json格式数据源码分享
qt·http·json
weixin_399264294 小时前
QT c++ 测控系统 一套报警规则(上)
c++·qt
Drone_xjw6 小时前
window下的qt5.14.2配置vs2022
开发语言·qt
Prejudices6 小时前
关于QMessageBox的一些使用总结和避坑指南
qt