@ 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纯正,异形. |
…/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。 。 。 .
词汇: 四大小,带,框,行,相
收据是文字的多个大小. 下面的建筑商 覆盖什么 设计票的使用,每张票都生产 纯文本加一面旗帜, (中文(简体) ), 因此,两个编码器在同一地方打印相同的字符: 这里加一个盒子,这里画一个乐队,这里以一个乐队为中心.
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分 所以
标记在一个指纹中只显示其最黑暗的细节,或者没有.
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 所以商家双击一个图标而不是
输入旗帜时 :
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。 。 。 .