
本文介绍在 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 printer\n", 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 打印子系统处理,完美契合“仅需固定宽度纯文本输出”的轻量需求。只需注意编码转换与运行上下文,即可稳定集成进您的消息服务器。











