1. Qt PDF生成技术演进从QPrinter到QPdfWriter在Qt4时代开发者使用QPrinter类来生成PDF文件这个设计初衷是为了打印而存在的类意外地成为了当时PDF生成的主要工具。我记得第一次用QPrinter生成PDF时感觉就像是用螺丝刀当锤子使 - 虽然能用但总感觉不太对劲。随着Qt5的发布Qt团队引入了专门用于PDF生成的QPdfWriter类。这个变化不仅仅是简单的类名替换而是整个设计理念的升级。QPdfWriter去掉了打印相关的冗余功能专注于PDF生成使得API更加简洁高效。在实际项目中我遇到过这样一个典型场景一个医疗报告系统需要将患者检查结果输出为PDF。在Qt4环境下我们不得不处理各种打印相关的参数比如纸张来源、双面打印等完全用不到的功能。迁移到Qt5后代码量减少了近30%性能提升了约40%这充分证明了专用工具的价值。2. 跨版本迁移实战指南2.1 模块配置差异从Qt4迁移到Qt5/6时第一个要注意的就是模块系统的变化。在Qt4中QPrinter是QtGui模块的一部分而在Qt5中所有打印相关类都被移到了Qt Print Support模块。我踩过的一个坑是在.pro文件中忘记添加printsupport模块结果编译时报了一堆莫名其妙的错误。正确的配置应该是QT printsupport # 必须添加这行在代码中包含头文件时也要注意变化// Qt4风格(已过时) #include QPrinter // Qt5/6正确方式 #include QtPrintSupport/QPrinter #include QtPrintSupport/QPdfWriter2.2 API使用对比让我们通过一个实际例子看看两种方式的区别。假设我们要生成一个简单的文本PDFQt4 QPrinter方式QPrinter printer(QPrinter::HighResolution); printer.setOutputFormat(QPrinter::PdfFormat); printer.setOutputFileName(output.pdf); QPainter painter(printer); painter.drawText(100, 100, Hello Qt4 PDF); painter.end();Qt5 QPdfWriter方式QPdfWriter writer(output.pdf); writer.setResolution(300); // 更直观的分辨率设置 QPainter painter(writer); painter.drawText(100, 100, Hello Qt5 PDF); painter.end();可以看到QPdfWriter的API更加直观。去掉了setOutputFormat这种冗余调用分辨率设置也变得更合理直接使用DPI数值而非枚举值。3. Qt6中的字体嵌入问题与解决方案3.1 字体问题的发现在Qt6中我遇到了一个棘手的问题生成的PDF在某些设备上显示字体异常。经过排查发现是字体嵌入出了问题。这个问题在Qt5.15中不存在但在Qt6.0中突然出现让我花了整整两天时间调试。典型的症状是代码中设置了中文字体如Microsoft YaHei生成的PDF在开发电脑上显示正常但在其他电脑上却变成了默认字体。这是因为字体没有正确嵌入PDF文件。3.2 解决方案经过多次试验我找到了几种可行的解决方案显式启用字体嵌入QPdfWriter writer(output.pdf); writer.setPdfVersion(QPagedPaintDevice::PdfVersion_1_6); // 使用较高版本 QPainter painter(writer); // 关键设置 QFont font(Microsoft YaHei); font.setStyleStrategy(QFont::PreferMatch); // 重要 painter.setFont(font);使用字体子集化推荐// 在应用程序初始化时设置 QGuiApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);回退到Qt5兼容模式 如果你暂时无法解决Qt6的字体问题可以考虑使用Qt5兼容模块QT pdf qt5compat # 在.pro文件中添加4. 高级功能与性能优化4.1 多页文档处理生成多页PDF时正确的分页处理很重要。以下是经过实战检验的代码结构QPdfWriter writer(report.pdf); writer.setPageSize(QPageSize::A4); QPainter painter(writer); // 第一页内容 painter.drawText(/*...*/); // 添加图表等... // 换页 if (!writer.newPage()) { qWarning() Failed to create new page; return; } // 第二页内容 painter.drawText(/*...*/); painter.end();4.2 图像和表格处理对于复杂的PDF内容如图像和表格我有几个实用技巧图像处理QPixmap image(chart.png); image image.scaled(800, 600, Qt::KeepAspectRatio); QPainter painter(writer); QRect imageRect(100, 100, 800, 600); painter.drawPixmap(imageRect, image);表格生成// 使用HTML方式生成表格更简单 QTextDocument doc; doc.setHtml(table border1trtd项目/tdtd数值/td/tr/table); doc.drawContents(painter);4.3 性能优化技巧在大批量生成PDF时性能很重要。我总结了几点经验复用QPainter对象避免频繁创建销毁对于静态内容预渲染为QPixmap使用QTextDocument处理复杂文本布局设置合适的分辨率通常300DPI足够// 性能优化示例 QPdfWriter writer(bulk_report.pdf); writer.setResolution(300); // 300DPI是性价比最佳选择 QPainter painter; painter.begin(writer); // 批量处理 for (int i 0; i 100; i) { renderPage(painter, i); // 集中渲染逻辑 if (i 99) writer.newPage(); } painter.end();5. 跨版本兼容性实践5.1 条件编译策略在实际项目中我们经常需要维护跨Qt版本的代码。我的做法是使用条件编译#if QT_VERSION QT_VERSION_CHECK(5, 0, 0) // Qt4代码路径 QPrinter printer(QPrinter::HighResolution); printer.setOutputFormat(QPrinter::PdfFormat); #else // Qt5/6代码路径 QPdfWriter writer(output.pdf); writer.setResolution(300); #endif5.2 抽象层设计对于大型项目建议设计一个PDF生成抽象层class PdfGenerator { public: virtual void addText(const QString text) 0; virtual void addImage(const QImage image) 0; virtual bool save(const QString filename) 0; }; // Qt4实现 class Qt4PdfGenerator : public PdfGenerator { /*...*/ }; // Qt5/6实现 class Qt5PdfGenerator : public PdfGenerator { /*...*/ };这种设计使得核心业务代码不依赖具体Qt版本迁移时只需替换实现类。5.3 常见陷阱规避根据我的经验这些坑一定要避开坐标系统混淆QPdfWriter使用逻辑坐标与屏幕像素不同资源泄漏确保QPainter正确end()字体回退总是检查字体是否可用PDF版本兼容性设置合适的PDF版本线程安全问题PDF生成最好在主线程进行一个健壮的PDF生成函数应该包含这些安全检查bool generatePdf(const QString filename) { QPdfWriter writer(filename); if (!writer.device()-open(QIODevice::WriteOnly)) { qWarning() Failed to open file; return false; } QPainter painter; if (!painter.begin(writer)) { qWarning() Failed to initialize painter; return false; } // 实际绘制内容... painter.end(); return true; }6. 实战案例报表系统迁移去年我将一个金融报表系统从Qt4迁移到Qt6整个过程充满挑战也收获颇丰。这个系统需要生成包含复杂表格、图表和文字的PDF报告每天处理量在5000份左右。迁移步骤首先用抽象层隔离PDF生成代码实现Qt6版本的生成器类逐步替换旧实现同时保持旧代码作为回退性能调优特别是字体处理和图像压缩全面测试不同平台上的显示一致性性能对比指标Qt4(QPrinter)Qt6(QPdfWriter)生成速度120份/秒210份/秒文件大小平均450KB平均380KBCPU占用35%22%内存使用120MB85MB迁移后不仅性能提升代码也更简洁。最重要的是解决了长期存在的字体兼容性问题。