# @12-apps/thermal-printing 中文文档

规范地址：https://npmcn.com/package/%4012-apps/thermal-printing/

版本：1\.4\.0
npm 元数据更新时间：2026-10-19T12:44:34.497Z
中文译文更新时间：2026-11-05T19:55:12.975Z
软件包许可：MIT

本文由上游 README 翻译，可能存在偏差，请结合原文核对 API、命令与配置。版权与许可归原作者所有。

原始 npm 页面：https://www.npmjs.com/package/%4012-apps/thermal-printing
原文对照：https://npmcn.com/package/%4012-apps/thermal-printing/?lang=original
项目主页：https://github.com/12-apps/shared-packages#readme
源码仓库：https://github.com/12-apps/shared-packages

## 关键信息（来自 npm 元数据）

- 包体积（解压后）：183 kB，28 个文件
- 模块格式：ES Module
- TypeScript 类型：未声明
- Node.js 要求：\>=22\.0\.0
- 运行依赖：0 个
- peer 依赖：electron \>=30\.0\.0
- 首次发布：2026-08-27T15:15:36.906Z
- 最新版本发布：2026-10-19T12:44:33.877Z
- 近 30 天下载量：32912（2026-10-05 至 2026-11-03，npm 官方下载统计）
- 已发布版本数：8（近 12 个月 8 个）

## 中文简介

两种印刷方式的收据打印都附有一个热打印机,放在一个固定宽度的文件后面。 Isomorphic line model and slaping (./),四种类型大小,倒行带,盒,标签/挂行和光栅图像,ESC/POS编码器及其CP850代码

## 安装

```sh
npm install @12-apps/thermal-printing
```

也可以使用：`pnpm add @12-apps/thermal-printing`、`yarn add @12-apps/thermal-printing`、`bun add @12-apps/thermal-printing`

## README

# @ 12- apps/ 热印刷

两种印刷方式的收据打印 **一个** 固定宽度文档.

```bash
pnpm add @12-apps/thermal-printing
```

## 问题解决

接收打印机到达主机的方式有两种,主机无法到达 选择商店所购买的 :

|  | **网络** (Wi-Fi /以太网) (中文(简体) ) | **当地** (USB有线电视) |
| --- | --- | --- |
| 如何达到 | 初级ESC/POS,通过TCP插座, `:9100` 以公约方式 | 它被插入的机器上的浏览器,通过OS打印 |
| 谁开的 | 服务器 : `./net` | 标签 `./html` |
| 没有打开 | 静态打印 | 静静停止: USB 设备没有地址 |
| 成功意味着什么 | 字节离开进程 | 文档已到达操作系统 |

天真的形状是两条代码路径,成本首次出现在商店 将一种打印机换成另一种打印机: 票会变, 因为有秒 路径是第二个布局。 所以这里的分割是 **一个文档,两个编码器**。 。 。 .

```
       host builds            this package encodes
   ┌──────────────────┐      ┌─────────────────────┐
   │  TicketLine[]    │─────▶│ ./escpos → bytes    │──▶ socket (./net)
   │  (line, field,   │      │ ./html   → document │──▶ a tab prints it
   │   rule, wrap)    │      └─────────────────────┘
   └──────────────────┘
```

两者的布局都具有权威性. `./html` 将同一行改为 宽度完全相同的单空间列,而不是设置一个 `<table>` —— 说 这就是一揽子方案拒绝的分歧.

## 它不会做的

**它没有副本.** 没有一句话,在任何语言。 发送返回失败 (单位:千美元) `reason` 代码和 `host:port` 它的目标是: 主机将它变成 说话,用读屏幕的人的语言 两个主机可以说: 10.0.0.9的打印机没有回答,"厨房打印机离线了"来自 同样的事件.

**这不是你的票.** 门票上写着什么 顺序是什么 主机的产品决定——也是主机自己的词汇,货币的地方 和时区直播。 这个软件包给你下面的算术:列 数来数去,用一个被吊住的缩进来包裹, `Label: value` 该字段 当没有值时消失.

**它没有模型和路线.** 没有Prisma片面的,没有什么 挂载.

## 表面

| 导出 | 这是什么 |
| --- | --- |
| `@12-apps/thermal-printing` | 线路型号—— `TicketLine`, (中文(简体) ), `columnsFor`, (中文(简体) ), `wrap`, (中文(简体) ), `line`, (中文(简体) ), `centered`, (中文(简体) ), `rule`, (中文(简体) ), `field`, (中文(简体) ), `PAPER_WIDTHS_MM`,则 [参考表格词汇](https://npmcn.com/package/%4012-apps/thermal-printing/#readme-the-vocabulary-four-sizes-bands-boxes-rows-pictures)数字 : `textLines`, (中文(简体) ), `band`, (中文(简体) ), `box`, (中文(简体) ), `row`, (中文(简体) ), `image`, (中文(简体) ), `LINE_SIZES`, (中文(简体) ), `printableDotsFor`, (中文(简体) ), `textWidth`纯正,异形. |
| `…/escpos` | `encodeTicket(lines)` → `Uint8Array`初始化、CP850、对齐、强调、字体和放大、倒转、行距、, `GS v 0` 光栅,饲料,部分切. `encodeDrawerPulse(options?)` 五字节 `ESC p m t1 t2` 打开现金抽屉 连接到打印机 单独或附在票上. |
| `…/html` | `renderTicketHtml(lines, paperWidthMm, lang?)` → 一个独立的文档,正好是卷可打印的宽度(72毫米为80分,48毫米为58分),纯黑色为白色,有: `@page { margin: 0 }`。 。 。 . `rasterToDataUri(raster)` 1位图片作为 `data:` URI,用于预览. |
| `…/raster` | `rasterizeSvg(svg, { width })` + 打印机点大小的 RGBA; `toMonochrome(rgba, options?)` + 可打印的 1 位 `RasterImage`纯正的,异形的,没有帆布和本地模块. |
| `…/net` | `sendToNetworkPrinter(host, port, bytes, options?)`。仅节点(`node:net`),从不扔出,结构化失败. |
| `…/routing` | `printerRoute(printers)` 编号 : `printerFor(route, destinationId)` ——带有默认值,通用于您自己的打印机行. |
| `…/discovery` | `scanForPrinters(options?)` 编号 : `probePrinter(host, port?)` 找到没人记的地址 唯节. |
| `…/discovery/bridge` | `startDiscoveryBridge({ allowedOrigins })` ——一个回回回服务器 HTTPS 页面可能会调用以运行扫描. 唯节. |
| `…/electron` | `printHtml({ html, deviceName, paperWidthMm?, session? })` ——外屏窗口,无声打印到已命名设备(或默认),后被销毁. 页面总是被命名,是一卷: `paperWidthMm` 宽(取出:最窄的一卷,可打印宽度可保留已铸出的尸体)并高至票面,放入可打印区域。 没有它 Electron 向司机询问 A4(FUT-3009). 拒绝拒绝 `PrintRefusedError` (单位:千美元)`reason: string | null`从来没有句子. `electron` 是一个可选的对等点. |
| `…/discovery/cli` | `runFinder({ allowedOrigins })` 编号 : `main(argv, env)` ——帮助商家下载,作为捆绑的功能. 唯节. |

`./net` 财务报告和审定财务报表 `./discovery` 坐在他们自己的子路径后,以便导入 编码器输入浏览器捆绑中,绝不拖动 `node:net` 在,和 `./electron` 在它自己的背后, 使其他任何进口 `electron`。 。 。 .

## 词汇: 四大小,带,框,行,相

收据是文字的多个大小. 下面的建筑商 覆盖什么 设计票的使用,每张票都生产 **纯文本加一面旗帜**, (中文(简体) ), 因此,两个编码器在同一地方打印相同的字符: 这里加一个盒子,这里画一个乐队,这里以一个乐队为中心.

```ts
import { band, box, image, row, rule, textLines, columnsFor } from '@12-apps/thermal-printing';

const w = printer.paperWidthMm; // 58 or 80
const lines = [
  image(logo),                                                     // a RasterImage, centred
  ...textLines(copy.headline, w, { size: 'xlarge', bold: true, align: 'center' }),
  ...band(copy.paidBanner, w),                                     // white on black, edge to edge
  ...row(item.label, money(item.totalCents), w, { indent: 3 }),    // amount flush right
  rule(columnsFor(w)),
  ...row(copy.total, money(totalCents), w, { size: 'large', bold: true }),
  ...box(copy.bringChange, w),                                     // ┌──┐ │ … │ └──┘
  ...textLines(copy.footer, w, { size: 'small', align: 'center' }),
];
```

体型高的建筑师们 **纸张宽度**,而不是列数 : 尺寸每卷有四个数,建造者知道它的大小 需要帮助。 遗产 `line`, (中文(简体) ), `centered`, (中文(简体) ), `rule` 财务报告和审定财务报表 `field` 不变,a 只使用它们编码的门票 准确的字节.

### 四个尺寸,以及它们如何映射到ESC/ 业务干事

ESC/POS没有分数大小——它有两个内置字体和整数 放大相会. 每个大小都是一个字体,一个倍数,所有四个可以达到 `ESC !` 单独( 此类中每个打印机的指令集部分) :

| 大小 | 参考步骤 | 字体 | 放大 | 单元格(点数) | 格力高度 | 58毫米炮 | 80毫米纵列 |
| --- | --- | --- | --- | --- | --- | --- | --- |
| `small` | P-细节 | 页:1 | 1×1 (单位:千美元) | 9×17 (中文(简体) ) | 六点 | 第42条 | 64国道 |
| `medium` | M——案文 | A 类 | 1×1 (单位:千美元) | 12x24 (中文(简体) ) | ≈ 8.5平方公尺 | 第32条 | 第48条 |
| `large` | G——大事记(`TOTAL`(中文(简体) ) | 页:1 | 2×2 (韩语) | 18x34 (中文(简体) ) | 十二点 | 21国 | 第32条 |
| `xlarge` | GG——标题 | A 类 | 2×2 (韩语) | 24×48 (中文(简体) ) | \* 17平方公尺 | 16个 | 24个 |

`medium` 财务报告和审定财务报表 `large` 匹配8.5平方公尺和12平方公尺,几乎精确于8个点/毫米. `small` 是字体 B,是字体 A 下的唯一步骤. `xlarge` 是最大的一步可以达到 无 `GS !`;下一个(3×3)将留下16个80毫米和10个纵列 58毫米,在窄卷上不能有典型的头条.

**为何这些尺寸使用双宽度 `double` 从来没有.** 遗产 `double` 下划线为双倍 _高度_ 只因为有一条线包裹在48分 列并印出两倍宽的无声再包装24. 危险是 列数与放大不一致。 大小的一行带着它的大小, `columnsFor(paperWidthMm, size)` {\\fn黑体\\fs22\\bord1\\shad0\\3ahbe\\4ah00\\fscx67\\fscy66\\2chffffff\\3ch808080}答案是 {\\fn黑体\\fs22\\bord1\\shad0\\3ahbe\\4ah00\\fscx67\\fscy66\\2chffffff\\3ch808080}建设者们会把答案包起来 计数——所以宽度和高度被放大在一起,一个只代表 比较高的(读作收缩的)被避免.

HTML 文档复制了列数而不是点数大小: 车体为可打印宽度,以毫米为单位(80毫米为72毫米,58毫米为48毫米) 其类型大小 `columnsFor(paperWidthMm)` 字符填充——一栏是 1.5毫米,打印机自有的字体 A 单元格——和有 N 列的大小设定为 (体列/N)em - `small` 0.75 计, `large` 1.5 计, `xlarge` 2毫米80毫米口径 A 类 线条在屏幕上和纸上都包裹在同一处,没有到达 边框头不能打印 。 文字是纯黑色的,取出一个更厚的点 因此,一个常量的干线印是坚固的,而不是被抖动的灰色 按每个点一个图像像素绘制( e)`width / 8` 毫米),未出声.

`LINE_SIZE_METRICS` 财务报告和审定财务报表 `FONT_CELL_DOTS` 导出表格,用于想要的主机 以绘制自己的预览.

### 带和箱

-   **`band`** 倒转( e)`GS B 1`),加入到整列数中,这样 黑色从卷的一边跑到另一边——一个倒印的空间是 一个黑牢房。 在 HTML 中是 `background:#000;color:#fff` 与 `print-color-adjust:exact`,否则浏览器在 打印和乐队 出白色的白色.
-   **`box`** 以 CP850 所携带的框绘字符绘制(`┌─┐│└┘`, (中文(简体) ), 由于 ESC/POS 没有矩形命令; 它在每个打印机上打印并制作 相同在浏览器中.
-   两者均关闭行距(`ESC 3 n`, n = 单元格高度),所以多行 带子是一个块和一个盒子的垂直相接; `ESC 2` 后还取.
-   两者默认为 **粗体** - 他们在那里被注意;通过 `bold: false` 选择退出.

> **纸上验证的硬件假设 :** `ESC 3 n` 改为 `n` 点, 它持有203-dpi级的这个包目标(Bematech MP4200, Epson TM-T20 and also,其中线间距运动单元为一点. A 类 垂直运动单元不同的打印机(由 `GS P`,或一个 180/300-dpi头)将比单元格更紧放或更松动; 在依靠它们之前,先检查一个带和一个盒子,然后检查实际模型.

### 行数

`row(label, amount, paperWidthMm, { size?, bold?, indent? })` 计出金额 右冲上 **第一个** 线条并包装旁边的标签(在 可选悬挂缩进). 这么长的金额 标签会低于三分之一 线条移动到标签下方的一行,仍然向右冲出. 金额从不分割或截取:比整行打印宽一个 整个在它自己的线上,并运行过边缘(打印机把它包起来),因为一个 切数是错的.

**宽是 NFC 文本的代码点** (单位:千美元)`textWidth`页:1 每个建筑工: emoji 算一列和分解 `é` 成员人数 先说 这就是ESC/POPS编码器的写法 – 每张CP850字节 代码点, `?` 对于一个无法实现的——那么校正就停留在纸上。 浏览器 可能绘制比 HTML 预览中一个单空间单元格宽的emoji .

### 图片

A 类 `RasterImage` 实值 `{ width, height, data }` 在打印机中 **点** (8个每毫米) 从上到下, `Math.ceil(width / 8)` 每个字节,MSB最左边,1=黑色—— 没错 `GS v 0`格式,所以编码器复制它, 最多255条 作行相会. 疯狂在 `printableDotsFor(paperWidthMm)` 用于全宽标志( 576) 80毫米上点,58毫米上点384个)或为标记而更小;编码器都没有再作缩放. 在 HTML 中,相同的位成为 1- 位 BMP `data:` URI(没有帆布,没有降压,没有 相同代码在节点和一个浏览器中,每个字符大小为12个点,因此需要 纸张上的屏幕宽度 `image-rendering: pixelated`。 。 。 .

### 将图片降为一分( N)`./raster`(中文(简体) )

热头有一墨. **50%的门槛值是错误的默认值**: 品牌 颜色大多是浅色的——一种橙色像 `#ED7A1F` 润滑油有0.57分 所以 标记在一个指纹中只显示其最黑暗的细节,或者没有.

```ts
import { rasterizeSvg, toMonochrome } from '@12-apps/thermal-printing/raster';
import { image, printableDotsFor } from '@12-apps/thermal-printing';

// A vector mark, drawn at the size it will print (12 mm = 96 dots):
const mark = toMonochrome(rasterizeSvg(markSvg, { width: 96 }));
// A bitmap logo the host decoded to RGBA at the dot width (canvas, sharp, …):
const logo = toMonochrome({ width, height, data: rgba });

lines.unshift(image(logo));
```

**政策** (单位:千美元)`mode: 'auto'`,默认值 :

-   **平面艺术** ——几件坚固的颜色,即标志或标志是什么 **临界值为 0.75**。所有者所看到的:橙色、红色、蓝色或 绿色的标志印像一个 白色细节被敲倒了的清晰的黑色相片 出; 淡色( 淡黄色, 贴面, 淡灰色- luma 0. 75 and up) 不见了。 显示预览( E) (`rasterToDataUri`在纸张之前.
-   **一张相片** ——许多颜色——是 **抖动** (阿金森;弗洛伊德-) 斯坦伯格语Name 因此音调变成点密度。 一个橙色的区域出来作为罚款 黑色40%左右,不是黑色固体.
-   透明的像素是纸张 。 结果 `method` 说哪个分行跑了.

`mode: 'threshold' | 'dither'`, (中文(简体) ), `threshold` 财务报告和审定财务报表 `kernel` 推翻选择.

**为什么SVG光栅是小的.** 完全忠诚的选择是本地的 模块`resvg-js`, (中文(简体) ), `sharp`: 每个平台的二进制且浏览器中无任何内容) 或 a Web Assemblysis(兆字节和Async init)——在一个没有 完全依赖。 单色打印机的复制量是平整的 一个板块的标记是平面填满的,所以 `rasterizeSvg` 绘图 确切地说,反异名: `path` (每个命令), `rect` (圆), `circle`, (中文(简体) ), `ellipse`, (中文(简体) ), `polygon`, (中文(简体) ), `polyline`, (中文(简体) ), `g`, (中文(简体) ), `transform`, (中文(简体) ), `fill`, (中文(简体) ), `fill-rule`, (中文(简体) ), `fill-opacity`, (中文(简体) ), `opacity`, (中文(简体) ), `viewBox` 财务报告和审定财务报表 `preserveAspectRatio` (见否. 不论它不画, 中风,梯度,文本, `<image>`, (中文(简体) ), `<use>`, (中文(简体) ), 剪接、口罩、过滤器、, `<style>` - 这个 **名称** 内 `unsupported`,所以一个主机 可以拒绝文件或自行光滑 并进来 `toMonochrome`。 。 。 . 解码PNG/JPEG是故意留给主机的,主机已经有一个解码器了.

**上传的 SVG 是不信任的输入**因此,它可以要求的每一个成本是上限, 任何封顶上的文档 **拒绝** - 0×0结果,原因在 `unsupported` ——从未投入和分配:

| 限制 | 价值 | 原因在其中 `unsupported` |
| --- | --- | --- |
| 源长度, `MAX_SVG_LENGTH` | 256 000个字符 | `too large` |
| 输出, `MAX_RASTER_WIDTH` × `MAX_RASTER_HEIGHT` | 576× 2048个点(在分配前检查,包括取自视图框的高度) | `output too large` |
| 形状, `MAX_SHAPES` | 4096 (英语) | `too many shapes` |
| 扁平的顶点, `MAX_PATH_POINTS` | 250 000美元 | `too many path points` |
| 扫描转换工作, `MAX_FILL_WORK` | 50 000 000单位, **从任何行被填充之前的边缘估计** (对接子行×对数2边,每边框像素外加5个外加5个外加-正上方的边框) | `too complex to fill` |

文档由手写线性扫描仪读取(无回路正则) 表达式,因此,任何精心设计的注释、属性或数字都无法解析 ),每个形状只在覆盖的像素上复合 每个行. `toMonochrome` 检查它的输入: 并非精确的缓冲 `width × height × 4` 字节或尺寸超过576×2048,抛出一个 `RangeError` 在分配任何内容之前——这些维度是主机在之后的选择 解码,所以不匹配是呼叫器错误而不是上传到的东西.

## 找到一个打印机 没人写下地址

设置屏幕要求店主提供 IP 地址是要求公平 一个网络工程师和一个不公平的卖午餐的人的问题: 地址是由他们从未登录过的路由器传送到设备上的 没有屏幕. `./discovery` 扫描本地网络, 以查看设备 打印端口和其发现的分级.

**打开的端口无法证明设备的存在 .** 打印冲洗机,一秒钟 服务器或无关的电器都可能在9100上回答. 所以探测器问 真正的ESC/POPS问题一旦连接起来—— `DLE EOT 1`,实时状态,其中 打印什么也没有,并且可以安全地发送到一个非打印器上 —— 并等级一个设备 答题 `confirmed` 对一个只是打开了港口, `candidate`。 。 。 . 沉默从不被视为反对打印机的证据,因为很多 此硬件根本不执行状态 。 操作员没有选择: 每一个候选人都被送回,测试页仍然是唯一的证据.

**浏览器无法完成此任务**,这是什么 `./discovery/bridge` 这是为. 无法获取 HTTPS 页面 `http://192.168.0.50` ——内容不一——甚至 chrome的私人网络访问规则要求飞行前 9100上的打印机无法回答 。 循环是例外: `127.0.0.1` 实值 _可能值得信赖_所以有个帮手在商家的机器上跑 可以进行扫描,并将答案交给标签.

这个服务器可以被商家打开的每一个页面所达到,所以可以绑定 仅回回转, 答案 **只有起源的开始**,然后在 a 上退出 TTL (英语). 来源许可列表是真实的控制——浏览器设置 `Origin` 自己和一个页面无法伪造, TTL 将它必须的窗口绑起 错进来。 没有两者,这是端口扫描仪 任何网站都可能瞄准 商店网. `runFinder` 因此 **拒绝从没有来源开始**数字 : 不存在安全默认,所以不存在默认.

### 包装助手

`./discovery/cli` 是一个函数,而不是二进制,因为此软件包会发布 TypeScript源和一个主机的发行是自己的. 一个品牌的建筑捆绑 `main()` 把它包装在每一个月台上, 把它自己的发源地烘烤在 结束 `PRINTER_FINDER_ORIGINS` 所以商家双击一个图标而不是 输入旗帜时 :

```bash
esbuild --bundle --platform=node --format=cjs --outfile=finder.cjs \
  --banner:js='process.env.PRINTER_FINDER_ORIGINS ||= "https://admin.example.com"' \
  your-entry.cjs
```

**一个二进制需要签名 才能被人们使用.** 未签名 `.exe` 满足 Windows SmartScreen 的"未识别应用"壁和未注释 `.app` 门卫直接拒绝——对店主来说 此特性结束 。 这是证书和释放管道 不是密码.

## 三件容易出错的事

**密码页.** 打印机不讲 UTF-8 —— 它会打印每个字的格子 字节。 发送一个重音单词 因为UTF-8 将两个字符放在一个属于, 每个打印机上,看起来完全像字体问题. `./escpos` 选择 CP850 并绘制一张票所携带的拉丁字母 {\\fn黑体\\fs22\\bord1\\shad0\\3ahbe\\4ah00\\fscx67\\fscy66\\2chffffff\\3ch808080}在回到 `?`因为一个词 有人 仍然可以读取比 `?` 中语.

**"申"不是"被印".** 一个原始的上面没有协议 `:9100` 套接字: 无 握手,没有承认,没有状态框架。 一个空卷子,一个果酱,和一个 完美的门票是同样的成功——和 `window.print()` 返回时 对话被解除,这是没有更好的。 一个报告"印刷"的主持人正在制作 承诺运输从来没有做出。 给接线员重印一次.

**拥有打印机的目的地由它来回答,或者一无所获.** 切换 一个房间的打印机关闭 必须停止 房间的门票, 而不是悄悄地改变路线 在别的地方 出错房间的罚单是无人能及的 房间永远看不到. `./routing` 将不活动的打印机准确保存在地图中, 以便 "这里没有打印机"和"这里的打印机关闭"都保持不同的答案.

## 收 养

见 [采用.md](https://npmcn.com/package/%4012-apps/thermal-printing/ADOPTING.md)。 。 。 .

## 常见问题

### @12-apps/thermal-printing 是什么？

两种印刷方式的收据打印都附有一个热打印机,放在一个固定宽度的文件后面。 Isomorphic line model and slaping \(\./\),四种类型大小,倒行带,盒,标签/挂行和光栅图像,ESC/POS编码器及其CP850代码

### 如何安装 @12-apps/thermal-printing？

在项目目录运行 npm install @12-apps/thermal-printing，或使用 pnpm add / yarn add / bun add。

### @12-apps/thermal-printing 支持 TypeScript 吗？

该包的 package\.json 没有声明类型入口。需要类型提示时，可以在 npm 上查找社区维护的 @types/12-apps\_\_thermal-printing 是否存在。

### @12-apps/thermal-printing 支持 ESM 还是 CommonJS？

以 ES Module 形式发布，推荐使用 import 引入。在 CommonJS 项目中，Node\.js 20\.19\+ / 22\.12\+ 可以直接 require\(\) 不含顶层 await 的 ES Module，更早的版本需要改用动态 import\(\)。

### @12-apps/thermal-printing 需要什么版本的 Node\.js？

package\.json 的 engines 字段要求 Node\.js \>=22\.0\.0。版本不符时 npm 默认只给出警告，开启 engine-strict 后会拒绝安装。

### @12-apps/thermal-printing 可以商用吗？

@12-apps/thermal-printing 使用 MIT 许可。宽松许可：允许商业使用、修改和再分发，通常只需在分发时保留原版权与许可声明。以上为概括，不构成法律意见，请以许可原文为准。

### @12-apps/thermal-printing 的下载量有多少？

根据 npm 官方下载统计，@12-apps/thermal-printing 在 2026/10/05 – 2026/11/03 这 30 天内被下载 32,912 次，日均约 1,097 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入，适合横向比较热度，不能直接等同于使用人数。

### @12-apps/thermal-printing 还在维护吗？

最新版本 1\.4\.0 发布于 2026/10/19（19 天前）；至今共发布 8 个版本，其中过去 12 个月发布了 8 个；首次发布于 2026/08/27。发布频率只是参考信号：功能稳定的工具可能很少需要更新，建议结合源码仓库的 issue 和提交记录判断。

