Qt中使用QPdfWriter类结合QPainter类绘制并输出PDF文件

一.类的介绍

1.QPdfWriter介绍

Qt中提供了一个直接可以处理PDF的类,这就是QPdfWriter类。
(1)PDF文件生成

支持创建新的PDF文件或覆盖已有文件,通过构造函数直接绑定文件路径或QFile对象;

默认生成矢量图形PDF,支持高分辨率输出(可设置DPI);

2)页面属性配置

页面方向:通过setPageOrientation(QPageLayout::Orientation)设置纵向(Portrait)或横向(Landscape);

页面尺寸:使用setPageSize(QPageSize::A4)定义纸张大小,支持ISO标准尺寸(如A4、A3);

页边距:通过setPageMargins()调整内容区域与页边的距离;
(3)内容绘制

与QPainter深度集成,支持所有标准绘图操作:

图形:线段、矩形、椭圆、多边形等;

文本:多字体样式、对齐方式、旋转文字;

图像:支持PNG、JPG、SVG等格式的嵌入;
(4)多页面管理

通过QPainter::begin()和QPainter::end()控制绘制流程;

使用QPrinter::newPage()或手动分页逻辑实现多页文档;

2.QPainter介绍

(1)QPainter类功能

QPainter是Qt框架中用于2D图形绘制的核心类,提供高度优化的绘图功能,支持在QWidget、QImage、QPixmap、QPrinter等设备上进行绘制。其主要特性包括:

  • 支持矢量图形(直线/曲线/几何图形)和位图操作;
  • 提供坐标变换、复合模式、抗锯齿等高级特性;
  • 集成字体渲染、图像合成等专业级功能;
  • 必须通过paintEvent()事件或在继承自QPaintDevice的类中使用;
    (2)QPainter类接口
  1. 基础绘图操作
  2. 文本与图像处理
  3. 状态控制与高级特性

    QPainter类还有很多接口函数,尤其是跟绘制有关的,很多重载的接口方便不同情况的使用,具体可以参考官网的介绍QPainter类

二.开发生成PDF文件

下面开始用上文中的两个类来封装一个专门用来绘制PDF文件的类。

1.使用前要注意:

  • 坐标系系统:PDF坐标系原点在页面左上角,Y轴向下延伸,X轴向右延伸;
  • 单位换算:使用QPageLayout::Millimeter设置毫米单位,绘制时默认使用像素单位;
  • 图像缩放:推荐使用QRect参数控制图片显示尺寸,避免直接缩放;
  • 字体嵌入:中文字体需通过QFontDatabase加载系统字体;
  • 多页处理:通过QPdfWriter的newPage()创建新页面;

2.绘制流程梳理

  • 想要操作PDF文件,首先得有个文件,使用QFile的对象指向文件,然后创建QPdfWriter类的对象,并将QPdfWriter绑定在该文件上,然后用QPdfWriter对象设定PDF的一些参数,比如DPI,绘制页面大小等。
  • 其次,想要绘制得打开文件,调用QFile对象打开绑定的pdf格式的文件;
  • 再次,创建QPainter类对象,用该对象的各个规制接口来绘制各种图形文字等,如果你要设计绘制的接口很多的话,这里其实是最耗时的;
  • 最后,正确的释放资源,关闭文件;

3.代码说明

现在实操

  • QtCreator上创建一个简单的应用程序项目,先编译下,确保原始项目没问题;
  • 在程序界面上添加一个按钮,命名"btCreatePdf",连接好按钮对应的点击信号槽;
  • 添加新的C++类,继承自QObject,类命名"PdfGenerator",这就是我们准备开发的一个专门操作PDF的自定义类,也就是我们所有对PDF的操作都在这个类里边完成;
  • "PdfGenerator"类的设计开发,包含QPdfWriter,QPainter,QFont,QImage,QPageSize, QFile等类;创建QPdfWriter类的对象,QPainter类的对象,QFile类的对象;调用这些对象的接口实现PDF的绘制。
  • 最后,在主程序中包含上面自定义类,在界面按钮"btCreatePdf"中调用其实现PDF绘制。
    不多说,直接上代码:

PdfGenerator 类的头文件:

cpp 复制代码
// pdfgenerator.h 
#include <QObject>
#include <QPdfWriter>
#include <QPainter>
#include <QFont>
#include <QImage>
#include <QPageSize>
#include <QFile>

class PdfGenerator : public QObject {
    Q_OBJECT
public:
    explicit PdfGenerator(const QString &fileName, QPagedPaintDevice::PageSize size = QPagedPaintDevice::PageSize::A4);
    ~PdfGenerator();
 
    // 基础设置 
    void setMargins(qreal left, qreal top, qreal right, qreal bottom);
    void setResolution(int dpi);
    void newPage();
	bool beginPage();
	bool endPage();
 
// 绘制接口
    //绘制线段
    void drawLine(const QPointF &start, const QPointF &end, const QColor &color, qreal width);
    //绘制文字
    void drawText(const QRectF &rect, const QString &text, const QFont &font, const QColor &color, Qt::Alignment align);
    //绘制图片
    void drawImage(const QRectF &rect, const QString &imagePath, bool keepAspectRatio);
    //绘制矩形
    void drawRect(const QRectF &rect, const QColor &fillColor, const QColor &borderColor, qreal borderWidth);
    //绘制椭圆
    void drawEllipse(const QRectF &rect, const QColor &fillColor, const QColor &borderColor, qreal borderWidth);
   //想设计其他绘制接口继续往下加	
  
private:
    QPdfWriter *m_writer = nullptr;
    QPainter *m_painter = nullptr;
    QRect m_pageRect;
    QFile m_pdfFile;
};

PdfGenerator 类的cpp文件

cpp 复制代码
#include "PdfGenerator.h"
#include <QtDebug>
// pdfgenerator.cpp  
PdfGenerator::PdfGenerator(const QString &fileName, QPagedPaintDevice::PageSize size)
{
    m_pdfFile.setFileName(fileName);
    m_writer = new QPdfWriter(&m_pdfFile);
    m_writer->setPageSize(size);
    m_writer->setResolution(300);
    m_writer->setPageMargins(QMarginsF(20, 20, 20, 20), QPageLayout::Millimeter);
    m_pageRect = m_writer->pageLayout().paintRectPixels(m_writer->resolution());
    // 计算可绘制区域 
    m_pageRect = QRect(0, 0, m_writer->width(), m_writer->height());
    if(!m_pdfFile.open(QIODevice::WriteOnly))
        return ;

}
 
PdfGenerator::~PdfGenerator()
{
    if (m_painter->isActive())
    {
        m_painter->end();
    }
    delete m_painter;
    delete m_writer;
}
 
void PdfGenerator::setMargins(qreal left, qreal top, qreal right, qreal bottom)
{
    m_writer->setPageMargins(QMarginsF(left, top, right, bottom), QPageLayout::Millimeter);
    m_pageRect = m_writer->pageLayout().paintRectPixels(m_writer->resolution()); // 更新绘制区域[5]()
}
 
void PdfGenerator::newPage()
{
     // 创建新页
    m_writer->newPage();
}

 bool PdfGenerator::beginPage()
 {
     bool bRet = false;
     if(nullptr == m_painter)
     {
        m_painter = new QPainter(m_writer);
     }
     //启用抗锯齿
     m_painter->setRenderHint(QPainter::Antialiasing);
    if (nullptr != m_painter)
    {
        m_painter->begin(m_writer);
        //m_painter->reset(new  QPainter(m_writer.data()));
        bRet = m_painter->isActive();
    }
    qDebug() << "beginPage bRet is " << bRet;
    return bRet;
}
 
bool PdfGenerator::endPage() {

    if (m_painter && m_painter->isActive())
    {
        m_painter->end();
        m_writer->deleteLater();
        m_pdfFile.close();
        return true;
    }
    m_pdfFile.close();
    return false;
}

// 绘制线段 
void PdfGenerator::drawLine(const QPointF &start, const QPointF &end, const QColor &color, qreal width)
{
    if (!m_painter->isActive())
		return;
 
    m_painter->save();
    m_painter->setPen(QPen(color, width));
    m_painter->drawLine(start, end);
    m_painter->restore();
}
 
// 绘制文本(支持对齐)
void PdfGenerator::drawText(const QRectF &rect, const QString &text, const QFont &font, const QColor &color, Qt::Alignment align)
{
    if (!m_painter->isActive()) 
    	return;
 
    m_painter->save();
    m_painter->setFont(font);
    m_painter->setPen(color);
    m_painter->drawText(rect, static_cast<int>(align), text);
    m_painter->restore();
}
 
// 绘制图片(自动缩放)
void PdfGenerator::drawImage(const QRectF &rect, const QString &imagePath, bool keepAspectRatio)
{
    if (!m_painter->isActive())
    	return;
 
    QPixmap pixmap(imagePath);
    if (pixmap.isNull())  return;
 
    QRectF targetRect = rect;
    if (keepAspectRatio) {
        QSizeF scaled = pixmap.size().scaled(rect.size().toSize(),  Qt::KeepAspectRatio);
        targetRect.setSize(scaled); 
    }
 
    m_painter->drawPixmap(targetRect, pixmap, pixmap.rect()); 
}
 
// 绘制矩形(支持填充)
void PdfGenerator::drawRect(const QRectF &rect, const QColor &fillColor, const QColor &borderColor, qreal borderWidth)
{
	if (!m_painter->isActive())
		return;
 
    m_painter->save();
    m_painter->setBrush(QBrush(fillColor));
    m_painter->setPen(QPen(borderColor, borderWidth));
    m_painter->drawRect(rect);
    m_painter->restore();
}
 
// 绘制椭圆 
void PdfGenerator::drawEllipse(const QRectF &rect, const QColor &fillColor, const QColor &borderColor, qreal borderWidth)
{
    if (!m_painter->isActive()) 
    	return;
    m_painter->save();
    m_painter->setBrush(QBrush(fillColor));
    m_painter->setPen(QPen(borderColor, borderWidth));
    m_painter->drawEllipse(rect);
    m_painter->restore();
}

主程序按钮调用PdfGenerator类绘制PDF

cpp 复制代码
//创建Pdf
void MainWindow::on_btCreatePdf_clicked()
{
    qDebug() << "into on_btCreatePdf_clicked";
    PdfGenerator doc("E:/test/output.pdf");
    if (doc.beginPage())
    {
        qDebug() << "beginPage success";
        // 绘制灰色线段
        doc.drawLine(QPointF(20,  60), QPointF(150, 200), Qt::lightGray, 2.0);

        // 添加图片(保持比例),例子的资源里没有添加这张图片,所以下面PDF里没有绘制出来图片
        doc.drawImage(QRectF(100,  100, 100, 100), "logo.png", false);

        // 绘制蓝色文字
        QFont font("Arial", 12, QFont::Bold);
        doc.drawText(QRectF(70,  270, 270, 50), "Hello PDF!", font, Qt::blue, Qt::AlignVCenter | Qt::AlignHCenter);

        // 绘制绿色填充矩形
        doc.drawRect(QRectF(300,  150, 100, 40), Qt::green, Qt::black, 1.5);

        // 绘制黄色边框椭圆
        doc.drawEllipse(QRectF(250,  200, 100, 80), Qt::transparent, Qt::yellow, 2.0);

        doc.endPage();
    }

}

执行后,展示结果:

以此自定义PdfGenerator类作为基础,后续可以根据QPainter类本身带有的各种图形绘制功能,封装你想做的绘制接口,实际项目应用中,就以你封装的接口进行各种布局绘制操作,来完成项目要求。

相关推荐
软件开发技术局几秒前
撕碎QT面具(8):对控件采用自动增加函数(转到槽)的方式,发现函数不能被调用的解决方案
开发语言·qt
余衫马2 分钟前
CentOS7 离线安装 Postgresql 指南
数据库·postgresql
d3soft33 分钟前
deepseek清华大学第二版 如何获取 DeepSeek如何赋能职场应用 PDF文档 电子档(附下载)
ai·pdf·教程·deepseek·赋能职场
E___V___E35 分钟前
MySQL数据库入门到大蛇尚硅谷宋红康老师笔记 高级篇 part 2
数据库·笔记·mysql
hello_simon36 分钟前
【Word转PDF】在线Doc/Docx转换为PDF格式 免费在线转换 功能强大好用
职场和发展·pdf·word·学习方法·word转pdf·石墨文档·word转换
m0_748254881 小时前
mysql之如何获知版本
数据库·mysql
OopspoO1 小时前
QT事件循环
qt·事件循环
mikey棒棒棒2 小时前
Redis——优惠券秒杀问题(分布式id、一人多单超卖、乐悲锁、CAS、分布式锁、Redisson)
数据库·redis·lua·redisson·watchdog·cas·并发锁
MessiGo2 小时前
OpenGL(2)基于Qt做OpenGL开发
qt
水手胡巴3 小时前
oracle apex post接口
数据库·oracle