一.类的介绍
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 类的头文件:
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类本身带有的各种图形绘制功能,封装你想做的绘制接口,实际项目应用中,就以你封装的接口进行各种布局绘制操作,来完成项目要求。