一.類的介紹
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類本身帶有的各種圖形繪制功能,封裝你想做的繪制接口,實際項目應用中,就以你封裝的接口進行各種布局繪制操作,來完成項目要求。