1. 为什么需要绕过Office,直连打印机?
大家好,我是老张,一个在自动化领域摸爬滚打了十来年的程序员。今天想和大家聊聊一个看似“古老”但极其实用的技术:用Python直接控制打印机。你可能觉得,打印嘛,不就是点一下“文件”->“打印”吗?但在很多实际的生产、办公自动化场景里,这种方式效率太低,限制太多了。
我最早遇到这个问题,是在做一个医疗器械的样品管理系统。每天需要打印几百张不干胶标签,贴在试管和档案袋上。标签内容来自数据库,格式固定,但包含编号、姓名、检测项目和日期。一开始,我们用了最“省事”的办法:用Python生成Word文档(.docx),然后调用Word的COM接口去打印。效果确实不错,字体、排版都能精确控制。但问题很快就来了:每次打印,后台都会“唰”地弹出一个Word窗口,虽然一闪而过,但几百次下来,不仅看着烦,还偶尔会卡住,导致整个打印队列停滞。更别提对Office版本的依赖了,换台电脑可能就歇菜。
后来试过生成PDF再打印。PDF是安静了,不弹窗,但打印出来的文字边缘总觉得有点“发虚”,不如Word直接打印那么锐利。对于要求精准、清晰的小字号标签来说,这点差异很要命。至于直接打印TXT文本?那更是天方夜谭,字体、加粗、位置统统没法控制。
所以,我的需求很明确:我需要一个像Word一样能精确控制每一个像素的排版,但又像TXT打印一样安静、高效、不依赖任何第三方大型软件的方法。 答案就是绕过所有中间商,直接和打印机“对话”。这就是我们今天要讲的,使用Python的win32ui库进行底层图形设备接口(GDI)绘图,直接把我们要的文字、图形“画”到打印机的页面上。
这种方法听起来有点硬核,但一旦掌握,你就拥有了对打印输出的绝对控制权。无论是打印A4报告、票据,还是各种奇形怪状的标签贴,你都能用代码像画家在画布上作画一样,自由挥洒。接下来,我就带你从零开始,搞定这套流程。
2. 环境准备与核心原理初探
2.1 你需要准备什么?
在开始写代码之前,我们得先把场子搭好。放心,不需要很复杂的环境。
第一,操作系统。我们这套方法的核心win32ui库,是Python在Windows平台上的专属福利。所以,你需要在Windows系统上进行开发和生产部署。Linux或macOS下有其他打印控制方案,但今天我们先聚焦在Windows这条最成熟的路线上。
第二,Python环境。建议使用Python 3.6及以上版本。我个人的主力环境是Python 3.8,比较稳定。
第三,也是最重要的,安装pywin32库。 这个库封装了Windows大量的API,win32ui就在其中。安装非常简单,打开你的命令行(CMD或PowerShell),输入:
pip install pywin32
有时候,仅仅pip install可能还不够,因为pywin32包含一些需要注册的COM组件。如果安装后导入win32ui报错,可以尝试以管理员身份运行命令行,执行以下命令进行后安装:
python -m pywin32_postinstall -install
第四,一台打印机。 可以是连接在你电脑上的本地打印机,也可以是网络打印机。最好是一台你常用的,这样测试起来方便。我的测试机是一台联想M7615DNA激光打印机,还有一台热敏标签打印机。
准备好了吗?让我们先来理解一下它的工作原理,这能帮你少走很多弯路。
2.2 核心原理:把打印机当成一张画布
你可以暂时忘掉“文档”、“页面”这些概念。win32ui提供给我们的思路非常图形化:它把打印机(或者说打印任务)虚拟成一张“画布”(Device Context, 设备上下文),我们在这张画布上用代码“画画”,画完了,把整张画布交给打印机输出。
这个过程和你在Windows画图程序里操作很像:
- 创建画布:
CreateDC()->CreatePrinterDC(), 指定用哪台打印机。 - 开始一幅新画:
StartDoc()给这幅画起个名字(任务名),StartPage()表示开始画这一页。 - 作画:
- 选画笔(字体):
CreateFont()创建一种字体,SelectObject()把它选为当前使用的“画笔”。 - 写文字:
TextOut(x, y, “文字”),在坐标(x, y)处写下文字。 - 画图形:
MoveTo(),LineTo()画线,Rectangle()画矩形,Ellipse()画椭圆。
- 选画笔(字体):
- 完成并交付:
EndPage()表示这一页画完了,EndDoc()表示整幅画(任务)完成,可以送出去打印了。
所有的精准控制,都源于我们对这张“画布”上每一个像素坐标的掌控。接下来,我们就从获取这张画布的实际大小开始。
3. 第一步:获取打印机的“可打印区域”
这是所有精准排版的基础,也是最容易踩坑的第一步。你以为你设置的位置是相对于A4纸的左上角,但打印机往往有自己的“边界”。这个边界内的区域,才是真正能着墨的“可打印区域”。
想象一下,你买了一个带边框的相框,你能放照片的区域,肯定比相框本身要小一圈。打印机也是如此,由于机械结构限制,纸张边缘的一小部分是无法打印的。用代码获取这个区域尺寸至关重要。
import win32ui
# 1. 创建一个设备上下文对象
hDC = win32ui.CreateDC()
# 2. 将其与你的打印机关联。参数是打印机名称,传入空字符串则使用系统默认打印机。
# 打印机名称可以在“控制面板->设备和打印机”里找到。
hDC.CreatePrinterDC('') # 使用默认打印机
# 3. 获取可打印区域的像素尺寸
printable_width = hDC.GetDeviceCaps(8) # 宽(像素)
printable_height = hDC.GetDeviceCaps(10) # 高(像素)
print(f"可打印区域宽度:{printable_width} 像素")
print(f"可打印区域高度:{printable_height} 像素")
# 记得用完释放资源(虽然程序结束会自动释放,但好习惯要养成)
hDC.DeleteDC()
关键解释:
GetDeviceCaps(8)和GetDeviceCaps(10)是固定的索引值,分别对应物理可打印区域的宽度和高度。这个值是打印机驱动告诉我们的,是最准确的。- 单位是像素,但这

129

被折叠的 条评论
为什么被折叠?



