retail-print-kit 0.0.0-stage → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +209 -2
- package/bin/retail-print-kit.js +318 -0
- package/dist/browser/retail-print-kit.js +26 -0
- package/dist/browser/retail-print-kit.js.map +7 -0
- package/dist/browser/retail-print-kit.mjs +25 -0
- package/dist/browser.d.ts +15 -0
- package/dist/browser.js +35 -0
- package/dist/core/barcode1d.d.ts +12 -0
- package/dist/core/barcode1d.js +192 -0
- package/dist/core/bitmap.d.ts +31 -0
- package/dist/core/bitmap.js +126 -0
- package/dist/core/builder.d.ts +58 -0
- package/dist/core/builder.js +177 -0
- package/dist/core/encode.d.ts +8 -0
- package/dist/core/encode.js +17 -0
- package/dist/core/escpos-encoder.d.ts +4 -0
- package/dist/core/escpos-encoder.js +150 -0
- package/dist/core/identify.d.ts +22 -0
- package/dist/core/identify.js +71 -0
- package/dist/core/image.d.ts +13 -0
- package/dist/core/image.js +32 -0
- package/dist/core/ops.d.ts +78 -0
- package/dist/core/ops.js +2 -0
- package/dist/core/printer.d.ts +35 -0
- package/dist/core/printer.js +96 -0
- package/dist/core/rasterizer.d.ts +29 -0
- package/dist/core/rasterizer.js +199 -0
- package/dist/core/star-graphic-encoder.d.ts +12 -0
- package/dist/core/star-graphic-encoder.js +48 -0
- package/dist/core/star-line-encoder.d.ts +8 -0
- package/dist/core/star-line-encoder.js +119 -0
- package/dist/core/status.d.ts +22 -0
- package/dist/core/status.js +52 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.js +49 -0
- package/dist/label/builder.d.ts +39 -0
- package/dist/label/builder.js +37 -0
- package/dist/label/ezpl.d.ts +6 -0
- package/dist/label/ezpl.js +78 -0
- package/dist/label/index.d.ts +12 -0
- package/dist/label/index.js +42 -0
- package/dist/label/svg.d.ts +3 -0
- package/dist/label/svg.js +46 -0
- package/dist/label/template.d.ts +61 -0
- package/dist/label/template.js +33 -0
- package/dist/label/tspl.d.ts +3 -0
- package/dist/label/tspl.js +83 -0
- package/dist/label/types.d.ts +69 -0
- package/dist/label/types.js +10 -0
- package/dist/label/zpl.d.ts +3 -0
- package/dist/label/zpl.js +57 -0
- package/dist/preview/html.d.ts +14 -0
- package/dist/preview/html.js +132 -0
- package/dist/preview/index.d.ts +8 -0
- package/dist/preview/index.js +14 -0
- package/dist/preview/png.d.ts +3 -0
- package/dist/preview/png.js +89 -0
- package/dist/profiles/epson.d.ts +10 -0
- package/dist/profiles/epson.js +54 -0
- package/dist/profiles/index.d.ts +7 -0
- package/dist/profiles/index.js +36 -0
- package/dist/profiles/star.d.ts +7 -0
- package/dist/profiles/star.js +39 -0
- package/dist/profiles/types.d.ts +41 -0
- package/dist/profiles/types.js +2 -0
- package/dist/template/expr.d.ts +16 -0
- package/dist/template/expr.js +85 -0
- package/dist/template/index.d.ts +4 -0
- package/dist/template/index.js +27 -0
- package/dist/template/render.d.ts +8 -0
- package/dist/template/render.js +119 -0
- package/dist/template/types.d.ts +81 -0
- package/dist/template/types.js +2 -0
- package/dist/transports/discover.d.ts +21 -0
- package/dist/transports/discover.js +73 -0
- package/dist/transports/net.d.ts +20 -0
- package/dist/transports/net.js +72 -0
- package/dist/transports/serial.d.ts +20 -0
- package/dist/transports/serial.js +71 -0
- package/dist/transports/types.d.ts +16 -0
- package/dist/transports/types.js +41 -0
- package/dist/transports/usb.d.ts +30 -0
- package/dist/transports/usb.js +128 -0
- package/dist/util/base64.d.ts +2 -0
- package/dist/util/base64.js +38 -0
- package/package.json +133 -4
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.Printer = void 0;
|
|
18
|
+
const encode_1 = require("./encode");
|
|
19
|
+
const builder_1 = require("./builder");
|
|
20
|
+
const status_1 = require("./status");
|
|
21
|
+
__exportStar(require("./builder"), exports);
|
|
22
|
+
/** An `OpBuilder` bound to a transport: `open()`, record ops, `close()` sends them. */
|
|
23
|
+
class Printer extends builder_1.OpBuilder {
|
|
24
|
+
constructor(transport, profile, options = {}) {
|
|
25
|
+
super(profile);
|
|
26
|
+
this.transport = transport;
|
|
27
|
+
this.options = options;
|
|
28
|
+
this.opened = false;
|
|
29
|
+
}
|
|
30
|
+
async open() {
|
|
31
|
+
await this.transport.open();
|
|
32
|
+
this.opened = true;
|
|
33
|
+
if (this.options.initOnOpen !== false)
|
|
34
|
+
this.ops.unshift({ kind: 'init' });
|
|
35
|
+
return this;
|
|
36
|
+
}
|
|
37
|
+
/** Encodes and sends everything recorded so far, keeping the connection open. */
|
|
38
|
+
async flush() {
|
|
39
|
+
if (!this.opened)
|
|
40
|
+
throw new Error('retail-print-kit: call open() before flush()');
|
|
41
|
+
const ops = this.ops;
|
|
42
|
+
this.ops = [];
|
|
43
|
+
if (ops.length)
|
|
44
|
+
await this.transport.write((0, encode_1.encode)(ops, this.profile, this.options));
|
|
45
|
+
return this;
|
|
46
|
+
}
|
|
47
|
+
/** Flushes, then closes the transport. */
|
|
48
|
+
async close() {
|
|
49
|
+
try {
|
|
50
|
+
await this.flush();
|
|
51
|
+
}
|
|
52
|
+
finally {
|
|
53
|
+
this.opened = false;
|
|
54
|
+
await this.transport.close();
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/** Renders a template with data and appends the result. */
|
|
58
|
+
render(template, data) {
|
|
59
|
+
const { render } = require('../template/render');
|
|
60
|
+
return this.append(render(template, data, this.profile));
|
|
61
|
+
}
|
|
62
|
+
/** HTML preview of the recorded ops. */
|
|
63
|
+
toHtml(options) {
|
|
64
|
+
const { opsToHtml } = require('../preview/html');
|
|
65
|
+
return opsToHtml(this.ops, this.profile, options);
|
|
66
|
+
}
|
|
67
|
+
/** The bytes that would be sent for the recorded ops; useful for tests and previews. */
|
|
68
|
+
toBuffer() {
|
|
69
|
+
return (0, encode_1.encode)(this.ops, this.profile, this.options);
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Asks the printer for its state (ESC/POS DLE EOT 1..4). Flushes pending ops first so the
|
|
73
|
+
* reply is not mixed with receipt data. Needs a transport with `read`.
|
|
74
|
+
*/
|
|
75
|
+
async status() {
|
|
76
|
+
if (this.profile.set !== 'escpos' && this.profile.set !== 'star-line')
|
|
77
|
+
return status_1.UNSUPPORTED_STATUS;
|
|
78
|
+
if (!this.transport.read)
|
|
79
|
+
throw new Error(`retail-print-kit: ${this.transport.name} transport cannot read from the printer`);
|
|
80
|
+
await this.flush();
|
|
81
|
+
const timeout = this.options.statusTimeout ?? 1500;
|
|
82
|
+
if (this.profile.set === 'star-line') {
|
|
83
|
+
await this.transport.write(Buffer.from([0x1b, 0x06, 0x01]));
|
|
84
|
+
const reply = await this.transport.read(timeout);
|
|
85
|
+
return (0, status_1.parseStarStatus)(Array.from(reply));
|
|
86
|
+
}
|
|
87
|
+
const bytes = [];
|
|
88
|
+
for (const n of [1, 2, 3, 4]) {
|
|
89
|
+
await this.transport.write(Buffer.from([0x10, 0x04, n]));
|
|
90
|
+
const reply = await this.transport.read(timeout);
|
|
91
|
+
bytes.push(reply[0]);
|
|
92
|
+
}
|
|
93
|
+
return (0, status_1.parseEscPosStatus)(bytes);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
exports.Printer = Printer;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Op } from './ops';
|
|
2
|
+
import { Profile } from '../profiles/types';
|
|
3
|
+
import { Bitmap } from './bitmap';
|
|
4
|
+
/** Turns text ops into a page image for printers that only accept bitmaps (Star TSP100). */
|
|
5
|
+
export interface Rasterizer {
|
|
6
|
+
render(ops: Op[], profile: Profile): Bitmap;
|
|
7
|
+
}
|
|
8
|
+
interface Canvas2D {
|
|
9
|
+
width: number;
|
|
10
|
+
height: number;
|
|
11
|
+
getContext(kind: '2d'): any;
|
|
12
|
+
}
|
|
13
|
+
export interface CanvasFactory {
|
|
14
|
+
(width: number, height: number): Canvas2D;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Lays text out the way a line printer would (hard wrap at the column count, character cells of
|
|
18
|
+
* dots/columns width, 24-dot lines, size multipliers), then paints it with a monospace font on a
|
|
19
|
+
* canvas and thresholds to 1 bit. The canvas comes from `@napi-rs/canvas` or `canvas` unless a
|
|
20
|
+
* factory is given; Electron hosts can pass a factory that wraps `document.createElement('canvas')`.
|
|
21
|
+
*/
|
|
22
|
+
export declare class CanvasRasterizer implements Rasterizer {
|
|
23
|
+
private readonly factory;
|
|
24
|
+
private readonly fontFamily;
|
|
25
|
+
constructor(factory?: CanvasFactory, fontFamily?: string);
|
|
26
|
+
render(ops: Op[], profile: Profile): Bitmap;
|
|
27
|
+
}
|
|
28
|
+
export declare function loadCanvasFactory(): CanvasFactory;
|
|
29
|
+
export {};
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CanvasRasterizer = void 0;
|
|
4
|
+
exports.loadCanvasFactory = loadCanvasFactory;
|
|
5
|
+
const bitmap_1 = require("./bitmap");
|
|
6
|
+
const barcode1d_1 = require("./barcode1d");
|
|
7
|
+
const LINE_DOTS = 24;
|
|
8
|
+
/**
|
|
9
|
+
* Lays text out the way a line printer would (hard wrap at the column count, character cells of
|
|
10
|
+
* dots/columns width, 24-dot lines, size multipliers), then paints it with a monospace font on a
|
|
11
|
+
* canvas and thresholds to 1 bit. The canvas comes from `@napi-rs/canvas` or `canvas` unless a
|
|
12
|
+
* factory is given; Electron hosts can pass a factory that wraps `document.createElement('canvas')`.
|
|
13
|
+
*/
|
|
14
|
+
class CanvasRasterizer {
|
|
15
|
+
constructor(factory = loadCanvasFactory(), fontFamily = 'monospace') {
|
|
16
|
+
this.factory = factory;
|
|
17
|
+
this.fontFamily = fontFamily;
|
|
18
|
+
}
|
|
19
|
+
render(ops, profile) {
|
|
20
|
+
const width = profile.dots;
|
|
21
|
+
const items = layout(ops, profile);
|
|
22
|
+
const height = items.reduce((h, it) => Math.max(h, it.y + it.height), 0);
|
|
23
|
+
if (height === 0)
|
|
24
|
+
return (0, bitmap_1.emptyBitmap)(width, 0);
|
|
25
|
+
const canvas = this.factory(width, height);
|
|
26
|
+
const ctx = canvas.getContext('2d');
|
|
27
|
+
ctx.fillStyle = '#fff';
|
|
28
|
+
ctx.fillRect(0, 0, width, height);
|
|
29
|
+
ctx.fillStyle = '#000';
|
|
30
|
+
ctx.textBaseline = 'top';
|
|
31
|
+
const page = (0, bitmap_1.emptyBitmap)(width, height);
|
|
32
|
+
for (const it of items) {
|
|
33
|
+
if (it.type === 'gap')
|
|
34
|
+
continue;
|
|
35
|
+
if (it.type === 'bitmap') {
|
|
36
|
+
const x = it.align === 'left' ? 0 : it.align === 'right' ? width - it.bitmap.width : Math.floor((width - it.bitmap.width) / 2);
|
|
37
|
+
(0, bitmap_1.blit)(page, it.bitmap, x, it.y);
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
const s = it.state;
|
|
41
|
+
const cell = Math.floor(width / profile.columns[s.font]);
|
|
42
|
+
const cw = cell * s.w;
|
|
43
|
+
const ch = LINE_DOTS * s.h;
|
|
44
|
+
const textWidth = it.text.length * cw;
|
|
45
|
+
const x0 = s.align === 'left' ? 0 : s.align === 'right' ? width - textWidth : Math.floor((width - textWidth) / 2);
|
|
46
|
+
if (s.invert) {
|
|
47
|
+
ctx.fillStyle = '#000';
|
|
48
|
+
ctx.fillRect(x0, it.y, textWidth, ch);
|
|
49
|
+
ctx.fillStyle = '#fff';
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
ctx.fillStyle = '#000';
|
|
53
|
+
}
|
|
54
|
+
const weight = s.bold ? 'bold ' : '';
|
|
55
|
+
ctx.font = `${weight}100px ${this.fontFamily}`;
|
|
56
|
+
const advance = (ctx.measureText('M').width || 60) / 100;
|
|
57
|
+
const fontSize = Math.floor(Math.min(cw / advance, ch * 0.92));
|
|
58
|
+
ctx.font = `${weight}${fontSize}px ${this.fontFamily}`;
|
|
59
|
+
const glyphWidth = ctx.measureText('M').width;
|
|
60
|
+
const scaleY = Math.max(1, (ch * 0.92) / fontSize);
|
|
61
|
+
const baseline = it.y + Math.floor((ch - fontSize * scaleY) / 2);
|
|
62
|
+
for (let i = 0; i < it.text.length; i++) {
|
|
63
|
+
const chr = it.text[i];
|
|
64
|
+
if (chr === ' ')
|
|
65
|
+
continue;
|
|
66
|
+
ctx.save();
|
|
67
|
+
ctx.translate(x0 + i * cw + Math.floor((cw - glyphWidth) / 2), baseline);
|
|
68
|
+
ctx.scale(1, scaleY);
|
|
69
|
+
ctx.fillText(chr, 0, 0);
|
|
70
|
+
ctx.restore();
|
|
71
|
+
}
|
|
72
|
+
if (s.underline) {
|
|
73
|
+
const t = s.underline === 2 ? 2 : 1;
|
|
74
|
+
ctx.fillRect(x0, it.y + ch - t - 1, textWidth, t);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
const img = ctx.getImageData(0, 0, width, height);
|
|
78
|
+
const text = (0, bitmap_1.fromRgba)(img.data, width, height, 170);
|
|
79
|
+
for (let i = 0; i < page.bits.length; i++)
|
|
80
|
+
page.bits[i] |= text.bits[i];
|
|
81
|
+
return page;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
exports.CanvasRasterizer = CanvasRasterizer;
|
|
85
|
+
function layout(ops, profile) {
|
|
86
|
+
const items = [];
|
|
87
|
+
let y = 0;
|
|
88
|
+
const state = { align: 'left', font: 'a', w: 1, h: 1, bold: false, underline: 0, invert: false };
|
|
89
|
+
let pending = '';
|
|
90
|
+
const flushLine = (force) => {
|
|
91
|
+
if (!pending && !force)
|
|
92
|
+
return;
|
|
93
|
+
const cols = Math.max(1, Math.floor(profile.columns[state.font] / state.w));
|
|
94
|
+
const text = pending;
|
|
95
|
+
pending = '';
|
|
96
|
+
const parts = [];
|
|
97
|
+
for (let i = 0; i < text.length; i += cols)
|
|
98
|
+
parts.push(text.slice(i, i + cols));
|
|
99
|
+
if (!parts.length)
|
|
100
|
+
parts.push('');
|
|
101
|
+
for (const p of parts) {
|
|
102
|
+
const height = LINE_DOTS * state.h;
|
|
103
|
+
items.push({ type: 'line', text: p, state: { ...state }, y, height });
|
|
104
|
+
y += height;
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
for (const op of ops) {
|
|
108
|
+
switch (op.kind) {
|
|
109
|
+
case 'text': {
|
|
110
|
+
const lines = op.text.split('\n');
|
|
111
|
+
for (let i = 0; i < lines.length; i++) {
|
|
112
|
+
pending += lines[i];
|
|
113
|
+
if (i < lines.length - 1)
|
|
114
|
+
flushLine(true);
|
|
115
|
+
}
|
|
116
|
+
break;
|
|
117
|
+
}
|
|
118
|
+
case 'newline':
|
|
119
|
+
for (let i = 0; i < op.count; i++)
|
|
120
|
+
flushLine(true);
|
|
121
|
+
break;
|
|
122
|
+
case 'feed':
|
|
123
|
+
flushLine(false);
|
|
124
|
+
items.push({ type: 'gap', y, height: LINE_DOTS * op.lines });
|
|
125
|
+
y += LINE_DOTS * op.lines;
|
|
126
|
+
break;
|
|
127
|
+
case 'align':
|
|
128
|
+
flushLine(false);
|
|
129
|
+
state.align = op.align;
|
|
130
|
+
break;
|
|
131
|
+
case 'font':
|
|
132
|
+
flushLine(false);
|
|
133
|
+
state.font = op.font;
|
|
134
|
+
break;
|
|
135
|
+
case 'size':
|
|
136
|
+
flushLine(false);
|
|
137
|
+
state.w = Math.max(1, Math.min(8, Math.floor(op.width)));
|
|
138
|
+
state.h = Math.max(1, Math.min(8, Math.floor(op.height)));
|
|
139
|
+
break;
|
|
140
|
+
case 'style':
|
|
141
|
+
flushLine(false);
|
|
142
|
+
state.bold = !!op.style.bold;
|
|
143
|
+
state.underline = op.style.underline ?? 0;
|
|
144
|
+
state.invert = !!op.style.invert;
|
|
145
|
+
break;
|
|
146
|
+
case 'qr': {
|
|
147
|
+
flushLine(false);
|
|
148
|
+
const bm = (0, bitmap_1.qrBitmap)(op.data, op.options);
|
|
149
|
+
items.push({ type: 'bitmap', bitmap: bm, align: state.align, y, height: bm.height });
|
|
150
|
+
y += bm.height;
|
|
151
|
+
break;
|
|
152
|
+
}
|
|
153
|
+
case 'raster': {
|
|
154
|
+
flushLine(false);
|
|
155
|
+
const bm = (0, bitmap_1.fitWidth)({ width: op.width, height: op.height, bits: op.bits }, Math.min(op.width, profile.dots), 'left');
|
|
156
|
+
items.push({ type: 'bitmap', bitmap: bm, align: state.align, y, height: bm.height });
|
|
157
|
+
y += bm.height;
|
|
158
|
+
break;
|
|
159
|
+
}
|
|
160
|
+
case 'barcode': {
|
|
161
|
+
flushLine(false);
|
|
162
|
+
const bm = (0, barcode1d_1.barcodeBitmap)(op.type, op.data, op.options.width ?? 2, op.options.height ?? 80);
|
|
163
|
+
items.push({ type: 'bitmap', bitmap: bm, align: state.align, y, height: bm.height });
|
|
164
|
+
y += bm.height;
|
|
165
|
+
const pos = op.options.position ?? 'below';
|
|
166
|
+
if (pos === 'below' || pos === 'both') {
|
|
167
|
+
pending = op.data;
|
|
168
|
+
flushLine(true);
|
|
169
|
+
}
|
|
170
|
+
break;
|
|
171
|
+
}
|
|
172
|
+
case 'cut':
|
|
173
|
+
flushLine(false);
|
|
174
|
+
if (op.feed > 0) {
|
|
175
|
+
items.push({ type: 'gap', y, height: LINE_DOTS * op.feed });
|
|
176
|
+
y += LINE_DOTS * op.feed;
|
|
177
|
+
}
|
|
178
|
+
break;
|
|
179
|
+
default:
|
|
180
|
+
break;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
flushLine(false);
|
|
184
|
+
return items;
|
|
185
|
+
}
|
|
186
|
+
function loadCanvasFactory() {
|
|
187
|
+
for (const name of ['@napi-rs/canvas', 'canvas']) {
|
|
188
|
+
try {
|
|
189
|
+
const mod = require(name);
|
|
190
|
+
return (w, h) => mod.createCanvas(w, h);
|
|
191
|
+
}
|
|
192
|
+
catch {
|
|
193
|
+
/* try the next */
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
return () => {
|
|
197
|
+
throw new Error('retail-print-kit: bitmap printers need a canvas; install @napi-rs/canvas or pass a CanvasFactory');
|
|
198
|
+
};
|
|
199
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Op } from './ops';
|
|
2
|
+
import { Profile } from '../profiles/types';
|
|
3
|
+
import { Rasterizer } from './rasterizer';
|
|
4
|
+
/**
|
|
5
|
+
* Star raster mode (TSP100 / TSP143 family, "graphic only" printers). The whole receipt is one
|
|
6
|
+
* bitmap sent line by line with `b nL nH data`; cut happens at end of document per the EOT mode.
|
|
7
|
+
* Drawer kick uses the peripheral commands outside raster mode.
|
|
8
|
+
*
|
|
9
|
+
* Byte values for ESC * r E (EOT cut mode) follow the Star raster spec as read; verify on a real
|
|
10
|
+
* TSP143 before release: 1 = full cut, 2 = partial cut, 0 = no cut.
|
|
11
|
+
*/
|
|
12
|
+
export declare function encodeStarGraphic(ops: Op[], profile: Profile, rasterizer: Rasterizer): Buffer;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.encodeStarGraphic = encodeStarGraphic;
|
|
4
|
+
const ESC = 0x1b;
|
|
5
|
+
const NUL = 0x00;
|
|
6
|
+
const FF = 0x0c;
|
|
7
|
+
const BEL = 0x07;
|
|
8
|
+
const SUB = 0x1a;
|
|
9
|
+
/**
|
|
10
|
+
* Star raster mode (TSP100 / TSP143 family, "graphic only" printers). The whole receipt is one
|
|
11
|
+
* bitmap sent line by line with `b nL nH data`; cut happens at end of document per the EOT mode.
|
|
12
|
+
* Drawer kick uses the peripheral commands outside raster mode.
|
|
13
|
+
*
|
|
14
|
+
* Byte values for ESC * r E (EOT cut mode) follow the Star raster spec as read; verify on a real
|
|
15
|
+
* TSP143 before release: 1 = full cut, 2 = partial cut, 0 = no cut.
|
|
16
|
+
*/
|
|
17
|
+
function encodeStarGraphic(ops, profile, rasterizer) {
|
|
18
|
+
const chunks = [];
|
|
19
|
+
const push = (...parts) => {
|
|
20
|
+
for (const p of parts)
|
|
21
|
+
chunks.push(typeof p === 'number' ? Buffer.from([p]) : Buffer.from(p));
|
|
22
|
+
};
|
|
23
|
+
const cut = ops.find((o) => o.kind === 'cut');
|
|
24
|
+
const drawers = ops.filter((o) => o.kind === 'drawer');
|
|
25
|
+
const raws = ops.filter((o) => o.kind === 'raw');
|
|
26
|
+
const printable = ops.filter((o) => o.kind !== 'drawer' && o.kind !== 'raw' && o.kind !== 'init');
|
|
27
|
+
const page = printable.length ? rasterizer.render(printable, profile) : null;
|
|
28
|
+
if (page && page.height > 0) {
|
|
29
|
+
push([ESC, 0x2a, 0x72, 0x52]);
|
|
30
|
+
push([ESC, 0x2a, 0x72, 0x41]);
|
|
31
|
+
push([ESC, 0x2a, 0x72, 0x50, 0x30, NUL]);
|
|
32
|
+
push([ESC, 0x2a, 0x72, 0x45, cut ? (cut.partial ? 0x32 : 0x31) : 0x30, NUL]);
|
|
33
|
+
const rowBytes = Math.ceil(page.width / 8);
|
|
34
|
+
for (let y = 0; y < page.height; y++) {
|
|
35
|
+
push([0x62, rowBytes & 0xff, rowBytes >> 8], page.bits.subarray(y * rowBytes, (y + 1) * rowBytes));
|
|
36
|
+
}
|
|
37
|
+
push([ESC, FF, NUL]);
|
|
38
|
+
push([ESC, 0x2a, 0x72, 0x42]);
|
|
39
|
+
}
|
|
40
|
+
for (const d of drawers) {
|
|
41
|
+
const on = Math.max(1, Math.min(127, Math.floor(profile.drawer.on / 5)));
|
|
42
|
+
const off = Math.max(1, Math.min(127, Math.floor(profile.drawer.off / 5)));
|
|
43
|
+
push([ESC, BEL, on, off], d.pin === 2 ? BEL : SUB);
|
|
44
|
+
}
|
|
45
|
+
for (const r of raws)
|
|
46
|
+
push(r.bytes);
|
|
47
|
+
return Buffer.concat(chunks);
|
|
48
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Op } from './ops';
|
|
2
|
+
import { Profile } from '../profiles/types';
|
|
3
|
+
/**
|
|
4
|
+
* Star Line Mode (TSP650II, TSP700II, mC-Print, SP700). Text is printed by the printer, like
|
|
5
|
+
* ESC/POS, but every command byte differs. Drawer kick is BEL (drawer 1) or SUB (drawer 2) after
|
|
6
|
+
* ESC BEL sets the pulse.
|
|
7
|
+
*/
|
|
8
|
+
export declare function encodeStarLine(ops: Op[], profile: Profile): Buffer;
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.encodeStarLine = encodeStarLine;
|
|
7
|
+
const iconv_lite_1 = __importDefault(require("iconv-lite"));
|
|
8
|
+
const ESC = 0x1b;
|
|
9
|
+
const GS = 0x1d;
|
|
10
|
+
const RS = 0x1e;
|
|
11
|
+
const LF = 0x0a;
|
|
12
|
+
const BEL = 0x07;
|
|
13
|
+
const SUB = 0x1a;
|
|
14
|
+
/** Star barcode type numbers for ESC b. */
|
|
15
|
+
const BARCODE_CODE = {
|
|
16
|
+
UPC_E: 0, UPC_A: 1, EAN8: 2, EAN13: 3, CODE39: 4, ITF: 5, CODE128: 6, CODABAR: 8,
|
|
17
|
+
};
|
|
18
|
+
const QR_CORRECTION = { L: 0, M: 1, Q: 2, H: 3 };
|
|
19
|
+
function clamp(n, lo, hi) {
|
|
20
|
+
return Math.max(lo, Math.min(hi, Math.floor(n)));
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Star Line Mode (TSP650II, TSP700II, mC-Print, SP700). Text is printed by the printer, like
|
|
24
|
+
* ESC/POS, but every command byte differs. Drawer kick is BEL (drawer 1) or SUB (drawer 2) after
|
|
25
|
+
* ESC BEL sets the pulse.
|
|
26
|
+
*/
|
|
27
|
+
function encodeStarLine(ops, profile) {
|
|
28
|
+
const chunks = [];
|
|
29
|
+
const push = (...parts) => {
|
|
30
|
+
for (const p of parts)
|
|
31
|
+
chunks.push(typeof p === 'number' ? Buffer.from([p]) : Buffer.from(p));
|
|
32
|
+
};
|
|
33
|
+
for (const op of ops) {
|
|
34
|
+
switch (op.kind) {
|
|
35
|
+
case 'init':
|
|
36
|
+
push([ESC, 0x40]);
|
|
37
|
+
if (profile.codepage !== null)
|
|
38
|
+
push([ESC, GS, 0x74, profile.codepage]);
|
|
39
|
+
break;
|
|
40
|
+
case 'text':
|
|
41
|
+
push(iconv_lite_1.default.encode(op.text, profile.encoding));
|
|
42
|
+
break;
|
|
43
|
+
case 'newline':
|
|
44
|
+
for (let i = 0; i < op.count; i++)
|
|
45
|
+
push(LF);
|
|
46
|
+
break;
|
|
47
|
+
case 'feed':
|
|
48
|
+
push([ESC, 0x61, clamp(op.lines, 0, 255)]);
|
|
49
|
+
break;
|
|
50
|
+
case 'align':
|
|
51
|
+
push([ESC, GS, 0x61, op.align === 'left' ? 0 : op.align === 'center' ? 1 : 2]);
|
|
52
|
+
break;
|
|
53
|
+
case 'font':
|
|
54
|
+
push([ESC, RS, 0x46, op.font === 'a' ? 0 : 1]);
|
|
55
|
+
break;
|
|
56
|
+
case 'size':
|
|
57
|
+
push([ESC, 0x69, clamp(op.height, 1, 6) - 1, clamp(op.width, 1, 6) - 1]);
|
|
58
|
+
break;
|
|
59
|
+
case 'style':
|
|
60
|
+
push([ESC, op.style.bold ? 0x45 : 0x46]);
|
|
61
|
+
push([ESC, 0x2d, op.style.underline ? 1 : 0]);
|
|
62
|
+
if (profile.features.invert)
|
|
63
|
+
push([ESC, op.style.invert ? 0x34 : 0x35]);
|
|
64
|
+
break;
|
|
65
|
+
case 'lineSpacing':
|
|
66
|
+
push(op.dots === null ? [ESC, 0x7a, 1] : [ESC, 0x30]);
|
|
67
|
+
break;
|
|
68
|
+
case 'cut': {
|
|
69
|
+
const feed = clamp(op.feed, 0, 255);
|
|
70
|
+
if (feed > 0)
|
|
71
|
+
push([ESC, 0x61, feed]);
|
|
72
|
+
push([ESC, 0x64, op.partial ? 3 : 2]);
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
case 'drawer': {
|
|
76
|
+
const on = clamp(profile.drawer.on / 5, 1, 127);
|
|
77
|
+
const off = clamp(profile.drawer.off / 5, 1, 127);
|
|
78
|
+
push([ESC, BEL, on, off], op.pin === 2 ? BEL : SUB);
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
case 'beep':
|
|
82
|
+
break;
|
|
83
|
+
case 'barcode': {
|
|
84
|
+
const o = op.options;
|
|
85
|
+
const pos = o.position ?? 'below';
|
|
86
|
+
const hri = pos === 'none' || pos === 'above' ? 3 : 4;
|
|
87
|
+
const code = BARCODE_CODE[op.type];
|
|
88
|
+
if (code === undefined)
|
|
89
|
+
throw new Error(`retail-print-kit: Star Line has no barcode type ${op.type}`);
|
|
90
|
+
push([ESC, 0x62, code, hri, clamp(o.width ?? 2, 1, 9), clamp(o.height ?? 80, 1, 255)], Buffer.from(op.data, 'ascii'), RS);
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
case 'qr':
|
|
94
|
+
if (!profile.features.nativeQr)
|
|
95
|
+
throw new Error(`retail-print-kit: profile ${profile.id} has no native QR`);
|
|
96
|
+
pushQr(push, op.data, op.options);
|
|
97
|
+
break;
|
|
98
|
+
case 'raster': {
|
|
99
|
+
if (!profile.features.raster)
|
|
100
|
+
throw new Error(`retail-print-kit: profile ${profile.id} cannot print raster images`);
|
|
101
|
+
const xBytes = Math.ceil(op.width / 8);
|
|
102
|
+
push([ESC, GS, 0x53, 1, xBytes & 0xff, xBytes >> 8, op.height & 0xff, op.height >> 8, 0], op.bits);
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
case 'raw':
|
|
106
|
+
push(op.bytes);
|
|
107
|
+
break;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return Buffer.concat(chunks);
|
|
111
|
+
}
|
|
112
|
+
function pushQr(push, data, options) {
|
|
113
|
+
const payload = Buffer.from(data, 'utf8');
|
|
114
|
+
push([ESC, GS, 0x79, 0x53, 0x30, options.model === 1 ? 1 : 2]);
|
|
115
|
+
push([ESC, GS, 0x79, 0x53, 0x31, QR_CORRECTION[options.correction ?? 'M']]);
|
|
116
|
+
push([ESC, GS, 0x79, 0x53, 0x32, clamp(options.size ?? 6, 1, 8)]);
|
|
117
|
+
push([ESC, GS, 0x79, 0x44, 0x31, 0, payload.length & 0xff, payload.length >> 8], payload);
|
|
118
|
+
push([ESC, GS, 0x79, 0x50]);
|
|
119
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export interface PrinterStatus {
|
|
2
|
+
/** False when the profile's command set has no status query implemented. */
|
|
3
|
+
supported: boolean;
|
|
4
|
+
online: boolean;
|
|
5
|
+
coverOpen: boolean;
|
|
6
|
+
paperOut: boolean;
|
|
7
|
+
paperNearEnd: boolean;
|
|
8
|
+
cutterError: boolean;
|
|
9
|
+
recoverableError: boolean;
|
|
10
|
+
unrecoverableError: boolean;
|
|
11
|
+
/** Level of drawer kick-out connector pin 3; which level means "open" depends on the drawer. */
|
|
12
|
+
drawerPinHigh: boolean;
|
|
13
|
+
raw: number[];
|
|
14
|
+
}
|
|
15
|
+
/** Parses the four ESC/POS real-time status bytes (DLE EOT 1..4). */
|
|
16
|
+
export declare function parseEscPosStatus(b: number[]): PrinterStatus;
|
|
17
|
+
export declare const UNSUPPORTED_STATUS: PrinterStatus;
|
|
18
|
+
/**
|
|
19
|
+
* Parses a Star Line Mode status block (reply to ESC ACK SOH). Bit positions follow the Star Line
|
|
20
|
+
* Mode command specification as read; verify against a TSP650II / mC-Print before relying on it.
|
|
21
|
+
*/
|
|
22
|
+
export declare function parseStarStatus(b: number[]): PrinterStatus;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.UNSUPPORTED_STATUS = void 0;
|
|
4
|
+
exports.parseEscPosStatus = parseEscPosStatus;
|
|
5
|
+
exports.parseStarStatus = parseStarStatus;
|
|
6
|
+
/** Parses the four ESC/POS real-time status bytes (DLE EOT 1..4). */
|
|
7
|
+
function parseEscPosStatus(b) {
|
|
8
|
+
const [printer = 0, offline = 0, error = 0, paper = 0] = b;
|
|
9
|
+
return {
|
|
10
|
+
supported: true,
|
|
11
|
+
online: (printer & 0x08) === 0,
|
|
12
|
+
drawerPinHigh: (printer & 0x04) !== 0,
|
|
13
|
+
coverOpen: (offline & 0x04) !== 0,
|
|
14
|
+
paperOut: (offline & 0x20) !== 0 || (paper & 0x60) !== 0,
|
|
15
|
+
paperNearEnd: (paper & 0x0c) !== 0,
|
|
16
|
+
cutterError: (error & 0x08) !== 0,
|
|
17
|
+
recoverableError: (error & 0x40) !== 0,
|
|
18
|
+
unrecoverableError: (error & 0x20) !== 0,
|
|
19
|
+
raw: b,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
exports.UNSUPPORTED_STATUS = {
|
|
23
|
+
supported: false,
|
|
24
|
+
online: true,
|
|
25
|
+
coverOpen: false,
|
|
26
|
+
paperOut: false,
|
|
27
|
+
paperNearEnd: false,
|
|
28
|
+
cutterError: false,
|
|
29
|
+
recoverableError: false,
|
|
30
|
+
unrecoverableError: false,
|
|
31
|
+
drawerPinHigh: false,
|
|
32
|
+
raw: [],
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Parses a Star Line Mode status block (reply to ESC ACK SOH). Bit positions follow the Star Line
|
|
36
|
+
* Mode command specification as read; verify against a TSP650II / mC-Print before relying on it.
|
|
37
|
+
*/
|
|
38
|
+
function parseStarStatus(b) {
|
|
39
|
+
const [, printer = 0, error = 0, , paper = 0] = b;
|
|
40
|
+
return {
|
|
41
|
+
supported: true,
|
|
42
|
+
online: (printer & 0x08) === 0,
|
|
43
|
+
coverOpen: (printer & 0x20) !== 0,
|
|
44
|
+
drawerPinHigh: (printer & 0x04) !== 0,
|
|
45
|
+
cutterError: (error & 0x08) !== 0,
|
|
46
|
+
unrecoverableError: (error & 0x20) !== 0,
|
|
47
|
+
recoverableError: (error & 0x40) !== 0,
|
|
48
|
+
paperNearEnd: (paper & 0x04) !== 0,
|
|
49
|
+
paperOut: (paper & 0x08) !== 0,
|
|
50
|
+
raw: b,
|
|
51
|
+
};
|
|
52
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export * from './core/ops';
|
|
2
|
+
export * from './core/printer';
|
|
3
|
+
export { encode } from './core/encode';
|
|
4
|
+
export { encodeEscPos } from './core/escpos-encoder';
|
|
5
|
+
export * from './profiles';
|
|
6
|
+
export type { Transport } from './transports/types';
|
|
7
|
+
export { NetTransport } from './transports/net';
|
|
8
|
+
export type { NetOptions } from './transports/net';
|
|
9
|
+
export * from './core/bitmap';
|
|
10
|
+
export * from './core/status';
|
|
11
|
+
export { CanvasRasterizer, loadCanvasFactory } from './core/rasterizer';
|
|
12
|
+
export type { Rasterizer, CanvasFactory } from './core/rasterizer';
|
|
13
|
+
export type { EncodeContext } from './core/encode';
|
|
14
|
+
export { encodeStarLine } from './core/star-line-encoder';
|
|
15
|
+
export { encodeStarGraphic } from './core/star-graphic-encoder';
|
|
16
|
+
export * from './core/barcode1d';
|
|
17
|
+
export * from './template';
|
|
18
|
+
export * from './preview';
|
|
19
|
+
export * from './core/identify';
|
|
20
|
+
export * from './label';
|
|
21
|
+
export { toBase64 } from './util/base64';
|
|
22
|
+
export { loadImage } from './core/image';
|
|
23
|
+
export type { LoadImageOptions } from './core/image';
|
|
24
|
+
export { discoverNetworkPrinters, localSubnets } from './transports/discover';
|
|
25
|
+
export type { DiscoverOptions, DiscoveredPrinter } from './transports/discover';
|
|
26
|
+
export { fromBase64 } from './util/base64';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.fromBase64 = exports.localSubnets = exports.discoverNetworkPrinters = exports.loadImage = exports.toBase64 = exports.encodeStarGraphic = exports.encodeStarLine = exports.loadCanvasFactory = exports.CanvasRasterizer = exports.NetTransport = exports.encodeEscPos = exports.encode = void 0;
|
|
18
|
+
__exportStar(require("./core/ops"), exports);
|
|
19
|
+
__exportStar(require("./core/printer"), exports);
|
|
20
|
+
var encode_1 = require("./core/encode");
|
|
21
|
+
Object.defineProperty(exports, "encode", { enumerable: true, get: function () { return encode_1.encode; } });
|
|
22
|
+
var escpos_encoder_1 = require("./core/escpos-encoder");
|
|
23
|
+
Object.defineProperty(exports, "encodeEscPos", { enumerable: true, get: function () { return escpos_encoder_1.encodeEscPos; } });
|
|
24
|
+
__exportStar(require("./profiles"), exports);
|
|
25
|
+
var net_1 = require("./transports/net");
|
|
26
|
+
Object.defineProperty(exports, "NetTransport", { enumerable: true, get: function () { return net_1.NetTransport; } });
|
|
27
|
+
__exportStar(require("./core/bitmap"), exports);
|
|
28
|
+
__exportStar(require("./core/status"), exports);
|
|
29
|
+
var rasterizer_1 = require("./core/rasterizer");
|
|
30
|
+
Object.defineProperty(exports, "CanvasRasterizer", { enumerable: true, get: function () { return rasterizer_1.CanvasRasterizer; } });
|
|
31
|
+
Object.defineProperty(exports, "loadCanvasFactory", { enumerable: true, get: function () { return rasterizer_1.loadCanvasFactory; } });
|
|
32
|
+
var star_line_encoder_1 = require("./core/star-line-encoder");
|
|
33
|
+
Object.defineProperty(exports, "encodeStarLine", { enumerable: true, get: function () { return star_line_encoder_1.encodeStarLine; } });
|
|
34
|
+
var star_graphic_encoder_1 = require("./core/star-graphic-encoder");
|
|
35
|
+
Object.defineProperty(exports, "encodeStarGraphic", { enumerable: true, get: function () { return star_graphic_encoder_1.encodeStarGraphic; } });
|
|
36
|
+
__exportStar(require("./core/barcode1d"), exports);
|
|
37
|
+
__exportStar(require("./template"), exports);
|
|
38
|
+
__exportStar(require("./preview"), exports);
|
|
39
|
+
__exportStar(require("./core/identify"), exports);
|
|
40
|
+
__exportStar(require("./label"), exports);
|
|
41
|
+
var base64_1 = require("./util/base64");
|
|
42
|
+
Object.defineProperty(exports, "toBase64", { enumerable: true, get: function () { return base64_1.toBase64; } });
|
|
43
|
+
var image_1 = require("./core/image");
|
|
44
|
+
Object.defineProperty(exports, "loadImage", { enumerable: true, get: function () { return image_1.loadImage; } });
|
|
45
|
+
var discover_1 = require("./transports/discover");
|
|
46
|
+
Object.defineProperty(exports, "discoverNetworkPrinters", { enumerable: true, get: function () { return discover_1.discoverNetworkPrinters; } });
|
|
47
|
+
Object.defineProperty(exports, "localSubnets", { enumerable: true, get: function () { return discover_1.localSubnets; } });
|
|
48
|
+
var base64_2 = require("./util/base64");
|
|
49
|
+
Object.defineProperty(exports, "fromBase64", { enumerable: true, get: function () { return base64_2.fromBase64; } });
|