前言

        QT对打印和PDF应用场景,做了简单的封装,复杂的功能还是得用第三方库,打印功能简单的文本可以不用PDF,涉及图形的基本都要用到PDF。

Linux打印

        随着国产信创项目替换基于Linux的桌面系统国产信创系统,Linux桌面系统用的装机量越来越多,打印功能的使用频度也越来越高,以下分享Linux下的打印工具。

CPUS

        ​​CUPS​​(​​Common UNIX Printing System​​,通用UNIX打印系统)是 Linux 和类 UNIX 操作系统上事实标准的、开源的打印系统。在 2007 年被苹果公司收购。苹果macOS的打印系统也是基于 CUPS(Quartz+CUPS,Quartz将内容渲染为PDF格式‌,再推送到CUPS进行打印)。

        国产信创操作系统(麒麟kylin和统信UOS系统)默认安装了CUPS,可以提供web、官方工具或者是第三方工具查看系统打印机信息。

web访问

        默认端口是631,创建打印机需要用户名和密码。

http://localhost:631/

在上图中可以看到Local Printers中有一个CUPS-PDF,这是把打印内容输出为PDF文件,CUPS默认是不安装这个功能的,需要手动下载

sudo apt install cups-pdf

cups-pdf

配置文件:/etc/cups/cups-pdf.conf
配置文件中Out ${HOME}/PDF为pdf文件保存的路径,可以修改为:Out /home/printpool

对于新手来说,web方式不是很好操作,以下介绍好用的工具。

system-config-printer

apt install system-config-printer

以下展示如何创建一个PD打印机

以上配置完成之后,打开一个待打印的文件,点击打印按钮之后,即可看到新增的打印机,如下图

应用场景延伸

        以上PDF打印机是可以创建很多的,但cups-pdf配置文件指向了统一的输出目录,如果需要多个cups-pdf,多个不同的输出目录,可以如下操作:

在/etc/cups目录下拷贝cups-pdf.conf,比如生成cups-pdf-mypdf.conf,修改cups-pdf-mypdf中Out的输出目录即可,这个时候在web端我打马赛克的地方可以看到这个PDF,但在system-config-printer工具中是不到的,不管没关系,创建过程是一样的,只需要修改设备URI即可,如下

命令行工具

lpstat -p -d: 列出所有打印机 (-p) 并显示默认打印机 (-d)

lp <文件名>: 使用默认打印机打印文件

lp -d <打印机名> <文件名>: 指定打印机进行打印。
sudo lpadmin -p <打印机名称> <关键参数>:创建打印机

sudo lpadmin -p MyPrinter -E -v ipp://192.168.1.100/ipp/port1 -m everywhere

连接类型​

​URI格式​

​示例​

PDF打印机cups-pdf:/cups-pdf:/

USB打印机

usb://vendor/model?serial=xxx

usb://HP/Deskjet?serial=123ABC

网络IPP打印机

ipp://IP地址/ipp/print

ipp://192.168.1.100/ipp/print

Windows共享打印机

smb://用户名:密码@主机名/打印机共享名

smb://user:pass@WINPC/OfficePrinter

LPD协议打印机

lpd://IP地址/队列名

lpd://192.168.1.101/L1

AppSocket打印机

socket://IP地址:端口

socket://192.168.1.102:9100

以ppd模板创建打印机

        ppd模板文件默认生成在/etc/cups/ppd/目录下,上面通过system-config-printer创建的【我的PDF打印机】在此目录下会生成一个【我的PDF打印机.ppd】文件,以此作为模板即可生成配置一样的打印机,如下

sudo lpadmin -p 我的打印机 -E -v cups-pdf:/ -P /etc/cups/ppd/我的PDF打印机.ppd

cups开发库

sudo apt install cups libcups2-dev

编译时加上-lcups

//列出所有打印机
#include <cups/cups.h>int main() {cups_dest_t *dests;int num_dests = cupsGetDests(&dests);  // 获取打印机列表printf("找到 %d 台打印机:\n", num_dests);for (int i = 0; i < num_dests; i++) {printf("%d. %s", i+1, dests[i].name);if (dests[i].is_default) printf(" [默认]");printf("\n");}cupsFreeDests(num_dests, dests);  // 释放内存return 0;
}

以下是核心API

​函数​

​描述​

cupsGetDests()

获取打印机列表

cupsPrintFile()

提交文件打印任务

cupsGetDefault()

获取默认打印机名称

cupsLastErrorString()

获取最后一次错误的描述

cupsAddOption()

添加打印选项

cupsGetPPD()

获取打印机的 PPD 文件路径

cupsTempFile()

创建临时文件

cupsMarkOptions()

验证打印选项是否有效

window打印

        传统打印路径依赖 ‌GDI‌(图形设备接口),较老旧,现代路径使用 ‌XPS‌(XML 打印规范,类似 PDF)。打印驱动直接与 GDI/XPS 交互。从这里可以看出window下与Linux和macOS完全不一样,QT的接口封装也不一样,可能也是QT没有对QPrinter进行深度的封装的原因吧。

        在国产信创项目推进过程中,很多客户都会问:我的这些打印机都能在国产信创终端上使用吗。第一反应通常是:打印机厂商提供有驱动的都可以用(很多老打印机厂商是不维护驱动的,这部分打印机无法在国产信创设备下使用)。但换个思路,提供一台有老打印机驱动的window系统,结合以上分享的linux的cups-pdf打印机,把PDF文件传输到这个window系统下生成打印任务,即可解决”国产信创设备下使用不了老打印机“的问题。

        window下,QT的QPrinter支持调用系统级打印对话框来打印PDF文件,但不支持后台命令行方式(打印机名称+PDF文件名称)打印PDF文件的!以下分享一个支持后台命令行方式打印PDF文件的工具。

SumatraPDF

        下载地址:Sumatra PDF reader download pagehttps://www.sumatrapdfreader.org/download-free-pdf-viewer配置环境变量之后,打印PDF文件的命令行如下

SumatraPDF.exe -print-to "Lexmark MX410de" "D:\myfile.pdf" > nul 2>&1

        以上的分享也是希望点题:print和pdf分不开。 以下会分享一个qt样例,从开发角度进一步了解两者的关系:选择并展示PDF内容,打印预览PDF内容,打印PDF内容。

效果图

功能详细讲解

​​​​        QPdf核心接口是QPdfDocuments,可进行加载PDF、获取PDF页数、将 PDF 页面渲染为图像。

选择PDF文件

调用了加载PDF、获取PDF页数两个接口,代码如下:

//头文件
#include <QPdfDocument>
class MainWindow : public QMainWindow
{
private:QPdfDocument *pdfDoc;
};//cpp文件
#include <QStandardPaths>
void MainWindow::on_btnSelect_clicked()
{QString desktopPath = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);//获取桌面路径QString file = QFileDialog::getOpenFileName(this,tr("Open PDF"), desktopPath, tr("PDF Files (*.pdf)"));if(file.isEmpty()) return;currentFile = file;pdfDoc->load(file);ui->spinBox->setMaximum(pdfDoc->pageCount());ui->spinBox->setValue(1);updatePreview();
}void MainWindow::updatePreview()
{if(pdfDoc->pageCount() <= 0) return;int page = ui->spinBox->value() - 1;QImage image = pdfDoc->render(page, ui->labelPreview->size());ui->labelPreview->setPixmap(QPixmap::fromImage(image));
}

打印预览

        QPdf部分调用了获取PDF页数、将 PDF 页面渲染为图像两个接口

        QPrinter部分调用了QPrintPreviewDialog弹出系统级的打印对话框,对话框上有打印按钮,另外QPrintPreviewWidget可以显示打印预览框,但是风格比较简洁,效果如下:

以下仅给出使用QPrintPreviewDialog的代码:

//头文件
#include <QPainter>
class MainWindow : public QMainWindow
{
private:QPrinter printer;
}//cpp文件
void MainWindow::on_actionPrintPreview_clicked()
{if (pdfDoc->pageCount() == 0) return; // 检查是否有可打印内容// 创建打印预览部件QPrintPreviewDialog preview(&printer, this);connect(&preview, &QPrintPreviewDialog::paintRequested,this, &MainWindow::printPreview);preview.exec();
}void MainWindow::printPreview()
{QPainter painter(&printer);painter.setRenderHints(QPainter::Antialiasing |QPainter::TextAntialiasing |QPainter::SmoothPixmapTransform,// 使用平滑变换绘制true);// 精确计算DPI缩放比例const double dpiScale = printer.logicalDpiX() / 72.0;painter.scale(dpiScale, dpiScale);// 高质量渲染PDF每页内容for(int i = 0; i < pdfDoc->pageCount(); ++i) {if(i > 0) printer.newPage();QSizeF pageSize = pdfDoc->pagePointSize(i);// 将PDF页面渲染为图像QImage pageImage = pdfDoc->render(i, pageSize.toSize() * dpiScale);// 绘制到打印机painter.drawImage(QRectF(0, 0, pageSize.width(), pageSize.height()),pageImage,QRectF(0, 0, pageImage.width(), pageImage.height()));}
}

打印文件

        QPdf部分调用了获取PDF页数、将 PDF 页面渲染为图像两个接口

        QPrinter部分调用了QPrintDialog弹出系统级的打印对话框,让用户选择打印机之后进行打印

void MainWindow::on_btnPrint_clicked()
{if(currentFile.isEmpty()) {QMessageBox::warning(this, tr("Error"), tr("No PDF file selected"));return;}QPrintDialog dialog(&printer, this);if(dialog.exec() == QDialog::Accepted) {QPainter painter;if(!painter.begin(&printer)) {QMessageBox::critical(this, tr("Error"), tr("Failed to initialize printer"));return;}for(int i = 0; i < pdfDoc->pageCount(); ++i) {if(i > 0) printer.newPage();//QImage image = pdfDoc->render(i, printer.pageRect(QPrinter::Point).size());QSizeF sizeF = printer.pageRect(QPrinter::Point).size();QImage image = pdfDoc->render(i, sizeF.toSize());  // 显式转换为QSizepainter.drawImage(printer.pageRect(QPrinter::Point), image);}painter.end();}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/95562.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/95562.shtml
英文地址,请注明出处:http://en.pswp.cn/bicheng/95562.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【大数据技术实战】Flink+DS+Dinky 自动化构建数仓平台

一、背景&#xff1a;企业数仓建设的现状与挑战在数字化转型进入深水区的今天&#xff0c;数据已成为企业核心生产要素&#xff0c;而实时数仓作为 “数据驱动决策” 的关键载体&#xff0c;其建设水平直接决定企业在市场竞争中的响应速度与决策精度。根据 IDC《2024 年全球大数…

Python开篇:撬动未来的万能钥匙 —— 从入门到架构的全链路指南

Python&#xff1a;撬动未来的万能钥匙——从入门到架构的全链路指南 在技术的星空中&#xff0c;Python 是那颗永不陨落的超新星——它用简洁的语法点燃创造之火&#xff0c;以庞大的生态铺就革新之路。无论你身处哪个领域&#xff0c;这把钥匙正在打开下一个时代的大门。2024…

【QT随笔】事件过滤器(installEventFilter 和 eventFilter 的组合)之生命周期管理详解

【QT随笔】事件过滤器(installEventFilter 和 eventFilter 的组合)之生命周期管理详解 上一章节中提到事件过滤器(Event Filter),用于处理特定事件。其中第二小节中提到了事件过滤器生命周期管理。本文将详细解析事件过滤器生命周期管理这一部分的内容。 (关注不迷路哈!…

关于linux软件编程12——网络编程3

一、单循环服务器 特点:1.可以处理多个客户端 (不能同时)2.效率不高//单循环服务器: socket bind listen while (1) {connfd accept();//通信 }特点:简单 可以处理多客户端 不能同时 二、并发服务器 --- 同时可以处理多个客户端1、设置一个选项(开启一个功能) ---让地址重…

thinkphp6通过workerman使用websocket

安装workerman依赖 composer require topthink/think-worker composer require topthink/think-worker1.0.* # 指定兼容版本‌:ml-citation{ref"1,7" data"citationList"}config配置 config/worker.php <?php return [// 扩展自身需要的配置host …

Rust SQLx 开发指南:利用 Tokio 进行性能优化

在当今高并发的应用开发环境中&#xff0c;数据库操作往往是性能瓶颈的主要来源之一。SQLx 作为一个纯 Rust 编写的异步 SQL 客户端库&#xff0c;通过与 Tokio 运行时深度集成&#xff0c;为开发者提供了处理数据库 I/O 密集型操作的强大工具。本文将带您深入了解如何利用这两…

嵌入式硬件电路分析---AD采集电路

文章目录摘要AD采集电路1AD采集电路2R77的真正作用是什么&#xff1f;理想与现实&#xff1a;为什么通常可以忽略R77的影响&#xff1f;摘要 AD采集 AD采集电路1 这是个人画的简化后的AD采集电路 这是一个AD检测电路&#xff0c;R1是一个可变电阻&#xff0c;R2是根据R1的常用…

Python爬取nc数据

1、单文件爬取爬取该网站下的crupre.nc数据&#xff0c;如下使用requests库&#xff0c;然后填写网站的url&#xff1a;"http://clima-dods.ictp.it/regcm4/CLM45/crudata/"和需要下载的文件名&#xff1a;"crupre.nc"import requests import osdef downlo…

策略模式 + 工厂模式

策略模式&#xff1a;简单来说解决的行为的封装与选择。如HandlerMapping&#xff0c;将 HTTP 请求映射到对应的处理器&#xff08;Controller 或方法&#xff09;。工厂模式&#xff1a;解决的是具有相同属性的对象创建问题&#xff0c;如BeanFactory创建bean对象。解决的代码…

Diamond基础3:在线逻辑分析仪Reveal的使用

文章目录1. 与ILA的区别2. 使用Reveal步骤3.Reveal注意事项4.传送门1. 与ILA的区别 Reveal是Lattice Diamond集成开发环境用于在线监测信号的工具&#xff0c;ILA是xilinx的Vivado集成开发工具的在线逻辑分析仪&#xff0c;同Reveal一样&#xff0c;均可以在项目运行过程中&am…

超适合程序员做知识整理的 AI 网站

这次要给大家分享一个超适合程序员做知识整理的 AI 网站 ——Notion AI&#xff0c;网址是Notion&#xff0c;它能把你随手记的杂乱笔记、代码片段、技术文档&#xff0c;一键梳理成逻辑清晰的结构化内容&#xff0c;小索奇我用它整理 “Python 爬虫知识点” 时&#xff0c;原本…

【 Selenium 爬虫】2025年8月25日-pixabay 图片采集

无恶意采集&#xff0c;取部分图片用来做相册测试的&#x1f604; 效果图import cn.hutool.core.io.FileUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.json.JSONUtil; import com.la.selenium.utils.SeleniumUtil; import lombok.extern.slf4j.Slf4j; import o…

服务器托管需要注意什么事项?

服务器托管是企业IT基础设施的关键环节&#xff0c;其稳定性和安全性直接影响业务连续性。需要注意下面这几点&#xff01; 一、服务商与机房选择 服务商资质 选择持有ISP证书的合法服务商&#xff0c;优先考虑运营超5年、市场口碑佳的老牌公司&#xff0c;技术团队需具备72…

微信小程序备忘

1.按钮事件中想切换到tabBar中的链接用switchTab&#xff0c;不能用navigateTo&#xff1a;agentPage: function() { wx.switchTab({url: /pages/agent/agent}) },特别注意&#xff1a;微信小程序中所谓的自定义&#xff0c;并不是完全的自定义&#xff0c;在app.json中定义&a…

虚拟机NAT模式通过宿主机(Windows)上网不稳定解决办法(无法上网)(将宿主机设置固定ip并配置dns)

文章目录问题描述解决办法分析**1. 问题的根本原因****(1) 宿主机动态IP的DNS配置问题****(2) NAT模式下的网络依赖****(3) 自习室WiFi的潜在限制****2. 用户操作的合理性分析****(1) 固定IP的作用****(2) 手动指定公共DNS的作用****3. 用户怀疑的正确性****4. 其他可能原因的排…

基于 HTML、CSS 和 JavaScript 的智能图像虚化系统

目录 1 前言 2 技术实现 2.1 HTML&#xff1a;搭建页面基础结构 2.2 CSS&#xff1a;打造科技感视觉体验 2.3 JavaScript&#xff1a;实现核心虚化功能 2.3.1 图像上传与初始化 2.3.2 实时虚化处理 2.3.3 图像下载功能 3 完整代码 4 运行结果 5 总结 1 前言 三大核…

PS更改图像尺寸

新建文档 1.左上角——新文件可以新建文档2.文件——新建文档3.快捷键CtrlN 对文件命名 输入新文件名称设置宽度和高度 设置文件的宽高&#xff0c;单位可以是像素、英寸、厘米等。还可以选择文件方向或者是否使用画板模式画布背景色 一般显示白色&#xff0c;也可以选择其他颜…

分词器详解(一)

文章目录&#x1f31f; 第0层&#xff1a;极简版&#xff08;30秒理解&#xff09;核心公式生活比喻&#x1f4da; 第1层&#xff1a;基础概念&#xff08;5分钟理解&#xff09;1. 分词器基础1.1 分词器的核心作用1.2 主流分词算法对比2. 基础实现2.1 BPE实现原理2.2 特殊标记…

推荐一个论文阅读工具ivySCI

1.一些关于ivySCI的数据 &#xff08;摘自&#xff1a;吴焱红&#xff0c;论文示范:ivySCI 在论文管理、阅读和笔记中的体验&#xff09; 1.科研人员花在文献阅读上的时间占总工作时间的 23%2.每年阅读的文献数量大概是 188 到 280 篇3.ivySCI 提供 Pad(iPad 和 Android) 和桌…

诊断服务器(Diagnostic Server)

在《SWS_Diagnostics.pdf》中,诊断服务器(Diagnostic Server) 是诊断管理(DM)的核心执行单元,聚焦 “软件集群(SoftwareCluster)级诊断资源的独立管控”,实现 UDS(ISO 14229-1)与 SOVD(ASAM 服务化诊断)的全流程诊断功能。以下结合文档 7.3 节 “Diagnostic Serve…