portakal 0.3.0 → 0.4.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 +149 -162
- package/dist/_chunks/types.d.mts +34 -1
- package/dist/image.d.mts +1 -1
- package/dist/index.d.mts +780 -14
- package/dist/index.mjs +5 -1
- package/dist/languages/cpcl.d.mts +1 -1
- package/dist/languages/cpcl.mjs +1 -1
- package/dist/languages/dpl.d.mts +1 -1
- package/dist/languages/dpl.mjs +1 -1
- package/dist/languages/epl.d.mts +1 -1
- package/dist/languages/epl.mjs +1 -1
- package/dist/languages/escpos.d.mts +1 -1
- package/dist/languages/escpos.mjs +1 -1
- package/dist/languages/ipl.d.mts +1 -1
- package/dist/languages/ipl.mjs +1 -1
- package/dist/languages/sbpl.d.mts +1 -1
- package/dist/languages/sbpl.mjs +1 -1
- package/dist/languages/starprnt.d.mts +1 -1
- package/dist/languages/starprnt.mjs +1 -1
- package/dist/languages/tsc.d.mts +1 -1
- package/dist/languages/tsc.mjs +1 -1
- package/dist/languages/zpl.d.mts +1 -1
- package/dist/languages/zpl.mjs +1 -1
- package/dist/preview.d.mts +1 -1
- package/dist/preview.mjs +2 -2
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -30,37 +30,51 @@
|
|
|
30
30
|
npm install portakal
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
-
### Product label
|
|
33
|
+
### Product label
|
|
34
34
|
|
|
35
35
|
```ts
|
|
36
|
-
import { label } from "portakal";
|
|
36
|
+
import { label } from "portakal/core";
|
|
37
|
+
import { tsc } from "portakal/lang/tsc";
|
|
37
38
|
|
|
38
|
-
const
|
|
39
|
+
const myLabel = label({ width: 40, height: 30, unit: "mm" })
|
|
39
40
|
.text("ACME Corp", { x: 10, y: 10, size: 2 })
|
|
40
|
-
.text("SKU: PRD-00123", { x: 10, y: 35
|
|
41
|
-
.
|
|
42
|
-
.
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
41
|
+
.text("SKU: PRD-00123", { x: 10, y: 35 })
|
|
42
|
+
.line({ x1: 5, y1: 55, x2: 310, y2: 55 })
|
|
43
|
+
.box({ x: 5, y: 5, width: 310, height: 230, thickness: 2 });
|
|
44
|
+
|
|
45
|
+
const code = tsc.compile(myLabel); // TSC/TSPL2 commands
|
|
46
|
+
const svg = tsc.preview(myLabel); // SVG preview with TSC font metrics
|
|
46
47
|
```
|
|
47
48
|
|
|
48
|
-
###
|
|
49
|
+
### Same label → any language
|
|
49
50
|
|
|
50
51
|
```ts
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
52
|
+
import { label } from "portakal/core";
|
|
53
|
+
import { tsc } from "portakal/lang/tsc";
|
|
54
|
+
import { zpl } from "portakal/lang/zpl";
|
|
55
|
+
import { epl } from "portakal/lang/epl";
|
|
56
|
+
import { escpos } from "portakal/lang/escpos";
|
|
57
|
+
|
|
58
|
+
const myLabel = label({ width: 40, height: 30, unit: "mm" }).text("Hello World", {
|
|
59
|
+
x: 10,
|
|
60
|
+
y: 10,
|
|
61
|
+
size: 2,
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
tsc.compile(myLabel); // TSC/TSPL2 — TSC, Gprinter, Xprinter, iDPRT
|
|
65
|
+
zpl.compile(myLabel); // Zebra ZPL II — GK420, ZT410, ZD620
|
|
66
|
+
epl.compile(myLabel); // Eltron EPL2 — LP/TLP 2824, GX420, ZD220
|
|
67
|
+
escpos.compile(myLabel); // ESC/POS — Epson, Bixolon, Star, Citizen (Uint8Array)
|
|
59
68
|
```
|
|
60
69
|
|
|
61
|
-
|
|
70
|
+
Only the imported languages enter your bundle — 100% tree-shakeable.
|
|
71
|
+
|
|
72
|
+
### Receipt (ESC/POS)
|
|
62
73
|
|
|
63
74
|
```ts
|
|
75
|
+
import { label } from "portakal/core";
|
|
76
|
+
import { escpos } from "portakal/lang/escpos";
|
|
77
|
+
|
|
64
78
|
const receipt = label({ width: 80, unit: "mm" })
|
|
65
79
|
.text("MY STORE", { align: "center", bold: true, size: 2 })
|
|
66
80
|
.text("123 Market St", { align: "center" })
|
|
@@ -68,88 +82,50 @@ const receipt = label({ width: 80, unit: "mm" })
|
|
|
68
82
|
.text("Hamburger x2 $25.98")
|
|
69
83
|
.text("Cola x1 $3.50")
|
|
70
84
|
.text("================================")
|
|
71
|
-
.text("TOTAL $29.48", { bold: true, size: 2 })
|
|
72
|
-
.barcode("INV-20260402-001", { type: "code128", height: 60 })
|
|
73
|
-
.qrcode("https://receipt.example.com/inv/001")
|
|
74
|
-
.toESCPOS(); // Uint8Array (binary)
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
### Logo / image printing
|
|
85
|
+
.text("TOTAL $29.48", { bold: true, size: 2 });
|
|
78
86
|
|
|
79
|
-
|
|
80
|
-
const
|
|
81
|
-
.image(myLogoBitmap, { x: 10, y: 10, width: 100 })
|
|
82
|
-
.text("Company Name", { x: 120, y: 20, size: 2 })
|
|
83
|
-
.toTSC();
|
|
87
|
+
const bytes = escpos.compile(receipt); // Uint8Array
|
|
88
|
+
const svg = escpos.preview(receipt); // Receipt-style SVG
|
|
84
89
|
```
|
|
85
90
|
|
|
86
|
-
###
|
|
91
|
+
### Each module: compile + parse + preview + validate
|
|
87
92
|
|
|
88
93
|
```ts
|
|
89
|
-
|
|
90
|
-
.text("Hello World", { x: 10, y: 10, size: 2 })
|
|
91
|
-
.barcode("123456789", { type: "code128", x: 10, y: 50, height: 60 });
|
|
94
|
+
import { tsc } from "portakal/lang/tsc";
|
|
92
95
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
myLabel.toEPL(); // Eltron EPL2 — LP/TLP 2824, GX420, ZD220
|
|
96
|
-
myLabel.toESCPOS(); // ESC/POS — Epson, Bixolon, Star, Citizen
|
|
97
|
-
```
|
|
96
|
+
// Compile: label → printer commands
|
|
97
|
+
tsc.compile(myLabel);
|
|
98
98
|
|
|
99
|
-
|
|
99
|
+
// Preview: label → SVG (per-language font metrics)
|
|
100
|
+
tsc.preview(myLabel);
|
|
100
101
|
|
|
101
|
-
|
|
102
|
+
// Parse: printer commands → structured data
|
|
103
|
+
tsc.parse(tscCode); // { commands, elements, widthDots, ... }
|
|
102
104
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
import { compileToZPL } from "portakal/zpl";
|
|
106
|
-
import { compileToEPL } from "portakal/epl";
|
|
107
|
-
import { compileToESCPOS } from "portakal/escpos";
|
|
108
|
-
import { compileToCPCL } from "portakal/cpcl";
|
|
109
|
-
import { compileToDPL } from "portakal/dpl";
|
|
110
|
-
import { compileToSBPL } from "portakal/sbpl";
|
|
111
|
-
import { compileToStarPRNT } from "portakal/starprnt";
|
|
112
|
-
import { compileToIPL } from "portakal/ipl";
|
|
113
|
-
import { imageToMonochrome } from "portakal/image";
|
|
114
|
-
import { formatPair, separator } from "portakal/receipt";
|
|
115
|
-
import { encodeTextForPrinter } from "portakal/encoding";
|
|
116
|
-
import { getProfile } from "portakal/profiles";
|
|
117
|
-
import { chunkedWrite } from "portakal/transport";
|
|
118
|
-
import { renderPreview } from "portakal/preview";
|
|
105
|
+
// Validate: check for errors
|
|
106
|
+
tsc.validate(tscCode); // { valid, errors, issues }
|
|
119
107
|
```
|
|
120
108
|
|
|
121
|
-
|
|
109
|
+
Available: `tsc`, `zpl`, `epl`, `cpcl`, `dpl`, `sbpl`, `escpos`, `starprnt`, `ipl`
|
|
122
110
|
|
|
123
|
-
|
|
111
|
+
### Barcode/QR via etiket
|
|
124
112
|
|
|
125
|
-
|
|
126
|
-
npm install portakal etiket
|
|
127
|
-
```
|
|
113
|
+
Use [`etiket`](https://github.com/productdevbook/etiket) for barcode/QR generation, then embed as image:
|
|
128
114
|
|
|
129
115
|
```ts
|
|
130
|
-
import { label } from "portakal";
|
|
116
|
+
import { label } from "portakal/core";
|
|
117
|
+
import { tsc } from "portakal/lang/tsc";
|
|
131
118
|
import { barcodePNG, qrcodePNG } from "etiket";
|
|
132
119
|
|
|
133
|
-
|
|
134
|
-
const cmd = label({ width: 40, height: 30, unit: "mm" })
|
|
120
|
+
const myLabel = label({ width: 40, height: 30, unit: "mm" })
|
|
135
121
|
.text("Product Label", { x: 10, y: 5 })
|
|
136
122
|
.image(barcodePNG("123456789", { type: "code128" }), { x: 10, y: 40, width: 200 })
|
|
137
|
-
.image(qrcodePNG("https://example.com"), { x: 220, y: 40, width: 80 })
|
|
138
|
-
.toZPL();
|
|
139
|
-
```
|
|
123
|
+
.image(qrcodePNG("https://example.com"), { x: 220, y: 40, width: 80 });
|
|
140
124
|
|
|
141
|
-
|
|
125
|
+
const code = tsc.compile(myLabel);
|
|
126
|
+
```
|
|
142
127
|
|
|
143
|
-
|
|
|
144
|
-
| :------------------------------------ | :------------------------------------------ | :---------------------------------------- |
|
|
145
|
-
| **Dependencies** | None | `etiket` (you install it) |
|
|
146
|
-
| **Speed** | Fast (only sends command text) | Slower (sends pixel data) |
|
|
147
|
-
| **Data size** | Small (~50 bytes) | Larger (bitmap data) |
|
|
148
|
-
| **Consistency** | Varies by printer model | Identical on every printer |
|
|
149
|
-
| **Format support** | Depends on printer (10-20 types) | 40+ barcode types, styled QR |
|
|
150
|
-
| **Styled QR** (dots, gradients, logo) | Not possible | Full support via etiket |
|
|
151
|
-
| **Works on cheap printers** | May not support QR/PDF417 | Always works (it's just an image) |
|
|
152
|
-
| **Best for** | Simple labels, fast printing | Pixel-perfect, guaranteed output |
|
|
128
|
+
| **Best for** | Simple labels, fast printing | Pixel-perfect, guaranteed output |
|
|
153
129
|
|
|
154
130
|
## API
|
|
155
131
|
|
|
@@ -187,56 +163,6 @@ builder.text("Hello", {
|
|
|
187
163
|
});
|
|
188
164
|
```
|
|
189
165
|
|
|
190
|
-
### `.barcode(data, options)`
|
|
191
|
-
|
|
192
|
-
Printer-native barcode (uses the printer's built-in encoder):
|
|
193
|
-
|
|
194
|
-
```ts
|
|
195
|
-
builder.barcode("123456789", {
|
|
196
|
-
type: "code128", // Symbology (see table below)
|
|
197
|
-
x: 10,
|
|
198
|
-
y: 50, // Position
|
|
199
|
-
height: 80, // Bar height in dots
|
|
200
|
-
narrowWidth: 2, // Narrow bar width
|
|
201
|
-
wideWidth: 4, // Wide bar width
|
|
202
|
-
readable: true, // Show human-readable text
|
|
203
|
-
readablePosition: "below", // "none" | "above" | "below" | "both"
|
|
204
|
-
rotation: 0, // 0 | 90 | 180 | 270
|
|
205
|
-
});
|
|
206
|
-
```
|
|
207
|
-
|
|
208
|
-
**Supported barcode types:**
|
|
209
|
-
|
|
210
|
-
| Type | Description |
|
|
211
|
-
| :----------------------------------- | :--------------------- |
|
|
212
|
-
| `code128` | Code 128 (auto subset) |
|
|
213
|
-
| `code128a` / `code128b` / `code128c` | Code 128 subsets |
|
|
214
|
-
| `code39` | Code 39 |
|
|
215
|
-
| `code93` | Code 93 |
|
|
216
|
-
| `ean13` / `ean8` | EAN-13 / EAN-8 |
|
|
217
|
-
| `upca` / `upce` | UPC-A / UPC-E |
|
|
218
|
-
| `itf` / `itf14` | Interleaved 2 of 5 |
|
|
219
|
-
| `codabar` | Codabar |
|
|
220
|
-
| `msi` | MSI Plessey |
|
|
221
|
-
| `plessey` | Plessey |
|
|
222
|
-
| `code11` | Code 11 |
|
|
223
|
-
| `postnet` / `planet` | USPS Postnet / Planet |
|
|
224
|
-
| `gs1_128` | GS1-128 |
|
|
225
|
-
| `gs1_databar` | GS1 DataBar |
|
|
226
|
-
|
|
227
|
-
### `.qrcode(data, options?)`
|
|
228
|
-
|
|
229
|
-
```ts
|
|
230
|
-
builder.qrcode("https://example.com", {
|
|
231
|
-
x: 10,
|
|
232
|
-
y: 100, // Position
|
|
233
|
-
ecc: "M", // "L" | "M" | "Q" | "H"
|
|
234
|
-
size: 6, // Module size 1-10
|
|
235
|
-
model: 2, // QR model 1 | 2
|
|
236
|
-
rotation: 0, // 0 | 90 | 180 | 270
|
|
237
|
-
});
|
|
238
|
-
```
|
|
239
|
-
|
|
240
166
|
### `.image(bitmap, options?)`
|
|
241
167
|
|
|
242
168
|
```ts
|
|
@@ -277,20 +203,16 @@ builder.raw("^FO10,10^FDCustom^FS"); // ZPL
|
|
|
277
203
|
builder.raw(new Uint8Array([0x1b, 0x70, 0x00, 0x32, 0x32])); // ESC/POS cash drawer
|
|
278
204
|
```
|
|
279
205
|
|
|
280
|
-
###
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
|
285
|
-
|
|
|
286
|
-
|
|
|
287
|
-
|
|
|
288
|
-
|
|
|
289
|
-
|
|
|
290
|
-
| `.toESCPOS()` | `Uint8Array` | ESC/POS receipt printers |
|
|
291
|
-
| `.toStarPRNT()` | `Uint8Array` | Star Micronics printers |
|
|
292
|
-
| `.toIPL()` | `string` | Intermec/Honeywell printers |
|
|
293
|
-
| `.toPreview()` | `string` | SVG preview (no printer needed) |
|
|
206
|
+
### Language Module Methods
|
|
207
|
+
|
|
208
|
+
Each language module (`tsc`, `zpl`, `epl`, `cpcl`, `dpl`, `sbpl`, `escpos`, `starprnt`, `ipl`) has:
|
|
209
|
+
|
|
210
|
+
| Method | Output | Description |
|
|
211
|
+
| :-------------------- | :----------------------- | :--------------------------------------- |
|
|
212
|
+
| `lang.compile(label)` | `string` or `Uint8Array` | Compile to printer commands |
|
|
213
|
+
| `lang.preview(label)` | `string` | SVG preview with language-specific fonts |
|
|
214
|
+
| `lang.parse(code)` | `object` | Parse printer commands → structured data |
|
|
215
|
+
| `lang.validate(code)` | `object` | Validate commands for errors/warnings |
|
|
294
216
|
|
|
295
217
|
### Image Processing
|
|
296
218
|
|
|
@@ -303,7 +225,7 @@ const bitmap = imageToMonochrome(rgbaPixels, width, height, {
|
|
|
303
225
|
dither: "floyd-steinberg", // "threshold" | "floyd-steinberg" | "atkinson" | "ordered"
|
|
304
226
|
});
|
|
305
227
|
|
|
306
|
-
label({ width: 40, height: 30 }).image(bitmap, { x: 10, y: 10 })
|
|
228
|
+
tsc.compile(label({ width: 40, height: 30 }).image(bitmap, { x: 10, y: 10 }));
|
|
307
229
|
```
|
|
308
230
|
|
|
309
231
|
### Receipt Layout
|
|
@@ -334,6 +256,60 @@ formatTable(
|
|
|
334
256
|
);
|
|
335
257
|
```
|
|
336
258
|
|
|
259
|
+
### Cross-Compiler
|
|
260
|
+
|
|
261
|
+
Convert between any printer languages — world's first thermal printer translator:
|
|
262
|
+
|
|
263
|
+
```ts
|
|
264
|
+
import { convert } from "portakal";
|
|
265
|
+
|
|
266
|
+
// TSC → ZPL
|
|
267
|
+
const { output } = convert(tscCode, "tsc", "zpl");
|
|
268
|
+
|
|
269
|
+
// ZPL → ESC/POS
|
|
270
|
+
const { output } = convert(zplCode, "zpl", "escpos");
|
|
271
|
+
|
|
272
|
+
// EPL → CPCL
|
|
273
|
+
const { output } = convert(eplCode, "epl", "cpcl");
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
7 source × 9 target = **63 conversion paths**.
|
|
277
|
+
|
|
278
|
+
### Validation
|
|
279
|
+
|
|
280
|
+
Check printer commands for errors before sending to printer:
|
|
281
|
+
|
|
282
|
+
```ts
|
|
283
|
+
import { validate } from "portakal";
|
|
284
|
+
|
|
285
|
+
const result = validate(code, "tsc");
|
|
286
|
+
// { valid: false, errors: 1, warnings: 2, issues: [
|
|
287
|
+
// { level: "error", message: "CLS must appear before label elements" },
|
|
288
|
+
// { level: "warning", message: "No PRINT command found" },
|
|
289
|
+
// ]}
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
TSC validation: SIZE order, CLS before elements, PRINT required, DENSITY 0-15, SPEED 1-18.
|
|
293
|
+
ZPL validation: ^XA/^XZ required, ^FD without ^FO, ^PW range.
|
|
294
|
+
|
|
295
|
+
### Printer Profiles
|
|
296
|
+
|
|
297
|
+
Auto-configure DPI, paper width, and capabilities based on printer model:
|
|
298
|
+
|
|
299
|
+
```ts
|
|
300
|
+
import { label, getProfile, findByVendorId } from "portakal";
|
|
301
|
+
|
|
302
|
+
// Auto-DPI from profile
|
|
303
|
+
label({ width: 40, height: 30, printer: "tsc-te310" }); // 300 DPI
|
|
304
|
+
label({ width: 80, printer: "epson-tm-t88vi" }); // 203 DPI
|
|
305
|
+
|
|
306
|
+
// Lookup profiles
|
|
307
|
+
getProfile("zebra-zd420"); // { name, dpi, paperWidth, ... }
|
|
308
|
+
findByVendorId(0x04b8); // All Epson printers
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
20 built-in profiles: Epson, Star, Bixolon, Citizen, TSC, Zebra, SATO, Honeywell, Generic.
|
|
312
|
+
|
|
337
313
|
## Supported Printer Languages
|
|
338
314
|
|
|
339
315
|
| Language | Printers | Status |
|
|
@@ -355,24 +331,23 @@ formatTable(
|
|
|
355
331
|
portakal generates commands only — it does **not** handle printer connections. Send the output over any transport you choose:
|
|
356
332
|
|
|
357
333
|
```ts
|
|
358
|
-
import { label } from "portakal";
|
|
334
|
+
import { label } from "portakal/core";
|
|
335
|
+
import { tsc } from "portakal/lang/tsc";
|
|
336
|
+
import { escpos } from "portakal/lang/escpos";
|
|
359
337
|
import net from "node:net";
|
|
360
338
|
|
|
361
|
-
const
|
|
339
|
+
const myLabel = label({ width: 40, height: 30 }).text("Hello", { x: 10, y: 10 });
|
|
340
|
+
const commands = tsc.compile(myLabel);
|
|
362
341
|
|
|
363
342
|
// TCP (port 9100)
|
|
364
343
|
const socket = net.createConnection({ host: "192.168.1.100", port: 9100 });
|
|
365
344
|
socket.write(commands);
|
|
366
345
|
socket.end();
|
|
367
346
|
|
|
368
|
-
// USB (via serialport)
|
|
369
|
-
import { SerialPort } from "serialport";
|
|
370
|
-
const port = new SerialPort({ path: "/dev/usb/lp0", baudRate: 9600 });
|
|
371
|
-
port.write(commands);
|
|
372
|
-
|
|
373
347
|
// ESC/POS (binary) over WebUSB
|
|
374
|
-
const
|
|
375
|
-
|
|
348
|
+
const receipt = label({ width: 80 }).text("Receipt");
|
|
349
|
+
const bytes = escpos.compile(receipt);
|
|
350
|
+
await usbDevice.transferOut(endpointNumber, bytes);
|
|
376
351
|
```
|
|
377
352
|
|
|
378
353
|
## Comparison
|
|
@@ -390,6 +365,10 @@ await usbDevice.transferOut(endpointNumber, escpos);
|
|
|
390
365
|
| Image dithering | :white_check_mark: | :x: | :x: | :x: |
|
|
391
366
|
| Receipt layout engine | :white_check_mark: | Partial | :x: | :x: |
|
|
392
367
|
| SVG preview | :white_check_mark: | :x: | :x: | :x: |
|
|
368
|
+
| Command parser (reverse) | :white_check_mark: 9 parsers | :x: | :x: | :x: |
|
|
369
|
+
| Cross-compiler (translate) | :white_check_mark: 63 paths | :x: | :x: | :x: |
|
|
370
|
+
| Command validation | :white_check_mark: | :x: | :x: | :x: |
|
|
371
|
+
| Printer profiles | :white_check_mark: 20 | :x: | :x: | :x: |
|
|
393
372
|
| Works in browser | :white_check_mark: | :x: | :x: | :white_check_mark: |
|
|
394
373
|
| No native modules (no gyp) | :white_check_mark: | :x: | :x: | :white_check_mark: |
|
|
395
374
|
| Pure ESM | :white_check_mark: | :x: (CJS) | :x: (CJS) | :x: (CJS) |
|
|
@@ -407,24 +386,32 @@ await usbDevice.transferOut(endpointNumber, escpos);
|
|
|
407
386
|
- Fluent builder API — one label definition compiles to any language
|
|
408
387
|
- **Image processing** — RGBA → monochrome with 4 dithering algorithms (Floyd-Steinberg, Atkinson, ordered, threshold)
|
|
409
388
|
- **Receipt layout engine** — same-line left+right alignment, tables, word-wrap, separators
|
|
410
|
-
- **SVG preview** —
|
|
389
|
+
- **SVG preview** — `lang.preview(label)` renders labels without a physical printer
|
|
390
|
+
- **9 parsers** — reverse-parse printer commands back to structured data (TSC, ZPL, EPL, CPCL, ESC/POS, DPL, SBPL, Star PRNT, IPL)
|
|
411
391
|
- Drawing primitives — box, line, circle, diagonal
|
|
412
392
|
- Raw command passthrough for advanced/unsupported features
|
|
413
393
|
- Optional [`etiket`](https://github.com/productdevbook/etiket) integration for barcode/QR images (40+ formats)
|
|
414
394
|
- Works in browser, Node.js, Deno, Bun, Electron
|
|
415
395
|
- **UTF-8 encoding engine** — auto code page selection (CP437, CP858, CP1252, CP866, CP857)
|
|
416
|
-
-
|
|
396
|
+
- **Cross-compiler** — convert between any languages (63 paths: TSC↔ZPL↔EPL↔CPCL↔DPL↔SBPL↔IPL↔ESC/POS↔Star)
|
|
397
|
+
- **Real validation** — parameter ranges, command order, structure checks
|
|
398
|
+
- **20 printer profiles** — auto-DPI, auto-width by model (Epson, Star, Zebra, TSC, SATO, etc.)
|
|
399
|
+
- **Language modules** — each language is a standalone module (compile + parse + preview + validate)
|
|
400
|
+
- **Per-language SVG preview** — TSC fonts differ from ZPL fonts, ESC/POS renders receipt-style
|
|
401
|
+
- 447 tests across 28 test files
|
|
417
402
|
|
|
418
403
|
## Contributing
|
|
419
404
|
|
|
420
405
|
Contributions are welcome! Here are areas where help is especially appreciated:
|
|
421
406
|
|
|
422
|
-
- **
|
|
423
|
-
-
|
|
424
|
-
-
|
|
425
|
-
-
|
|
426
|
-
-
|
|
427
|
-
-
|
|
407
|
+
- **Arabic/Hebrew RTL** support (bidi algorithm + Arabic shaping)
|
|
408
|
+
- **GS1/UDI label standards** (SSCC, GTIN, FMD, DSCSA templates)
|
|
409
|
+
- **Star TSP100** raster-only text rendering
|
|
410
|
+
- **CJK encoding** (GB18030, Shift_JIS, Big5, EUC-KR)
|
|
411
|
+
- **Fingerprint** (Honeywell BASIC-like) compiler
|
|
412
|
+
- **WebUSB/WebSerial/Web Bluetooth** transport adapters
|
|
413
|
+
- Additional printer profiles
|
|
414
|
+
- Parser validation rules for more languages
|
|
428
415
|
|
|
429
416
|
```bash
|
|
430
417
|
pnpm install # Install dependencies
|
package/dist/_chunks/types.d.mts
CHANGED
|
@@ -9,6 +9,8 @@ type Alignment = "left" | "center" | "right";
|
|
|
9
9
|
type DitherAlgorithm = "threshold" | "floyd-steinberg" | "atkinson" | "ordered";
|
|
10
10
|
/** Label configuration */
|
|
11
11
|
interface LabelConfig {
|
|
12
|
+
/** Printer profile ID (e.g., "epson-tm-t88vi", "zebra-zd420"). Overrides width, dpi, etc. */
|
|
13
|
+
printer?: string;
|
|
12
14
|
/** Label width */
|
|
13
15
|
width: number;
|
|
14
16
|
/** Label height (optional for receipt/continuous mode) */
|
|
@@ -122,6 +124,28 @@ interface MonochromeBitmap {
|
|
|
122
124
|
/** Bytes per row = ceil(width / 8) */
|
|
123
125
|
bytesPerRow: number;
|
|
124
126
|
}
|
|
127
|
+
/** Ellipse element options */
|
|
128
|
+
interface EllipseOptions {
|
|
129
|
+
x: number;
|
|
130
|
+
y: number;
|
|
131
|
+
width: number;
|
|
132
|
+
height: number;
|
|
133
|
+
thickness?: number;
|
|
134
|
+
}
|
|
135
|
+
/** Reverse region options (invert black/white) */
|
|
136
|
+
interface ReverseOptions {
|
|
137
|
+
x: number;
|
|
138
|
+
y: number;
|
|
139
|
+
width: number;
|
|
140
|
+
height: number;
|
|
141
|
+
}
|
|
142
|
+
/** Erase region options (clear to white) */
|
|
143
|
+
interface EraseOptions {
|
|
144
|
+
x: number;
|
|
145
|
+
y: number;
|
|
146
|
+
width: number;
|
|
147
|
+
height: number;
|
|
148
|
+
}
|
|
125
149
|
/** Internal label element union type */
|
|
126
150
|
type LabelElement = {
|
|
127
151
|
type: "text";
|
|
@@ -140,6 +164,15 @@ type LabelElement = {
|
|
|
140
164
|
} | {
|
|
141
165
|
type: "circle";
|
|
142
166
|
options: CircleOptions;
|
|
167
|
+
} | {
|
|
168
|
+
type: "ellipse";
|
|
169
|
+
options: EllipseOptions;
|
|
170
|
+
} | {
|
|
171
|
+
type: "reverse";
|
|
172
|
+
options: ReverseOptions;
|
|
173
|
+
} | {
|
|
174
|
+
type: "erase";
|
|
175
|
+
options: EraseOptions;
|
|
143
176
|
} | {
|
|
144
177
|
type: "raw";
|
|
145
178
|
content: string | Uint8Array;
|
|
@@ -166,4 +199,4 @@ interface ResolvedLabel {
|
|
|
166
199
|
elements: LabelElement[];
|
|
167
200
|
}
|
|
168
201
|
//#endregion
|
|
169
|
-
export {
|
|
202
|
+
export { EllipseOptions as a, LabelConfig as c, MonochromeBitmap as d, ResolvedLabel as f, Unit as g, TextOptions as h, DitherAlgorithm as i, LabelElement as l, Rotation as m, BoxOptions as n, EraseOptions as o, ReverseOptions as p, CircleOptions as r, ImageOptions as s, Alignment as t, LineOptions as u };
|
package/dist/image.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { d as MonochromeBitmap, i as DitherAlgorithm } from "./_chunks/types.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/image.d.ts
|
|
4
4
|
/** Convert RGBA pixel data to grayscale (BT.601 luminance, alpha composited on white) */
|