retail-print-kit 0.1.0 → 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/README.md +138 -35
- package/bin/retail-print-kit.js +101 -1
- package/dist/browser/retail-print-kit.js +15 -15
- package/dist/browser/retail-print-kit.js.map +4 -4
- package/dist/browser/retail-print-kit.mjs +15 -15
- package/dist/browser.d.ts +1 -1
- package/dist/browser.js +2 -1
- package/dist/core/bitmap.d.ts +13 -0
- package/dist/core/bitmap.js +46 -0
- package/dist/core/image.d.ts +13 -0
- package/dist/core/image.js +32 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +8 -1
- package/dist/template/render.js +9 -0
- package/dist/template/types.d.ts +6 -0
- package/dist/transports/discover.d.ts +21 -0
- package/dist/transports/discover.js +73 -0
- package/dist/util/base64.d.ts +1 -0
- package/dist/util/base64.js +19 -0
- package/package.json +40 -6
package/README.md
CHANGED
|
@@ -1,39 +1,109 @@
|
|
|
1
1
|
# retail-print-kit
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/retail-print-kit)
|
|
4
|
+
[](https://github.com/arka162/thermal-print/actions/workflows/ci.yml)
|
|
5
|
+
[](LICENSE)
|
|
5
6
|
|
|
6
|
-
|
|
7
|
+
**Receipt printers, cash drawers and label printers for Node.js and Electron, in one package.**
|
|
7
8
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
9
|
+
A modern replacement for `escpos` / `node-thermal-printer`: ESC/POS **and** Star printers, USB /
|
|
10
|
+
network / serial, printer profiles instead of model `if`s, receipt and label **templates**, live
|
|
11
|
+
**previews**, a visual **designer**, and a CLI for testing hardware in the field.
|
|
12
|
+
|
|
13
|
+
**[Open the designer](https://arka162.github.io/thermal-print/)** · [Printer matrix](docs/printer-matrix.md) · [Hardware checklist](docs/hardware-checklist.md)
|
|
14
|
+
|
|
15
|
+

|
|
16
|
+
|
|
17
|
+
## Why
|
|
18
|
+
|
|
19
|
+
| | retail-print-kit | escpos | node-thermal-printer |
|
|
20
|
+
|---|---|---|---|
|
|
21
|
+
| Epson ESC/POS and compatibles | yes | yes | yes |
|
|
22
|
+
| Star Line Mode (TSP650, mC-Print) | yes | no | yes |
|
|
23
|
+
| Star raster-only (TSP100 / TSP143) | yes | no | no |
|
|
24
|
+
| Label printers (ZPL, TSPL, EZPL) | yes | no | no |
|
|
25
|
+
| Printer profiles (cut, drawer pulse, code page, columns) | yes | no | no |
|
|
26
|
+
| Templates with data binding | yes | no | no |
|
|
27
|
+
| HTML / PNG / SVG preview without a printer | yes | no | no |
|
|
28
|
+
| Status (paper, cover, drawer) and model detection | yes | no | partly |
|
|
29
|
+
| Native dependencies in the core | none | none | none |
|
|
30
|
+
| TypeScript types | built in | community | built in |
|
|
31
|
+
|
|
32
|
+
## Install
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
npm install retail-print-kit
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
The core is pure JavaScript. Add a transport only if you need it:
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
npm install usb # USB printers
|
|
42
|
+
npm install serialport # RS-232 / USB-serial printers
|
|
43
|
+
npm install @napi-rs/canvas # PNG preview, image files, Star TSP100
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Node 18 or newer, including Electron's main process. CI runs on Linux, Windows and macOS.
|
|
47
|
+
|
|
48
|
+
## Quick start
|
|
15
49
|
|
|
16
50
|
```js
|
|
17
51
|
const { Printer, profiles } = require('retail-print-kit');
|
|
18
52
|
const { NetTransport } = require('retail-print-kit/net');
|
|
19
53
|
|
|
20
|
-
const printer = new Printer(new NetTransport('192.168.1.50'), profiles
|
|
54
|
+
const printer = new Printer(new NetTransport('192.168.1.50'), profiles['epson-tm-t88']);
|
|
21
55
|
await printer.open();
|
|
22
56
|
printer
|
|
23
57
|
.align('ct').style('b').size(2, 2).text('MY STORE')
|
|
24
58
|
.size(1, 1).style('normal').align('lt')
|
|
25
|
-
.tableCustom([
|
|
59
|
+
.tableCustom([
|
|
60
|
+
{ text: 'Coffee', align: 'LEFT', width: 0.7 },
|
|
61
|
+
{ text: '$2.50', align: 'RIGHT', width: 0.3 },
|
|
62
|
+
])
|
|
26
63
|
.drawLine()
|
|
64
|
+
.qr('https://example.com/receipt/1042')
|
|
27
65
|
.feed(2).cut().cashdraw();
|
|
28
66
|
await printer.close();
|
|
29
67
|
```
|
|
30
68
|
|
|
69
|
+
The method names match the `escpos` package (`text`, `align`, `style`, `size`, `feed`, `cut`,
|
|
70
|
+
`cashdraw`, `tableCustom`, `drawLine`, `barcode`, `qrimage`), so existing receipt code moves over
|
|
71
|
+
with a device swap. Other transports:
|
|
72
|
+
|
|
73
|
+
```js
|
|
74
|
+
const { UsbTransport } = require('retail-print-kit/usb'); // new UsbTransport() or { vendorId, productId }
|
|
75
|
+
const { SerialTransport } = require('retail-print-kit/serial'); // new SerialTransport('COM3', { baudRate: 9600 })
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Supported printers
|
|
79
|
+
|
|
80
|
+
| Family | Profile ids | Command set |
|
|
81
|
+
|---|---|---|
|
|
82
|
+
| Epson TM-T88 V/VI/VII, TM-T20III, TM-m30 | `epson-tm-t88`, `epson-tm-t20`, `epson-tm-m30` | ESC/POS |
|
|
83
|
+
| Bixolon, Citizen, SNBC, Xprinter, Rongta, HPRT, POS-X | `generic-escpos`, `generic-escpos-58` | ESC/POS |
|
|
84
|
+
| Star TSP650II, TSP700II, mC-Print3 | `star-tsp650`, `star-mc-print3` | Star Line Mode |
|
|
85
|
+
| Star TSP100 / TSP143 | `star-tsp100` | Star raster |
|
|
86
|
+
| Zebra, TSC and clones, Godex (labels) | `encodeZpl`, `encodeTspl`, `encodeEzpl` | ZPL, TSPL, EZPL |
|
|
87
|
+
|
|
88
|
+
A profile carries paper width, columns, code page, cut command, drawer pulse and features, so the
|
|
89
|
+
same receipt code prints correctly on each. `identify()` and `profileForUsb()` pick one for you.
|
|
90
|
+
See the [printer matrix](docs/printer-matrix.md) for what has been run on real hardware.
|
|
91
|
+
|
|
92
|
+
## Cash drawer and status
|
|
93
|
+
|
|
94
|
+
```js
|
|
95
|
+
printer.cashdraw(); // pin 2; cashdraw(5) for the second drawer
|
|
96
|
+
await printer.flush();
|
|
97
|
+
|
|
98
|
+
const s = await printer.status();
|
|
99
|
+
// { online, coverOpen, paperOut, paperNearEnd, cutterError, drawerPinHigh, ... }
|
|
100
|
+
```
|
|
101
|
+
|
|
31
102
|
## Templates
|
|
32
103
|
|
|
33
|
-
A receipt can be a JSON layout rendered with data
|
|
104
|
+
A receipt can be a JSON layout rendered with data. This is what the designer produces.
|
|
34
105
|
|
|
35
106
|
```js
|
|
36
|
-
const { render } = require('retail-print-kit');
|
|
37
107
|
const template = {
|
|
38
108
|
name: 'receipt', version: 1,
|
|
39
109
|
blocks: [
|
|
@@ -49,14 +119,24 @@ const template = {
|
|
|
49
119
|
{ type: 'cut' }, { type: 'drawer' },
|
|
50
120
|
],
|
|
51
121
|
};
|
|
52
|
-
printer.render(template, data);
|
|
53
|
-
|
|
122
|
+
printer.render(template, data);
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Blocks: `text`, `line`, `row`, `table`, `image`, `feed`, `newline`, `cut`, `drawer`, `qr`,
|
|
126
|
+
`barcode`, `each`, `if`. Values use `{{path | filter}}` with `upper`, `lower`, `trim`,
|
|
127
|
+
`money[:prefix]`, `fixed[:n]`, `pad:n`, `padEnd:n`, `default:x`, `date[:time|datetime]`, `count`.
|
|
128
|
+
Inside `each` and table rows, `this`, `@index` and `@number` refer to the current item.
|
|
129
|
+
|
|
130
|
+
## Images and logos
|
|
131
|
+
|
|
132
|
+
```js
|
|
133
|
+
const { loadImage } = require('retail-print-kit');
|
|
134
|
+
const logo = await loadImage('logo.png', { width: 384, dither: true }); // needs @napi-rs/canvas
|
|
135
|
+
printer.align('ct').image(logo);
|
|
54
136
|
```
|
|
55
137
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
`pad:n`, `padEnd:n`, `default:x`, `date[:time|datetime]`, `count`. Inside `each`/`table` rows, `this`,
|
|
59
|
-
`@index` and `@number` refer to the current item.
|
|
138
|
+
`fromRgba` and `ditherRgba` convert raw pixels; `serializeBitmap` stores a bitmap as JSON for the
|
|
139
|
+
template `image` block.
|
|
60
140
|
|
|
61
141
|
## Labels
|
|
62
142
|
|
|
@@ -68,40 +148,63 @@ const { LabelBuilder, encodeZpl, encodeTspl, encodeEzpl, labelToSvg, renderLabel
|
|
|
68
148
|
const b = new LabelBuilder({ widthMm: 50.8, heightMm: 25.4, dpi: 203 });
|
|
69
149
|
b.text(18, 14, 'Coffee Beans 1lb', 26).text(92, 65, '$12.99', 85, { bold: true })
|
|
70
150
|
.barcode(35, 140, '036000291452', 'UPC_A', 35).qr(330, 20, 'https://x.io/i/1', { module: 3 });
|
|
71
|
-
const zpl = encodeZpl(b.label);
|
|
72
|
-
const svg = labelToSvg(b.label);
|
|
151
|
+
const zpl = encodeZpl(b.label); // send to the printer on port 9100
|
|
152
|
+
const svg = labelToSvg(b.label); // preview
|
|
73
153
|
const label = renderLabel(labelTemplate, data); // from a designer JSON
|
|
74
154
|
```
|
|
75
155
|
|
|
156
|
+

|
|
157
|
+
|
|
76
158
|
## Designer
|
|
77
159
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
160
|
+
**https://arka162.github.io/thermal-print/** edits receipt and label templates in the browser:
|
|
161
|
+
block palette, nested `each` / `if`, property editor, sample data, live preview, drag-to-position
|
|
162
|
+
on labels, logo upload with dithering, JSON import and export. It runs entirely in the page from
|
|
163
|
+
the browser bundle (`retail-print-kit/browser`); nothing is uploaded.
|
|
164
|
+
|
|
165
|
+
## Preview without a printer
|
|
166
|
+
|
|
167
|
+
```js
|
|
168
|
+
printer.toHtml(); // paper-like HTML of the recorded ops
|
|
169
|
+
opsToHtml(ops, profile); opsToPng(ops, profile); // same without a Printer
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
<img src="https://raw.githubusercontent.com/arka162/thermal-print/main/docs/images/test-page.png" width="300" alt="PNG preview of the test page">
|
|
81
173
|
|
|
82
|
-
##
|
|
174
|
+
## Find and identify printers
|
|
83
175
|
|
|
84
176
|
```js
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
profileForUsb(0x04b8);
|
|
177
|
+
const { discoverNetworkPrinters, identify, profileForUsb } = require('retail-print-kit');
|
|
178
|
+
await discoverNetworkPrinters({ identify: true }); // [{ host, port, identity: { maker, model, profile } }]
|
|
179
|
+
await identify(transport); // GS I for ESC/POS, status probe for Star
|
|
180
|
+
profileForUsb(0x04b8); // profile from a USB vendor id
|
|
89
181
|
```
|
|
90
182
|
|
|
91
183
|
## CLI
|
|
92
184
|
|
|
93
185
|
```
|
|
186
|
+
npx retail-print-kit discover --identify # printers on this network
|
|
94
187
|
npx retail-print-kit test --net 192.168.1.50 --profile epson-tm-t20 # test page + status
|
|
188
|
+
npx retail-print-kit verify --net 192.168.1.50 --drawer # numbered hardware checks
|
|
95
189
|
npx retail-print-kit drawer --usb # kick the drawer
|
|
96
190
|
npx retail-print-kit status --serial /dev/ttyUSB0
|
|
97
|
-
npx retail-print-kit identify --net 192.168.1.50
|
|
98
|
-
npx retail-print-kit
|
|
191
|
+
npx retail-print-kit identify --net 192.168.1.50
|
|
192
|
+
npx retail-print-kit image --file logo.png --width 384 --dither --net 192.168.1.50
|
|
99
193
|
npx retail-print-kit print --template receipt.json --data sale.json --net 192.168.1.50
|
|
100
194
|
npx retail-print-kit print --template shelf.json --data item.json --language zpl --net 192.168.1.60
|
|
195
|
+
npx retail-print-kit verify-label --language tspl --net 192.168.1.60
|
|
196
|
+
npx retail-print-kit preview --profile star-tsp100 --out page.png # no printer needed
|
|
101
197
|
npx retail-print-kit profiles
|
|
102
|
-
npx retail-print-kit test --dry --profile star-tsp100 | head -c 200 # bytes only, no printer
|
|
103
198
|
```
|
|
104
199
|
|
|
105
|
-
|
|
200
|
+
Add `--dry` to any printing command to dump the bytes as hex instead of sending them.
|
|
201
|
+
|
|
202
|
+
## Status of hardware verification
|
|
203
|
+
|
|
204
|
+
Encoders are covered by byte-exact tests. Items still to be confirmed on real units are listed in
|
|
205
|
+
the [hardware checklist](docs/hardware-checklist.md): Star raster cut modes, Star status bits,
|
|
206
|
+
Godex font sizes, and QR syntax on EZPL and TSPL. Reports from the field are welcome as issues.
|
|
207
|
+
|
|
208
|
+
## License
|
|
106
209
|
|
|
107
|
-
|
|
210
|
+
MIT, Arkaprova Majumder.
|
package/bin/retail-print-kit.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
'use strict';
|
|
3
3
|
const fs = require('fs');
|
|
4
|
-
const { Printer, profiles, profileById, NetTransport, identify, opsToHtml, opsToPng, OpBuilder, render, renderLabel, encodeLabel, labelToSvg } = require('../dist');
|
|
4
|
+
const { Printer, profiles, profileById, NetTransport, identify, opsToHtml, opsToPng, OpBuilder, render, renderLabel, encodeLabel, labelToSvg, discoverNetworkPrinters, emptyBitmap, setPixel, qrBitmap, LabelBuilder, loadImage } = require('../dist');
|
|
5
5
|
|
|
6
6
|
process.stdout.on('error', (err) => {
|
|
7
7
|
if (err.code === 'EPIPE') process.exit(0);
|
|
@@ -14,6 +14,11 @@ Commands
|
|
|
14
14
|
test print a test page (text styles, table, barcode, QR) and show status
|
|
15
15
|
drawer kick the cash drawer
|
|
16
16
|
status print the printer status as JSON
|
|
17
|
+
verify print the numbered hardware verification page (see docs/hardware-checklist.md)
|
|
18
|
+
add --drawer to kick pin 2 then pin 5
|
|
19
|
+
verify-label print a verification label: --language zpl|tspl|ezpl [--width-mm 50.8 --height-mm 25.4]
|
|
20
|
+
discover find printers on the local network (port 9100); --identify asks each one its model
|
|
21
|
+
image print an image file: --file logo.png [--width 384] [--dither]
|
|
17
22
|
identify ask the printer its maker, model and firmware; suggest a profile
|
|
18
23
|
preview write the test page as HTML (--out page.html) or PNG (--out page.png)
|
|
19
24
|
print print a template: --template t.json --data d.json (receipt or label)
|
|
@@ -124,6 +129,72 @@ async function testPage(printer, profile) {
|
|
|
124
129
|
await showStatus(printer);
|
|
125
130
|
}
|
|
126
131
|
|
|
132
|
+
function section(p, id, title) {
|
|
133
|
+
p.align('lt').style('b').text(`[${id}] ${title}`).style('normal');
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
async function verifyPage(printer, profile, args) {
|
|
137
|
+
const cols = profile.columns.a;
|
|
138
|
+
const ruler = (n) => '1234567890'.repeat(Math.ceil(n / 10)).slice(0, n);
|
|
139
|
+
printer.align('ct').style('b').size(2, 2).text('VERIFY').size(1, 1).style('normal')
|
|
140
|
+
.text(`${profile.id} / ${profile.set}`).text(new Date().toLocaleString()).align('lt').drawLine();
|
|
141
|
+
section(printer, 'V1', `Font A ruler: last digit must be ${ruler(cols).slice(-1)} at the right edge (${cols} cols)`);
|
|
142
|
+
printer.text(ruler(cols));
|
|
143
|
+
section(printer, 'V2', `Font B ruler (${profile.columns.b} cols)`);
|
|
144
|
+
printer.setFont('b').text(ruler(profile.columns.b)).setFont('a');
|
|
145
|
+
section(printer, 'V3', 'Styles: bold, underline, double width, double height, inverse');
|
|
146
|
+
printer.style('b').text('bold').style('u').text('underline').style('normal').size(2, 1).text('wide').size(1, 2).text('tall').size(1, 1)
|
|
147
|
+
.style({ invert: true }).text(' inverse ').style('normal');
|
|
148
|
+
section(printer, 'V4', 'Alignment: left, centre, right');
|
|
149
|
+
printer.align('lt').text('left').align('ct').text('centre').align('rt').text('right').align('lt');
|
|
150
|
+
section(printer, 'V5', 'Code page: bytes 80..FF (expect CP437 box drawing in B0..DF)');
|
|
151
|
+
for (let row = 0x80; row < 0x100; row += 16) {
|
|
152
|
+
printer.print(row.toString(16).toUpperCase() + ' ').raw(Array.from({ length: 16 }, (_, i) => row + i)).newLine();
|
|
153
|
+
}
|
|
154
|
+
section(printer, 'V6', 'Barcodes: UPC-A, EAN-13, CODE128, CODE39');
|
|
155
|
+
printer.align('ct')
|
|
156
|
+
.barcode('036000291452', 'UPC_A', { height: 50 }).newLine()
|
|
157
|
+
.barcode('5901234123457', 'EAN13', { height: 50 }).newLine()
|
|
158
|
+
.barcode('RPK-12345', 'CODE128', { height: 50 }).newLine()
|
|
159
|
+
.barcode('RPK 123', 'CODE39', { height: 50 }).newLine().align('lt');
|
|
160
|
+
section(printer, 'V7', 'QR A = printer native, QR B = bitmap; both must scan to the same URL');
|
|
161
|
+
printer.align('ct').text('A');
|
|
162
|
+
if (profile.features.nativeQr || profile.set === 'star-graphic') printer.qr('https://github.com/arka162/thermal-print', { size: 5 });
|
|
163
|
+
else printer.text('(no native QR on this profile)');
|
|
164
|
+
printer.newLine().text('B').image(qrBitmap('https://github.com/arka162/thermal-print', { size: 5 })).newLine().align('lt');
|
|
165
|
+
section(printer, 'V8', 'Raster: checkerboard, then a diagonal line, edges sharp');
|
|
166
|
+
const bm = emptyBitmap(256, 64);
|
|
167
|
+
for (let y = 0; y < 64; y++) for (let x = 0; x < 256; x++) if (x < 128 ? ((x >> 3) + (y >> 3)) % 2 === 0 : Math.abs(x - 128 - y * 2) < 2) setPixel(bm, x, y, true);
|
|
168
|
+
printer.align('ct').image(bm).newLine().align('lt');
|
|
169
|
+
section(printer, 'V9', 'Partial cut follows this line');
|
|
170
|
+
printer.cut(true);
|
|
171
|
+
section(printer, 'V10', 'Full cut follows this line');
|
|
172
|
+
printer.text('End of verification page. Fill in docs/hardware-checklist.md.').cut(false);
|
|
173
|
+
await printer.flush();
|
|
174
|
+
if (args.drawer) {
|
|
175
|
+
printer.cashdraw(2);
|
|
176
|
+
await printer.flush();
|
|
177
|
+
await new Promise((r) => setTimeout(r, 1500));
|
|
178
|
+
printer.cashdraw(5);
|
|
179
|
+
await printer.flush();
|
|
180
|
+
console.log('V11 drawer: kicked pin 2, then pin 5 after 1.5 s');
|
|
181
|
+
}
|
|
182
|
+
await showStatus(printer);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function verifyLabel(args) {
|
|
186
|
+
const b = new LabelBuilder({ widthMm: Number(args['width-mm'] || 50.8), heightMm: Number(args['height-mm'] || 25.4), dpi: Number(args.dpi || 203) });
|
|
187
|
+
const w = b.label.width;
|
|
188
|
+
const h = b.label.height;
|
|
189
|
+
b.box(2, 2, w - 4, h - 4, 2)
|
|
190
|
+
.text(10, 8, 'L1 text 20', 20).text(10, 32, 'L2 text 32', 32)
|
|
191
|
+
.barcode(10, 72, '036000291452', 'UPC_A', 40)
|
|
192
|
+
.qr(w - 110, 8, 'https://github.com/arka162/thermal-print', { module: 3 })
|
|
193
|
+
.rect(10, h - 14, w - 20, 4)
|
|
194
|
+
.text(w - 150, h - 60, String(args.language || 'zpl').toUpperCase(), 40, { bold: true });
|
|
195
|
+
return b.label;
|
|
196
|
+
}
|
|
197
|
+
|
|
127
198
|
async function showStatus(printer) {
|
|
128
199
|
if (!printer.transport.read) return;
|
|
129
200
|
try {
|
|
@@ -144,6 +215,35 @@ async function main() {
|
|
|
144
215
|
return withPrinter(args, async (p) => { p.cashdraw(args.pin === '5' ? 5 : 2); });
|
|
145
216
|
case 'status':
|
|
146
217
|
return withPrinter(args, showStatus);
|
|
218
|
+
case 'verify':
|
|
219
|
+
return withPrinter(args, (p, profile) => verifyPage(p, profile, args));
|
|
220
|
+
case 'verify-label': {
|
|
221
|
+
const label = verifyLabel(args);
|
|
222
|
+
const language = String(args.language || 'zpl');
|
|
223
|
+
if (args.out) {
|
|
224
|
+
const out = String(args.out);
|
|
225
|
+
fs.writeFileSync(out, out.endsWith('.svg') ? labelToSvg(label) : Buffer.from(encodeLabel(label, language)));
|
|
226
|
+
console.log(`wrote ${out}`);
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
const t = transportFor(args);
|
|
230
|
+
await t.open();
|
|
231
|
+
try { await t.write(Buffer.from(encodeLabel(label, language))); } finally { await t.close(); }
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
case 'discover': {
|
|
235
|
+
const found = await discoverNetworkPrinters({ port: args.port ? Number(args.port) : undefined, identify: !!args.identify });
|
|
236
|
+
if (!found.length) console.log('no hosts answered on the printing port');
|
|
237
|
+
for (const p of found) {
|
|
238
|
+
const id = p.identity;
|
|
239
|
+
console.log(`${p.host}:${p.port}` + (id ? ` ${id.maker || '?'} ${id.model || ''} -> ${id.profile ? id.profile.id : 'unknown'}` : ''));
|
|
240
|
+
}
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
case 'image': {
|
|
244
|
+
const bm = await loadImage(String(args.file), { width: args.width ? Number(args.width) : undefined, dither: !!args.dither });
|
|
245
|
+
return withPrinter(args, async (p) => { p.align('ct').image(bm).newLine().cut(); });
|
|
246
|
+
}
|
|
147
247
|
case 'identify': {
|
|
148
248
|
const t = transportFor(args);
|
|
149
249
|
await t.open();
|