跳到主内容
版本: 1.x

热敏打印机模板

热敏模板使用 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" 打印打印机的原生分隔线。 dasheddotted 在当前列宽内打印基于字符的分隔符,用于在热敏预览截图中保留可见分隔线。

打印机命令

<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/pngdata:image/jpeg 数据 URL、绝对 http(s) URL,以及同源根相对路径。协议相对的 //、反斜杠以及百分号编码的 .. 遍历都会被拒绝。
  • 推荐格式:高对比度 PNG,使用透明或白色背景。JPEG 也可使用,但压缩伪影可能会打印成噪点。
  • 原始热敏输出尚不支持 SVG

提示

  • 从图库模板开始——图库中的热敏模板使用了以上所有模式,并已通过真实打印机验证。
  • 如果你的店铺使用不同的打印机,请测试两种纸张尺寸;也可以坚持使用 width="*" 列。
  • 货币请使用 _display 字段——它们已支持本地化格式。
  • 保持简单——热敏打印在设计上比 HTML 的格式化工具更少。多依靠 <row> + <col width="*"> 让打印机完成自己的工作。