portakal-lite 1.0.0 → 2.0.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 +80 -7
- package/dist/index.cjs +463 -175
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +118 -6
- package/dist/index.d.ts +118 -6
- package/dist/index.mjs +457 -175
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@ Integrates [etiket](https://github.com/productdevbook/etiket) package for seamle
|
|
|
5
5
|
|
|
6
6
|
- Fluent `label()` builder — text, boxes, lines, circles, ellipses, reverse/erase regions, images, raw commands
|
|
7
7
|
- `.barcode()` / `.qrcode()` — 40+ symbologies via [etiket](https://github.com/productdevbook/etiket)
|
|
8
|
-
- `tsc.compile()` / `zpl.compile()` → **printer-ready
|
|
8
|
+
- `tsc.compile()` / `zpl.compile()` → **printer-ready output** — TSC as a `Uint8Array` (binary bitmap payload), ZPL as a string. No transport, no connection — you send it.
|
|
9
9
|
- `tsc.preview()` / `zpl.preview()` → SVG rendering with per-language font metrics
|
|
10
10
|
- Receipt layout helpers: `formatPair`, `formatRow`, `formatTable`, `separator`, `wordWrap`
|
|
11
11
|
- One runtime dependency (`etiket`, itself zero-dep), pure ESM, works in Node, browsers, Deno, Bun
|
|
@@ -72,25 +72,59 @@ Barcode options: `x`, `y`, `height`, `moduleWidth`, `ratio`, `rotation`, `readab
|
|
|
72
72
|
|
|
73
73
|
QR options: `x`, `y`, `cellSize`, `ecc` (`L`/`M`/`Q`/`H`), `rotation`, `version`, `mode`, `mask`, `eci`, `gs1`.
|
|
74
74
|
|
|
75
|
-
###
|
|
75
|
+
### Sending to a printer
|
|
76
76
|
|
|
77
|
-
|
|
77
|
+
`tsc.compile()` returns a **`Uint8Array`**, not a string: the text commands are
|
|
78
|
+
ASCII, but a `BITMAP` payload is raw packed pixels. Send those bytes as-is — the
|
|
79
|
+
compiler has already concatenated the whole stream for you. `zpl.compile()`
|
|
80
|
+
returns a string (ZPL has no binary payload).
|
|
78
81
|
|
|
79
82
|
```ts
|
|
80
83
|
import { label, tsc } from "portakal-lite";
|
|
81
84
|
import net from "node:net";
|
|
82
85
|
|
|
83
|
-
const
|
|
86
|
+
const bytes = tsc.compile(
|
|
84
87
|
label({ width: 40, height: 30 }).text("Hello", { x: 10, y: 10 }),
|
|
85
88
|
);
|
|
86
89
|
|
|
87
90
|
// TCP label printers usually listen on port 9100
|
|
88
91
|
const socket = net.createConnection({ host: "192.168.1.100", port: 9100 });
|
|
89
|
-
socket.write(
|
|
92
|
+
socket.write(bytes);
|
|
90
93
|
socket.end();
|
|
91
94
|
|
|
92
95
|
// ...or write to a file for a print spooler
|
|
93
|
-
// fs.writeFileSync("label.prn",
|
|
96
|
+
// fs.writeFileSync("label.prn", bytes);
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Do **not** decode the output to text before sending. `new TextDecoder().decode(bytes)`,
|
|
100
|
+
`String.fromCharCode(...bytes)`, `bytes.toString()` and `JSON.stringify(bytes)` all
|
|
101
|
+
mangle bytes ≥ `0x80` and change the total length, so the printer reads the wrong
|
|
102
|
+
byte count — images print as noise, while text-only labels still look fine
|
|
103
|
+
because those bytes are pure ASCII.
|
|
104
|
+
|
|
105
|
+
For HTTP, post the bytes directly (no encoding needed):
|
|
106
|
+
|
|
107
|
+
```ts
|
|
108
|
+
await fetch("/print", {
|
|
109
|
+
method: "POST",
|
|
110
|
+
headers: { "Content-Type": "application/octet-stream" },
|
|
111
|
+
body: bytes, // React Native: new Blob([bytes], { type: "application/octet-stream" })
|
|
112
|
+
});
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
If a transport only accepts a `string` (some BLE and Expo bridges), wrap the
|
|
116
|
+
bytes losslessly with base64 and let the transport decode them:
|
|
117
|
+
|
|
118
|
+
```ts
|
|
119
|
+
import { bytesToBase64, chunkBytes } from "portakal-lite";
|
|
120
|
+
|
|
121
|
+
await ble.write(deviceId, characteristicId, bytesToBase64(bytes));
|
|
122
|
+
|
|
123
|
+
// MTU-limited link? Split the BYTES, then encode each chunk — never split
|
|
124
|
+
// an encoded string.
|
|
125
|
+
for (const chunk of chunkBytes(bytes, 180)) {
|
|
126
|
+
await ble.write(deviceId, characteristicId, bytesToBase64(chunk));
|
|
127
|
+
}
|
|
94
128
|
```
|
|
95
129
|
|
|
96
130
|
TSC output uses LF (`\n`) line endings by default. If your printer firmware
|
|
@@ -103,6 +137,21 @@ const commands = tsc.compile(
|
|
|
103
137
|
);
|
|
104
138
|
```
|
|
105
139
|
|
|
140
|
+
### Showing the output
|
|
141
|
+
|
|
142
|
+
`formatTSCBytes` (or `tsc.text(builder)`) renders the stream for display — ASCII
|
|
143
|
+
commands verbatim, with any binary `BITMAP` payload elided. Use it for a UI, a
|
|
144
|
+
log, or a "show compiled commands" panel; never send it in place of the bytes.
|
|
145
|
+
|
|
146
|
+
```ts
|
|
147
|
+
import { formatTSCBytes, tsc } from "portakal-lite";
|
|
148
|
+
|
|
149
|
+
formatTSCBytes(bytes);
|
|
150
|
+
// 'SIZE 40 mm,30 mm\nCLS\nTEXT 10,10,"2",0,1,1,"Hello"\nPRINT 1\n'
|
|
151
|
+
|
|
152
|
+
tsc.text(builder); // an image label shows: '… BITMAP 10,10,2,16,0,<32 bytes of bitmap data>\nPRINT 1\n'
|
|
153
|
+
```
|
|
154
|
+
|
|
106
155
|
### Receipt-style aligned lines
|
|
107
156
|
|
|
108
157
|
```ts
|
|
@@ -150,6 +199,29 @@ const b = label({ width: 40, height: 30 })
|
|
|
150
199
|
|
|
151
200
|
`MonochromeBitmap` is a 1-bit packed `Uint8Array`: `{ data, width, height, bytesPerRow }` with `bytesPerRow === Math.ceil(width / 8)`.
|
|
152
201
|
|
|
202
|
+
### Images
|
|
203
|
+
|
|
204
|
+
Raster graphics need packed 1-bit pixels. `toMonochromeBitmap` converts raw
|
|
205
|
+
grayscale or RGB/RGBA pixels (e.g. a decoded photo) into that shape, with
|
|
206
|
+
threshold or error-diffusion dithering:
|
|
207
|
+
|
|
208
|
+
```ts
|
|
209
|
+
import { label, tsc, toMonochromeBitmap } from "portakal-lite";
|
|
210
|
+
|
|
211
|
+
// pixels: row-major, 1 (grayscale), 3 (RGB) or 4 (RGBA) bytes per pixel.
|
|
212
|
+
const bitmap = toMonochromeBitmap(pixels, width, height, {
|
|
213
|
+
dither: "floyd-steinberg", // "threshold" | "floyd-steinberg" | "atkinson" | "ordered"
|
|
214
|
+
threshold: 128, // luminance cutoff (default 128)
|
|
215
|
+
invert: false,
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
const myLabel = label({ width: 40, height: 30 }).image(bitmap, { x: 20, y: 60 });
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
RGBA is composited over white, so transparent areas print as unmarked paper.
|
|
222
|
+
Note that TSC `BITMAP` cannot scale — emit the bitmap at the size you want
|
|
223
|
+
printed (ZPL `^GFA` is fixed-size too). See [`examples/image-label.js`](./examples/image-label.js).
|
|
224
|
+
|
|
153
225
|
## Examples
|
|
154
226
|
|
|
155
227
|
Runnable examples live in [`examples/`](./examples) — build the package first (`npm run build`), then run any:
|
|
@@ -159,6 +231,7 @@ node examples/basic-label.js # text + box + Code 128 + QR, both language
|
|
|
159
231
|
node examples/shipping-label.js # shipping label with tracking barcode + QR
|
|
160
232
|
node examples/receipt-label.js # receipt-style label with order barcode
|
|
161
233
|
node examples/max-symbologies.js # native + rasterized (EAN-13, UPC-A, Code 39, ITF, DataMatrix, PDF417, Aztec)
|
|
234
|
+
node examples/image-label.js # dithered bitmap image (BITMAP / ^GFA)
|
|
162
235
|
```
|
|
163
236
|
|
|
164
237
|
## Security
|
|
@@ -177,7 +250,7 @@ The compilers are hardened against command injection — the most important thin
|
|
|
177
250
|
|
|
178
251
|
## Differences from portakal
|
|
179
252
|
|
|
180
|
-
`portakal-lite` keeps the label builder, TSC/ZPL compilers, per-language preview, receipt helpers, and adds barcode/QR support backed by etiket
|
|
253
|
+
`portakal-lite` keeps the label builder, TSC/ZPL compilers, per-language preview, receipt helpers, and adds barcode/QR support backed by etiket plus image dithering via `toMonochromeBitmap()`. It drops the other 7 languages, parsers, `validate()`, cross-compiler, encoding engine, and the transport layer. Behavior of the generated TSC/ZPL commands is identical to portakal's, plus the hardening above.
|
|
181
254
|
|
|
182
255
|
## License
|
|
183
256
|
|