当前位置: 首页 > news >正文

QT打印 文本 + png公章

之前看到这个链接很有意思:

https://blog.csdn.net/wastelandboy/article/details/124535680

虽然很美,但是我想直接一个pdf 上 + png 打印就好了(因为这段代码免费😀,上面那个控件资源目前没有获取到)。

代码如下:

void CaiGou::drawContractContent(QPainter *painter, const QJsonObject &payload) { if (!painter || !painter->isActive()) return; painter->setRenderHint(QPainter::Antialiasing); // 🚩 1. 物理参数:获取打印机当前像素宽高 int pw = m_printer->width(); int ph = m_printer->height(); // 🚩 2. 比例适配系数(基准宽度 800) double factor = pw / 800.0; auto s = [factor](int val) -> int { return static_cast<int>(val * factor); }; // --- 字体配置 --- QString fontName = "黑体"; // ✅ 改为黑体 QFont titleFont(fontName, 22, QFont::Bold); QFont normalFont(fontName, 14); // ✅ 14px QFont tableHeaderFont(fontName, 14, QFont::Bold); // 3. 绘制标题 int yPos = s(50); painter->setFont(titleFont); painter->drawText(QRect(0, yPos, pw, s(40)), Qt::AlignCenter, "采 购 合 同"); // 4. 基础信息 yPos += s(60); int leftCol = s(50); painter->setFont(normalFont); painter->drawText(leftCol, yPos, "日 期:" + payload.value("date").toString()); painter->drawText(pw / 2 + s(20), yPos, "订单号:" + payload.value("orderNo").toString()); yPos += s(25); painter->drawText(leftCol, yPos, "甲 方:" + payload.value("jiaFang").toString()); yPos += s(25); painter->drawText(leftCol, yPos, "乙 方:" + payload.value("yiFang").toString()); yPos += s(30); painter->drawText(leftCol, yPos, "商品信息如下:"); // 5. 表格参数 yPos += s(20); int tableLeft = s(40); int tableWidth = pw - tableLeft * 2; QVector<int> colWidths = { static_cast<int>(tableWidth * 0.35), static_cast<int>(tableWidth * 0.15), static_cast<int>(tableWidth * 0.15), static_cast<int>(tableWidth * 0.15), static_cast<int>(tableWidth * 0.20) }; QStringList headers = {"规格、描述", "数量/吨", "单价", "总额", "备注"}; int baseRowHeight = s(35); // 设置边框笔 painter->setPen(QPen(Qt::black, factor * 1.5, Qt::SolidLine)); // A. 绘制表头 painter->setFont(tableHeaderFont); int xPos = tableLeft; for (int i = 0; i < headers.size(); ++i) { QRect cellRect(xPos, yPos, colWidths[i], baseRowHeight); painter->drawRect(cellRect); painter->drawText(cellRect, Qt::AlignCenter, headers[i]); xPos += colWidths[i]; } yPos += baseRowHeight; // B. 绘制数据行 painter->setFont(normalFont); QJsonArray goods = payload.value("goods").toArray(); for (int i = 0; i < goods.size(); ++i) { // 分页检查:预留底部空间(至少 150px) if (yPos + baseRowHeight > ph - s(150)) { m_printer->newPage(); yPos = s(40); // 新页顶部留白 // 重新绘制表头(增强体验) painter->setFont(tableHeaderFont); int x = tableLeft; for (int j = 0; j < headers.size(); ++j) { QRect r(x, yPos, colWidths[j], baseRowHeight); painter->drawRect(r); painter->drawText(r, Qt::AlignCenter, headers[j]); x += colWidths[j]; } yPos += baseRowHeight; painter->setFont(normalFont); } QJsonObject row = goods[i].toObject(); QString priceStr = row.value("price").toString(); bool isTotal = priceStr.contains("合计") || priceStr.contains("总金额"); QStringList rowData; if (isTotal) { rowData << "合计" << "" << "合同总额:" << row.value("total").toString() << ""; } else { rowData << row.value("desc").toString() << row.value("qty").toString() << row.value("price").toString() << row.value("total").toString() << row.value("memo").toString(); } // ✅ 正确计算多行所需高度(关键修复!) int maxRowHeight = baseRowHeight; for (int col = 0; col < rowData.size(); ++col) { QRect rect(0, 0, colWidths[col], 10000); // 高度足够大以允许换行 int flags = Qt::TextWordWrap; if (col == 0) flags |= Qt::AlignLeft; else flags |= Qt::AlignCenter; QRect bound = painter->fontMetrics().boundingRect(rect, flags, rowData[col]); int neededHeight = bound.height() + s(8); // 上下留点间距 if (neededHeight > maxRowHeight) { maxRowHeight = neededHeight; } } // 绘制单元格 xPos = tableLeft; for (int col = 0; col < rowData.size(); ++col) { QRect cellRect(xPos, yPos, colWidths[col], maxRowHeight); painter->drawRect(cellRect); int alignFlags = Qt::TextWordWrap; if (col == 0) { alignFlags |= Qt::AlignLeft | Qt::AlignVCenter; } else { alignFlags |= Qt::AlignCenter; } painter->drawText(cellRect, alignFlags, rowData[col]); xPos += colWidths[col]; } yPos += maxRowHeight; // ✅ 累加真实高度 } // ===== 记录表格结束位置,避免后续内容重叠 ===== int tableEndY = yPos; Q_UNUSED(tableEndY); // ===== 记录表格结束位置 ===== int currentY = yPos + s(25); // 表格与条款间安全间距 // 6. 合同条款(动态高度绘制) painter->setFont(normalFont); QStringList termLines; termLines << "1.产品交付方式:产品按乙方运输方式运至甲方指定地点交付" << " 交货地址:广州市南沙区东涌镇广珠路3-13号" << " 联系人:沈生" << " 联系电话:18739631062" << "2.交货期限:请乙方按以上备注栏要求的时间完成产品之交付。" << "3.付款方式:甲方在产品发货前付清,请乙方至少提前一天通知甲方。" << "4.产品验收:产品到货签收后,由甲方进行产品验收,验收数量以甲方清点数量为准,验收不合格的甲方有权要求乙方退换。"; // << " 交货地址:" + payload.value("address").toString() // 建议从payload获取地址 for (const QString &line : termLines) { // 动态计算这一行(可能包含自动换行)的高度 QRect boundingRect = painter->fontMetrics().boundingRect( QRect(tableLeft, currentY, tableWidth, 1000), Qt::AlignLeft | Qt::TextWordWrap, line ); // 绘制该段文本 painter->drawText(boundingRect, Qt::AlignLeft | Qt::TextWordWrap, line); // 关键:将 yPos 移动到当前文本块底部,并留出小行间距 currentY += boundingRect.height() + s(5); } // 7. 签名栏(基于上述动态计算后的 currentY) currentY += s(40); // 条款与签名间增加较明显的间距 int sigWidth = tableWidth / 2 + 20; int sigLineHeight = s(30); // 甲方乙方行 【带公章】 painter->drawText(tableLeft, currentY, "甲方:" + payload.value("jiaFang").toString()); painter->drawText(tableLeft + sigWidth, currentY, "乙方:" + payload.value("yiFang").toString()); currentY += sigLineHeight; painter->drawText(tableLeft, currentY, "代表:"); painter->drawText(tableLeft + sigWidth, currentY, "代表:"); currentY += sigLineHeight; painter->drawText(tableLeft, currentY, "日期:"); painter->drawText(tableLeft + sigWidth, currentY, "日期:"); // 🚩🚩🚩 电子公章逻辑:绘制在“日期”文字的下方 🚩🚩🚩 // 基础配置:开启抗锯齿和高质量缩放,确保印章边缘平滑 painter->save(); painter->setRenderHint(QPainter::Antialiasing); painter->setRenderHint(QPainter::SmoothPixmapTransform); painter->setOpacity(0.95); // 稍微带一点点透明,盖在白纸上更真实 int sealW = s(140); // 公章宽度 int sealH = s(140); // 公章高度 int sealY = currentY + s(5); // 统一的 Y 轴起始点(日期下方 5px) // --- 1. 甲方公章 --- QImage jiaImage(":/pic/year.png"); if (!jiaImage.isNull()) { // X 坐标:甲方文字起始位置 + 偏移 int jiaX = tableLeft + s(100); painter->drawImage(QRect(jiaX, sealY, sealW, sealH), jiaImage); } // --- 2. 乙方公章 --- QImage yiImage(":/pic/mus4.png"); if (!yiImage.isNull()) { // X 坐标:乙方文字起始位置 (tableLeft + sigWidth) + 相同偏移量 int yiX = tableLeft + sigWidth + s(100); painter->drawImage(QRect(yiX, sealY, sealW, sealH), yiImage); } painter->restore(); // 恢复状态 // 🚩🚩🚩 公章处理结束 🚩🚩🚩 }

运行效果如下:(ps:png要透明背景,否则代码还需要做透明处理)

http://www.jsqmd.com/news/662059/

相关文章:

  • 【OS】RTOS的任务切换原理
  • 如何用keil5软件的debug进行仿真调试
  • 硬件级精细温控:FanControl 风扇控制系统的技术架构与实战应用
  • 从EEPROM转战SPI Flash?STM32F103驱动W25Q64,你必须搞懂的‘页卷’与擦除机制
  • 微信小程序反编译实战:深度揭秘Wedecode如何实现跨平台源代码还原
  • 【地平线开发环境实战】基于Docker快速部署与配置全流程解析
  • 如何在3分钟内免费实现跨平台远程桌面控制:BilldDesk Pro完全指南
  • 【VSCode】多文件夹工作区的头文件路径引用
  • 2026年3月光学玻璃品牌推荐,支持来图定制加工,异形件均可按需生产制作 - 品牌推荐师
  • Access练习题(3)
  • 从摇骰子到抽奖机:用Arduino的random和randomSeed函数打造5个小项目
  • SQL利用窗口函数实现轻量级报表设计_实战技巧
  • 致远ZLG 功率分析仪PA2000mini
  • 从滑动窗口到RPN:目标检测候选区域生成技术的演进与核心
  • STM32F4标准库+LAN8720网线热插拔实战:从官方EVAL工程到实际项目的移植避坑指南
  • 2026年葫芦岛汽车贴膜行业选型指南白皮书 - GrowthUME
  • Obsidian Dataview终极指南:5个简单步骤将笔记库变为智能数据库
  • 如何在PC上免费玩Switch游戏?Ryujinx模拟器让你轻松实现
  • 气象科研人必备:用Python+WRF+Cartopy绘制专业雷达回波图(附完整代码)
  • Mapbox GL JS 实战:从零构建交互式地理可视化应用
  • 财务大数据是什么?怎么选财务大数据自动化工具?
  • 2026 年葫芦岛汽车贴膜全流程深度攻略:从选型到交付一站式指南 - GrowthUME
  • 先锁定目标客户,再找获客方法-佛山鼎策创局破局增长咨询
  • 2026年2款HR系统横评:红海云与用友谁更适合制造业?
  • 测试文章2
  • 沙盒测试-前缀和
  • 如何高效利用开源API资源库:开发者必备的完整指南
  • Python的__enter__传播上下文
  • WarcraftHelper:3步解决魔兽争霸3在Win11的兼容性问题
  • BaiduPCS-Go深度解析:命令行网盘管理实战指南