热敏打印机模板
热敏模板使用 XML 格式,可从同一模板生成屏幕预览和 ESC/POS 打印机命令。它们使用与 HTML 模板相同的 {{variable}} 占位符进行数据绑定。
如果收据打印机已通过打印机设置连接,请选择此引擎。
XML 元素
根元素
每个热敏模板都以一个 <receipt> 根元素开头:
<receipt paper-width="48">
<!-- 48 characters = 80mm paper -->
<!-- 32 characters = 58mm paper -->
</receipt>
文本和格式
<text>Plain text</text>
<bold>Bold text</bold>
<underline>Underlined text</underline>
<invert>Inverted (white on black)</invert>
对齐方式
<align mode="left">Left aligned</align>
<align mode="center">Centered</align>
<align mode="right">Right aligned</align>
文本大小
分别缩放文本宽度和高度:
<size width="2" height="2">Double-size text</size>
<size width="2" height="1">Wide text</size>
<size width="1" height="2">Tall text</size>
表格布局
使用 <row> 和 <col> 用于对齐列:
<row>
<col width="24">Item name</col>
<col width="8" align="right">Qty</col>
<col width="16" align="right">Price</col>
</row>
列宽以字符为单位。使用 width="*" 设置一个弹性列来吸收剩余空间,这样模板无需修改即可适配不同纸张宽度:
<row>
<col width="*">{{name}}</col>
<col width="12" align="right">{{line_total_display}}</col>
</row>
分隔符和间距
<line /> <!-- Default single rule -->
<line style="double" /> <!-- Printer-native double rule -->
<line style="dashed" /> <!-- Character dashes across the width -->
<line style="dotted" /> <!-- Character dots across the width -->
<feed lines="2" /> <!-- Blank lines -->
<line/> (或 style="single") 和 style="double" 打印打印机的原生分隔线。 dashed 和 dotted 在当前列宽内打印基于字符的分隔符,用于在热敏预览截图中保留可见分隔线。
打印机命令
<cut /> <!-- Full paper cut -->
<cut mode="partial" /> <!-- Partial cut (leaves a tab) -->
<drawer /> <!-- Open cash drawer -->
条形码和二维码
<barcode type="code128" height="40">{{order.number}}</barcode>
<barcode type="qrcode" scale="3">{{fiscal.qr_payload}}</barcode>
示例:简单的 80mm 收据
<receipt paper-width="48">
<align mode="center">
<size width="2" height="2">{{store.name}}</size>
</align>
<feed lines="1" />
<align mode="center">
<text>{{store.address_1}}</text>
<text>{{store.city}} {{store.state}} {{store.postcode}}</text>
{{#store.phone}}<text>{{store.phone}}</text>{{/store.phone}}
</align>
<line />
<text>Order: #{{order.number}}</text>
<text>Date: {{order.created.datetime}}</text>
{{#cashier.name}}<text>Cashier: {{cashier.name}}</text>{{/cashier.name}}
<line />
<!-- Column headers -->
<row>
<col width="*"><bold>Item</bold></col>
<col width="4" align="right"><bold>Qty</bold></col>
<col width="12" align="right"><bold>Total</bold></col>
</row>
<line />
<!-- Line items -->
{{#lines}}
<row>
<col width="*">{{name}}</col>
<col width="4" align="right">{{qty}}</col>
<col width="12" align="right">{{line_total_display}}</col>
</row>
{{/lines}}
<line />
<!-- Totals -->
<row>
<col width="*">Subtotal</col>
<col width="16" align="right">{{totals.subtotal_display}}</col>
</row>
{{#totals.tax_total}}
<row>
<col width="*">Tax</col>
<col width="16" align="right">{{totals.tax_total_display}}</col>
</row>
{{/totals.tax_total}}
<row>
<col width="*"><bold>TOTAL</bold></col>
<col width="16" align="right"><bold>{{totals.total_display}}</bold></col>
</row>
<line />
{{#payments}}
<row>
<col width="*">{{method_title}}</col>
<col width="16" align="right">{{amount_display}}</col>
</row>
{{#tendered}}
<row>
<col width="*">Tendered</col>
<col width="16" align="right">{{tendered_display}}</col>
</row>
<row>
<col width="*">Change</col>
<col width="16" align="right">{{change_display}}</col>
</row>
{{/tendered}}
{{/payments}}
<feed lines="2" />
<align mode="center">
<text>Thank you for your purchase!</text>
</align>
<feed lines="3" />
<cut />
</receipt>
星号宽度列
width="*" 功能使模板不依赖纸张宽度。无需为特定纸张尺寸硬编码列宽,而是对需要拉伸的列使用 *:
<!-- Works on 58mm (32 char) AND 80mm (48 char) printers -->
<row>
<col width="*">{{name}}</col>
<col width="10" align="right">{{line_total_display}}</col>
</row>
在 80mm 打印机(48 个字符)上,商品名称可获得 38 个字符。在 58mm 打印机(32 个字符)上,则可获得 22 个字符。固定列在两者上保持相同大小。
模板预览
模板编辑器会在编辑时显示实时热敏预览。预览会将 XML 渲染为带样式的等宽 HTML,模拟收据打印在纸上的效果。更改会在短暂延迟后更新(300ms 防抖)。
同一个模板会同时生成预览和 ESC/POS 打印机输出,没有单独的“打印”模板。条形码和二维码在预览中渲染为内联 SVG,在打印机上则渲染为原生 ESC/POS 命令(或光栅图像)。
编写提示和注意事项
热敏打印有一些 HTML 中不存在的陷阱。以下是作者最常遇到的问题。
将带样式的标题包在 <text> 中以换行
带样式的容器 — <bold>, <size>, <underline>, <align> — 本身不会输出换行。只有 <text> 和块级元素(<line/>, <row>, <feed>)会。
<!-- ❌ Bug: the heading runs together with whatever follows -->
<bold>{{i18n.bill_to}}</bold>
{{customer.name}}
<!-- ✅ Fix: wrap the inner content in <text> -->
<bold><text>{{i18n.bill_to}}</text></bold>
<text>{{customer.name}}</text>
随附的 80mm 图库模板会对每个标题使用这种模式。
避免在窄行内使用 <size width="2">
双倍宽度文本会使有效字符数翻倍。在 80mm 打印机上能放下的标题,在 42 列通用 80mm 打印机上可能会溢出,而在 58mm 纸张上只会留下 16 个字符。
对于醒目的值(大额合计、厨房订单号),请输出单独的缩放行,而不是将其包在多列中 <row>:
<!-- For store names, prefer normal-width, double-height -->
<bold><size height="2"><text>{{store.name}}</text></size></bold>
<!-- For a bold total, put it on its own line above the row -->
<align mode="right">
<bold><size height="2"><text>{{totals.total_display}}</text></size></bold>
</align>
使用 width="*" 创建与纸宽无关的布局
width="*"(星号)列会吸收固定宽度列之外的剩余宽度。因此,同一个模板无需修改即可在 32 列(58mm)、42 列(80mm 标准)和 48 列(80mm 宽幅)打印机上正确渲染:
<!-- Works on 58mm AND 80mm without changes -->
<row>
<col width="*">{{name}}</col>
<col width="10" align="right">{{line_total_display}}</col>
</row>
如果确实使用硬编码的数字宽度,请按 42 进行分配,而不是 48。总和为 48 的行会在常见的 42 列 80mm 打印机上换行。
<align> 块内居中的带样式标题
在 <align mode="center"> (或 right), 直接使用带样式的标题 — <bold>, <size>, <underline>, <invert> 直接放在其中 <align> — 后面跟着另一行时,会自动闭合到其独立的一行。居中缩放的店铺名称位于居中的地址行上方,即使没有显式的 <text> 换行。
在对齐块之外,保留 <text> 换行。
ESC/POS 标点规范化
当打印机语言为 ESC/POS 时,编码器会在写入前将排版标点规范化为安全的 ASCII:
- 短破折号、长破折号、数字破折号、Unicode 减号 →
- - 弯引号 → 直引号
- 不换行空格 → 普通空格
因此,Mon–Sat 9:00–18:00 和 "open" 即使在没有 Unicode 字体的打印机上也能正确打印。Star 打印机(star-line / star-prnt)保留原始排版,因此编写内容时应使用打印机字体支持的字符。
非拉丁和从右到左书写的文字
热敏打印机使用内置字体和代码页打印文本,因此阿拉伯语、希伯来语、波斯语、乌尔都语以及其他非拉丁文字只有在打印机设置为匹配的代码页(例如阿拉伯语使用 CP864 / Windows-1256)时才能正确打印,否则会出现空白或乱码。
这些文字的可靠做法是使用 完整收据光栅化,它会将整张收据渲染为图像,因此无论打印机内置字体如何,都能按设计效果打印。请参阅打印机设置了解如何启用光栅模式。
热敏打印机无法打印整页 HTML 模板,打印任务必须渲染为 ESC/POS 或 Star 命令,而 HTML 无法表达这些命令。热敏硬件请使用热敏模板;如需整页 A4/Letter HTML,请打印到系统/PDF 打印机,或通过 PrintNode 打印。
徽标和图像
使用 <image> 来嵌入徽标:
<align mode="center">
<image src="data:image/png;base64,..." />
</align>
WCPOS 会在客户端对图像进行光栅化:解码、将透明区域展平为白色、调整尺寸以适应打印机的点数预算、转换为单色(徽标使用 Atkinson 抖动,条形码使用阈值处理),然后发送 ESC/POS 或 Star 图像命令。
- 点数预算:58mm 宽度为 384 点,80mm 宽度为 576 点。
- 接受的来源:
data:image/png和data:image/jpeg数据 URL、绝对http(s)URL,以及同源根相对路径。协议相对的//、反斜杠以及百分号编码的..遍历都会被拒绝。 - 推荐格式:高对比度 PNG,使用透明或白色背景。JPEG 也可使用,但压缩伪影可能会打印成噪点。
- 原始热敏输出尚不支持 SVG。
提示
- 从图库模板开始——图库中的热敏模板使用了以上所有模式,并已通过真实打印机验证。
- 如果你的店铺使用不同的打印机,请测试两种纸张尺寸;也可以坚持使用
width="*"列。 - 货币请使用
_display字段——它们已支持本地化格式。 - 保持简单——热敏打印在设计上比 HTML 的格式化工具更少。多依靠
<row>+<col width="*">让打印机完成自己的工作。