@12-apps/thermal-printing

中文文档
1.4.0发布于 2026/10/19MITES Module零依赖构建来源已验证近 30 天下载 3.3万

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

中文使用文档
文档目录13 节
  1. 问题解决
  2. 它不会做的
  3. 表面
  4. 词汇: 四大小,带,框,行,相
  5. 四个尺寸,以及它们如何映射到ESC/ 业务干事
  6. 带和箱
  7. 行数
  8. 图片
  9. 将图片降为一分( N) ./raster (中文(简体) )
  10. 找到一个打印机 没人写下地址
  11. 包装助手
  12. 三件容易出错的事
  13. 收 养

@ 12- apps/ 热印刷

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

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

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

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

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

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毫米纵列
smallP-细节页:11×1 (单位:千美元)9×17 (中文(简体) )六点第42条64国道
mediumM——案文A 类1×1 (单位:千美元)12x24 (中文(简体) )≈ 8.5平方公尺第32条第48条
largeG——大事记(TOTAL(中文(简体) )页:12×2 (韩语)18x34 (中文(简体) )十二点21国第32条
xlargeGG——标题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分 所以 标记在一个指纹中只显示其最黑暗的细节,或者没有.

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_LENGTH256 000个字符too large
输出, MAX_RASTER_WIDTH × MAX_RASTER_HEIGHT576× 2048个点(在分配前检查,包括取自视图框的高度)output too large
形状, MAX_SHAPES4096 (英语)too many shapes
扁平的顶点, MAX_PATH_POINTS250 000美元too many path points
扫描转换工作, MAX_FILL_WORK50 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 所以商家双击一个图标而不是 输入旗帜时 :

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。 。 。 .

版本与发布记录

@12-apps/thermal-printing 共发布 8 个版本,首次发布于 2026/08/27;过去 12 个月发布了 8 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
1.4.0latest2026/10/1919 天前
1.3.32026/09/291 个月前
1.3.22026/09/291 个月前
1.3.12026/09/271 个月前
1.3.02026/09/271 个月前
1.2.02026/09/261 个月前

关于 @12-apps/thermal-printing 的常见问题

@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 和提交记录判断。