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类接口
- 基础绘图操作

- 文本与图像处理

- 状态控制与高级特性

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 类的头文件:
// 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文件
#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
//创建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中使用QPdfWriter类结合QPainter类绘制并输出PDF文件
一.类的介绍 1.QPdfWriter介绍 Qt中提供了一个直接可以处理PDF的类,这就是QPdfWriter类。 (1)PDF文件生成 支持创建新的PDF文件或覆盖已有文件,通过构造函数直接绑定文件路径或QFile对象; 默认生成矢量图形PDF&#…...
使用 Promptic 进行对话管理需要具备python技术中的那些编程能力?
使用 Promptic 进行对话管理时,需要掌握一些基础的编程知识和技能,以下是详细说明: 1. Python 编程基础 Promptic 是一个基于 Python 的开发框架,因此需要具备一定的 Python 编程能力,包括: 函数定义与使用:了解如何定义函数、使用参数和返回值。类型注解:熟悉 Python…...
使用 DeepSeek 生成流程图、甘特图与思维导图:结合 Typora 和 XMind 的高效工作流
在现代工作与学习中,可视化工具如流程图、甘特图和思维导图能够极大地提升信息整理与表达的效率。本文将详细介绍如何使用 DeepSeek 生成 Mermaid 文本,结合 Typora 快速生成流程图和甘特图,并通过 Markdown 格式生成思维导图,最终…...
遗传算法初探
组成要素 编码 分为二进制编码、实数编码和顺序编码 初始种群的产生 分为随机方法、基于反向学习优化的种群产生。 基于反向学习优化的种群其思想是先随机生成一个种群P(N),然后按照反向学习方法生成新的种群OP(N),合并两个种群,得到一个新的种群S(N…...
Oracle 连接报错:“ORA-12541:TNS:no listener ”,服务组件中找不到监听服务
一、 报错: navicat连接数据库报错:ORA-12541:TNS:no listener 二、排查问题 三、 解决问题 删除Oracle安装目录下选中的配置:listener.ora 及 listener*.bak相关的 cmd,用管理员打开 执行:netca 命…...
一文详解U盘启动UEFI/Legacy方式以及GPT/MBR关系
对于装系统的老手而说一直想研究一下装系统的原理,以及面对一些问题时的解决思路,故对以前的方法进行原理上的解释,主要想理解其底层原理。 引导模式 MBR分区可以同时支持UEFI和Legacy引导,我们可以看一下微pe制作的启动盘&#…...
计算机毕设-基于springboot的汽车配件销售管理系统的设计与实现(附源码+lw+ppt+开题报告)
博主介绍:✌多个项目实战经验、多个大型网购商城开发经验、在某机构指导学员上千名、专注于本行业领域✌ 技术范围:Java实战项目、Python实战项目、微信小程序/安卓实战项目、爬虫大数据实战项目、Nodejs实战项目、PHP实战项目、.NET实战项目、Golang实战…...
嵌入式八股文(五)硬件电路篇
一、名词概念 1. 整流和逆变 (1)整流:整流是将交流电(AC)转变为直流电(DC)。常见的整流电路包括单向整流(二极管)、桥式整流等。 半波整流:只使用交流电的正…...
C语言番外篇(3)------------>break、continue
看到我的封面图的时候,部分读者可能认为这和编程有什么关系呢? 实际上这个三个人指的是本篇文章有三个部分组成。 在之前的博客中我们提及到了while循环和for循环,在这里面我们学习了它们的基本语法。今天我们要提及的是关于while循环和for…...
Mac下Python版本管理,适用于pyenv不起作用的情况
前言 声明:之前也在网上看到过可以使用pyenv来管理python版本,但由于作者的python安装路径实在是繁杂不堪,因此安装完成pyenv体验下来没有任何用处,但偶然发现vscode似乎可以看到各个python版本,因此写下这篇博客记录…...
网络安全知识--网络、网络安全产品及密码产品概述
🍅 点击文末小卡片 ,免费获取网络安全全套资料,资料在手,涨薪更快 网络结构 网络设备:交换机、路由器、负载均衡 安全设备: 通信网络安全类:通信安全、网络监测与控制 区域边界安全类:隔离类…...
WiFi相关功能使用教程(wpa_supplicant及wpa_cli)
WiFi相关功能使用教程(wpa_supplicant及wpa_cli) 在之前的博客文中,我们已经成功交叉编译了wpa_supplicant和wpa_cli相关文件。 此篇文章中我们将介绍如何使用和配置WiFi模块。 先将生成的可执行文件拷贝到设备里 采用TFTP的方式拷贝到设备中并全都加上可执行权限…...
CentOS7 离线安装 Postgresql 指南
一、背景 服务器通常都是离线内网环境,想要通过联网方式一键下载安装 Postgresql 不太现实,本文将介绍如何在 CentOS7 离线安装 Postgresql,以及遇到困难如何解决。 二、安装包下载 先在本地下载好 rpm 包,再通过 ftp 上传到服…...
详解 为什么 tcp 会出现 粘包 拆包 问题
TCP 会出现 粘包 和 拆包 问题,主要是因为 TCP 是 面向字节流 的协议,它不关心应用层发送的数据是否有边界,也不会自动分割或合并数据包。由于 TCP 的流控制和传输机制,数据可能在传输过程中被拆分成多个小的 TCP 包,或…...
C/C++后端开发面经
字节跳动 客户端开发 实习 一面(50min) 自我介绍是否愿意转语言,是否只愿意搞后端选一个项目来详细谈谈HTTP和HTTPS有什么区别?谈一下HTTPS加密的具体过程: 非对称加密 对称加密 证书认证的方式 非对称加密是为了保证对称密钥的安全性。 对称…...
HTML之JavaScript DOM编程获取元素的方式
HTML之JavaScript DOM编程获取元素的方式 1.获得document DOM树window.document(是window的属性)2.从document中获取要操作的元素1.直接获取var aaa document.getElementById("username") // 根据元素的id值获取页面上的唯一一个元素,有同名的则返回找到的第一个var…...
路由器的WAN口和LAN口有什么区别?
今时今日,移动终端盛行的时代,WIFI可以说是家家户户都有使用到的网络接入方式。那么路由器当然也就是家家户户都不可或缺的设备了。而路由器上的两个实现网络连接的基础接口 ——WAN 口和 LAN 口,到底有什么区别?它们的功能和作用…...
人工智能(AI):科技新纪元的领航者
摘要 人工智能(AI)作为当今科技领域最具变革性的力量之一,正以惊人的速度重塑着我们的世界。本文旨在全面且专业地介绍人工智能,涵盖其定义、发展历程、关键技术、应用领域、面临的挑战以及未来展望等方面,以期为读者…...
CSS通过webkit-scrollbar设置滚动条样式
查看::-webkit-scrollbar-*各项关系 以下图为例,可以分别定义滚动条背景、滚动轨道、滚动滑块的样式。 需要先给外部容器设置高度,再设置overflow: auto,最后设置三个webkit属性。 <!DOCTYPE html> <html lang"en">…...
Seata1.5.2学习(二)——使用分布式事务锁@GlobalLock
目录 一、创建数据库 二、配置consumer-service 1.pom.xml 2.application.properties 3.启动类 4.其他代码 三、配置provider-service 1.pom.xml 2.application.properties 3.启动类 4.其他代码 四、分布式事务问题演示与解决办法 (一)分布式事务问题演示 (二)…...
三级分类bug解决
文章目录 前端后端 前端 <!DOCTYPE html> <html xmlns:th"http://www.thymeleaf.org" lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0&q…...
华为 网络安全 认证
🍅 点击文末小卡片 ,免费获取网络安全全套资料,资料在手,涨薪更快 华为 网络安全 认证:保障信息安全的重要一环 在数字化时代的今天,网络安全成为了企业和个人都需要高度重视的问题。尤其是在企业信息化的…...
企业金融数字场景平台:架构设计、实践与未来趋势
随着数字化转型的深入,企业金融领域正经历着前所未有的变革。中国民生银行信息科技部赵鑫团队提出的《企业金融数字场景平台》架构设计,不仅展现了金融科技的前沿应用,也为行业提供了宝贵的实践经验和未来发展的新视角。 架构设计࿱…...
网络运维学习笔记 019 HCIA-Datacom综合实验03
文章目录 综合实验3实验需求一:A公司网络规划二:B公司网络规划 配置一、ip、vlan、vlanif,stp、eth-trunkSW1SW2SW3R1 二、ospfSW1R1 三、NATR1ISP 四、拒绝ping允许httpSW1 五、右半部分vlan、dhcp、ospf、NATSW4R2 综合实验3 实验需求 一&…...
Python--函数进阶(上)
1. 参数深入理解 1.1 参数传递的内存机制 Python中参数传递的是内存地址(引用传递),而非值拷贝。这意味着: 可变对象(列表、字典)在函数内修改会影响外部变量。不可变对象(数字、字符串&…...
Linux 常见面试题汇总
在当今数字化时代,Linux 作为一种开源、稳定且高效的操作系统,在服务器领域占据着举足轻重的地位。无论是运维工程师、开发人员还是系统管理员,掌握 Linux 相关知识都成为了必备技能。这篇博客将为大家汇总一些常见的 Linux 面试题࿰…...
网络运维学习笔记 015网工初级(HCIA-Datacom与CCNA-EI)NAT网络地址转换
文章目录 NAT(Network Address Translation,网络地址转换)思科:1)PAT2)静态端口转换 华为:1)EasyIP2)NAT Server静态NAT:动态NAT:实验1:在R1上配置NAPT让内网…...
蓝桥杯刷题25.2.22|打卡
一、幸运数 3491 谨记:使用函数,拆分成多个小问题,不容易出错 #include <iostream> using namespace std; //计算位数 int check(int a){int count0;while(a){aa/10;count;}return count; } bool fun(int sum){int countcheck(sum);int…...
学习笔记-沁恒第五讲-米醋
一,设置音量 上次 这次 #include "uart.h" #include "debug.h" void audio_init() { Usart3_Init(); } void audio_play(u8 num) { u8 string[]{0x7e,0x05,0x41,0x00,num,0x05^0x41^0x00^num,0xef}; u8 i; for(i0;i<7;i) { USART_Se…...
骁勇善战的量化利器:多因子模型【量化理论】
我叫补三补四,很高兴见到大家,欢迎一起学习交流和进步 今天来讲一讲alpha策略制定后的测试问题 风险模型雏形 股票因子受多种因素影响,其价格由多种因素决定,所谓的多因子策略就是要发掘诸如此类的因子,以一种合理的方…...
