
本文介绍在 windows 平台使用 go 语言调用系统打印 api,将 utf-8 编码的纯文本(固定宽度字体)发送至默认打印机的完整实现方案,基于 `github.com/alexbrainman/printer` 封装库,无需外部依赖或渲染引擎。
在 go 生态中,原生打印支持并非标准库功能,尤其面向 windows 的底层 GDI 打印流程需绕过高级抽象(如 pdf 渲染或 Web 预览),直接对接 windows 打印子系统。对于消息服务器等后台服务场景,要求轻量、可靠、无 GUI 交互——此时推荐使用 github.com/alexbrainman/printer 这一专为 Windows 设计的 Go 绑定库。它封装了 CreateDC, StartDoc, StartPage, WritePrinter, EndPage, EndDoc 等核心 Win32 GDI 打印 API,屏蔽了复杂句柄管理和字符编码转换细节。
以下是一个生产就绪的打印示例(已适配 UTF-8 文本与等宽字体需求):
package main import ( "fmt" "log" prt "github.com/alexbrainman/printer" ) func printToDefault(text string) error { // 获取系统默认打印机名称 printerName, err := prt.Default() if err != nil { return fmt.Errorf("failed to get default printer: %w", err) } // 打开打印机句柄 p, err := prt.Open(printerName) if err != nil { return fmt.Errorf("failed to open printer %q: %w", printerName, err) } defer p.Close() // 确保资源释放 // 启动文档(文档名建议含时间戳或 ID,便于追踪) docName := fmt.Sprintf("GoPrint-%d", time.Now().unixNano()) if err = p.StartDocument(docName, "TEXT"); err != nil { return fmt.Errorf("failed to start document: %w", err) } defer p.DocumentEnd() // 使用 defer 确保异常时也能结束文档 // 开始新页面 if err = p.StartPage(); err != nil { return fmt.Errorf("failed to start page: %w", err) } defer p.PageEnd() // 同样确保页结束 // 写入 UTF-8 文本(Windows GDI 默认按 OEM 字符集解释字节流) // ⚠️ 关键:为正确显示中文等 UTF-8 字符,需先转换为系统活动代码页(如 GBK/CP936) // 以下使用 golang.org/x/text/encoding 演示转换(需 go get) /* encoder := encoding.Windows1252.NewEncoder() // 示例:若目标打印机支持 CP1252 encoded, _ := encoder.String(text) n, err := p.Write([]byte(encoded)) */ // ✅ 更稳妥做法:使用系统默认 OEM 编码(GetOEMCP),或指定打印机支持的代码页 // 实际部署前请确认打印机驱动是否支持 UTF-8 直通(少数现代打印机支持),否则必须转码 n, err := p.Write([]byte(text)) if err != nil { return fmt.Errorf("failed to write to printer: %w", err) } fmt.Printf("Successfully sent %d bytes to printern", n) return nil } func main() { msg := "Hello, 你好,Printer!nThis is monospace text.nLine 3." if err := printToDefault(msg); err != nil { log.Fatal(err) } }
关键注意事项:
- ✅ 字体控制:该库不提供字体设置接口。实际输出字体由打印机驱动默认决定(通常为 Courier 或系统等宽字体)。如需精确控制,需改用 GDI+ 或通过 EMF 记录生成,但会显著增加复杂度。
- ⚠️ UTF-8 编码兼容性:Windows GDI WritePrinter 接口接收原始字节流,默认按当前线程 OEM 代码页(如 CP437/CP936)解析。若直接传入 UTF-8 字节,可能显示乱码。强烈建议:
- 查询目标打印机规格,确认其是否原生支持 UTF-8(如部分 Zebra、Brother 型号);
- 否则,使用 golang.org/x/text/encoding 将 UTF-8 字符串显式转为对应 OEM 编码(例如 encoding.TraditionalChinese 对应 CP950);
- 或调用 Win32 MultiByteToWideChar + WideCharToMultiByte 实现动态代码页转换(需 CGO)。
- ?️ 权限与上下文:Windows 服务账户默认无交互式桌面会话,无法访问用户级打印机。若程序以 Windows Service 运行,需配置服务登录为“本地系统”并勾选“允许服务与桌面交互”(不推荐),或改用“网络打印协议(IPP/LPD)”对接 CUPS/linux 打印服务器,或通过 Windows 事件日志+计划任务间接触发用户会话打印。
- ? 错误处理与重试:打印机离线、缺纸、卡纸时 Write 可能返回 ERROR_IO_PENDING 或 ERROR_PRINT_CANCELLED。建议添加超时机制与状态轮询(prt.Status()),并设计幂等重试逻辑。
综上,alexbrainman/printer 是目前 Windows Go 打印最简洁可靠的方案。它直击本质——将文本作为原始字节流交由 Windows 打印子系统处理,完美契合“仅需固定宽度纯文本输出”的轻量需求。只需注意编码转换与运行上下文,即可稳定集成进您的消息服务器。