37 给 AI 用的文档打印实操指南(Windows,已实测)
2026-08-28 由千问办公在 Windows 11 实机全流程测试通过。本文面向 AI 助手:读完即可直接帮用户打印文档,不用再试错。
0. 给 AI 的总纲:先分清"问用户什么"和"自己做什么"
接到打印需求后,先收集必要信息,再动手。不要上来就发任务。
必须向用户确认的信息(AI 无法替用户决定)
| 项目 | 说明 | 示例问法 |
|---|---|---|
| 文件路径 | 绝对路径;确认文件存在、格式 | "请给我要打印文件的完整路径" |
| 打印机 | 用户机器可能有多台(实体+虚拟),列表发给用户选 | "你要打到这台还是那台?" |
| 纸张 | A4 / A3 / letter;不确定就问 | "纸张用 A4 吗?" |
| 单双面 | 单面 / 双面长边翻(普通文档)/ 双面短边翻 | "双面还是单面?" |
| 彩色/黑白 | 用户机器上可能有黑白机和彩机两台 | "要彩色还是黑白?" |
| 份数 | 默认 1 份 | "打几份?" |
| 页码范围 | 全部 / 第 1-3 页 / 奇偶页;长文档必问 | "全打还是只打一部分?" |
| 打印时机 | 立即出纸还是先出 PDF 预览确认 | — |
AI 可以自己做、不需要问用户的
- 枚举打印机列表(含状态、是否虚拟)→ 整理成选项给用户挑
- 检查文件存在性、大小、页数
- 判断打印机是实体还是虚拟(看端口名,见 §2)
- 渲染 PDF、设置参数、提交任务、监控队列、报告完成/报错
- 没有实体打印机时自建"文件端口测试打印机"做无纸验证
- 环境清理(删除测试打印机/端口、清理临时文件)
最简问法示例:"你要打印 X 文件,机器上有 A(黑白激光)和 B(彩色复合机),用哪台?单双面?几份?全打还是部分页?"
1. 技术方案总览(为什么是这个方案)
调研过三条路线后选定的零依赖方案,全部用系统内建能力,不需要下载任何软件(实测本机 GitHub 及全部镜像不可达,SumatraPDF 等下载路线装不上,别试):
PDF 文件
→ Python pypdfium2 渲染目标页为 300dpi 位图
→ PowerShell .NET System.Drawing.Printing.PrintDocument 参数化打印
→ 打印机队列 → 出纸
→ Get-PrintJob 轮询直到队列清空 = 完成
优点:参数控制全在一处(PrinterSettings/PageSettings)、无对话框、失败可定位、换打印机只改一个名字。
备选路线(了解即可,一般不用):SumatraPDF 命令行 -print-to -print-settings(功能等价,需下载 exe,网络被墙时装不上);Set-PrintConfiguration/PrintTicketXML 改打印机系统级默认(依赖厂商驱动,兼容性差)。
2. 第一步:枚举打印机并判断虚实
powershell.exe -NoProfile -Command '[Console]::OutputEncoding=[System.Text.Encoding]::UTF8; Get-Printer | Select-Object Name, DriverName, PortName, PrinterStatus | Format-Table -AutoSize'
判断依据(看 PortName):
| 端口特征 | 类型 | 能否实际出纸 |
|---|---|---|
PORTPROMPT: |
虚拟(Microsoft Print to PDF) | 否,弹"将打印输出另存为"对话框 |
| 虚拟驱动名(Kingsoft/Wondershare 等) | 虚拟 | 否 |
WSD-* / IP_* / IP 地址 |
网络实体打印机 | 是,但注意 Offline 状态 |
USB* / LPT* |
本地实体打印机 | 是 |
实体打印机还要看 PrinterStatus:Normal=可用,Offline=不在线(提醒用户开机/连网)。
把列表整理成"名称 + 黑白还是彩色 + 在线状态"给用户选。注意:虚拟打印机测试时每打一次都会弹系统对话框"将打印输出另存为"(真实标题就是这个,不是"另存为打印输出"),是 PORTPROMPT: 端口特性,无法用参数关掉。
3. 第二步:检查文件并获取页数
Get-Item "文件路径" | Select-Object FullName, Length, LastWriteTime
from pypdf import PdfReader
print(len(PdfReader(r'文件路径').pages)) # 页数
- 只支持 PDF(位图渲染管线)。Word/Excel/图片需先让用户另存为 PDF,或用对应 COM 自动化转换(Word:
SaveAs2 FileFormat:=17) - 加密/损坏的 PDF pypdfium2 会抛异常,提前发现
4. 第三步:渲染目标页(300dpi 够清晰)
import pypdfium2 as pdfium
pdf = pdfium.PdfDocument(r'文件路径')
for i in [0, 1]: # 0-based 页码,按用户要的范围来
img = pdf[i].render(scale=300/72) # 300dpi:A4 得 2481x3508
img.to_pil().save(rf'C:\temp\print_{i+1}.png')
页码换算:用户说"打第 3 页"= 索引 2。范围"1-3,5"逐页渲染。长文档别全渲,只渲用户要的页。
5. 第四步:参数化提交打印任务(核心代码)
写成一个 .ps1 脚本执行。⚠️ 脚本含中文注释时必须存成 UTF-8 with BOM(PowerShell 5.1 把无 BOM 的 UTF-8 当 ANSI 解析会直接语法错误)。转 BOM:
$c=[System.IO.File]::ReadAllText($path,[System.Text.Encoding]::UTF8)
[System.IO.File]::WriteAllText($path,$c,(New-Object System.Text.UTF8Encoding $true))
脚本模板(把注释里的参数位按用户需求改):
Add-Type -AssemblyName System.Drawing
$pngs = @("C:\temp\print_1.png", "C:\temp\print_2.png") # 渲染好的页
$pd = New-Object System.Drawing.Printing.PrintDocument
$pd.PrinterSettings.PrinterName = "这里填用户选的打印机名"
# ===== 参数区 =====
$pd.PrinterSettings.Copies = 1 # 份数
$pd.PrinterSettings.Duplex = [System.Drawing.Printing.Duplex]::Simplex # 单面
# 双面长边翻(普通文档): [System.Drawing.Printing.Duplex]::Vertical
# 双面短边翻(日历便签): [System.Drawing.Printing.Duplex]::Horizontal
$a4 = $pd.PrinterSettings.PaperSizes | Where-Object {
$_.Kind -eq [System.Drawing.Printing.PaperKind]::A4 } | Select-Object -First 1
$pd.DefaultPageSettings.PaperSize = $a4 # 纸张(从驱动 PaperSizes 取,别硬编码)
$pd.DefaultPageSettings.Landscape = $false # 横向改 $true
$pd.DefaultPageSettings.Color = $false # 黑白;彩色改 $true
$pd.PrintController = New-Object System.Drawing.Printing.StandardPrintController # 静默,无进度框
# ================
$script:pageIdx = 0
$pd.add_PrintPage({
param($sender, $e)
$img = [System.Drawing.Image]::FromFile($pngs[$script:pageIdx])
$e.Graphics.DrawImage($img, $e.MarginBounds)
$img.Dispose()
$script:pageIdx++
$e.HasMorePages = ($script:pageIdx -lt $pngs.Count)
})
$pd.Print()
参数速查:
| 用户需求 | 设置 |
|---|---|
| A4 | PaperSize = PaperKind.A4 |
| 单面 | Duplex = Simplex |
| 双面正常文档 | Duplex = Vertical(长边翻) |
| 黑白 | DefaultPageSettings.Color = $false |
| 彩色 | DefaultPageSettings.Color = $true |
| N 份 | PrinterSettings.Copies = N |
| 页码范围 | 只渲染用户要的页(§4),不必用 FromPage/ToPage |
| 横向 | Landscape = $true |
注意:黑白机(如 Pantum)上设 Color=$true 无意义但也无害;彩机上才能体现彩黑差别。
6. 第五步:监控队列直到完成
打印脚本退出后接着轮询:
for ($i = 0; $i -lt 120; $i++) {
Start-Sleep -Milliseconds 500
$jobs = Get-PrintJob -PrinterName "打印机名" -ErrorAction SilentlyContinue
if (-not $jobs) { break } # 队列清空 = 完成
foreach ($j in $jobs) {
"$($j.Id) $($j.DocumentName) $($j.JobStatus)" # Printing/Error/PaperOut...
}
}
状态判读:JobStatus 出现 Error/PaperOut/PaperJam/Offline → 把含义翻译给用户(缺纸/卡纸/离线)。边界:队列清空只能证明任务已被设备接收,纸有没有真出来以打印机面板为准;卡纸类故障会反映为队列报错,不会静默丢失。
实测耗时参考:2 页 A4 任务从提交到队列排空约 5 秒。
7. 没有实体打印机时:自建文件端口测试打印机(无纸验证)
虚拟打印机每次弹对话框无法自动化,解法是给 Print to PDF 驱动绑一个文件端口,打印结果直接落盘成真 PDF,全程无对话框、无需管理员权限:
Add-PrinterPort -Name "C:\temp\printtest\out.prn"
Add-Printer -Name "QW-FilePort-Test" -DriverName "Microsoft Print To PDF" -PortName "C:\temp\printtest\out.prn"
然后按 §5 打印,PrinterName 用 QW-FilePort-Test。验证输出:out.prn 文件头应为 %PDF-1.7,可直接改名 .pdf 打开核对页数(= 页数×份数)和内容。
用完必须清理:
Remove-Printer -Name "QW-FilePort-Test"
Remove-PrinterPort -Name "C:\temp\printtest\out.prn"
8. AI 执行流程清单(照此执行)
- 问用户:文件路径、打印机、纸张、单双面、彩黑、份数、页码范围(§0 表格)
Get-Printer枚举打印机,标出虚拟/离线,交给用户确认选哪台- 检查文件存在 + 页数(§3);非 PDF 先转 PDF
- 渲染目标页 300dpi PNG(§4)
- 写 .ps1(§5 模板,UTF-8 with BOM),执行提交任务
- 轮询
Get-PrintJob直到清空或报错(§6),向用户报告结果 - 若是测试打印机流程:验证 out.prn 是合法 PDF 后清理环境(§7)
- 全程把"做了什么、参数是什么、结果如何"告诉用户
9. 踩坑汇总(实测付出过代价的)
- PowerShell 5.1 + 无 BOM UTF-8 脚本 = 中文语法错误:必须 UTF-8 with BOM
- bash 里调 powershell:整条命令用单引号包住,否则
$变量被 bash 吞 - 控制台中文乱码:开头加
[Console]::OutputEncoding=[System.Text.Encoding]::UTF8 PORTPROMPT:端口必弹"将打印输出另存为":无人工响应则无输出文件,自动化场景用 §7 文件端口方案绕开- A4 纸张别硬编码宽高:从驱动
PaperSizes集合按PaperKind.A4取 - 网络下载工具不可靠:GitHub 及加速镜像可能全部不可达,优先系统内建能力
- 退出码/队列≠纸张落地:缺纸卡纸在队列报错可见,最终出纸以打印机面板为准