taro-bluetooth-print 2.2.0 → 2.3.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/CHANGELOG.md +38 -0
- package/README.md +128 -22
- package/dist/index.cjs.js +1 -1
- package/dist/index.es.js +1 -6995
- package/dist/index.umd.js +1 -1
- package/dist/types/adapters/AdapterFactory.d.ts +0 -1
- package/dist/types/adapters/AlipayAdapter.d.ts +6 -34
- package/dist/types/adapters/BaiduAdapter.d.ts +6 -34
- package/dist/types/adapters/BaseAdapter.d.ts +112 -1
- package/dist/types/adapters/ByteDanceAdapter.d.ts +6 -34
- package/dist/types/adapters/TaroAdapter.d.ts +6 -34
- package/dist/types/adapters/WebBluetoothAdapter.d.ts +0 -1
- package/dist/types/config/PrinterConfig.d.ts +0 -1
- package/dist/types/core/BluetoothPrinter.d.ts +0 -1
- package/dist/types/drivers/EscPos.d.ts +0 -1
- package/dist/types/drivers/TsplDriver.d.ts +251 -0
- package/dist/types/encoding/gbk-data.d.ts +12 -0
- package/dist/types/encoding/gbk-table.d.ts +5 -1
- package/dist/types/index.d.ts +5 -0
- package/dist/types/plugins/PluginManager.d.ts +87 -0
- package/dist/types/plugins/builtin/LoggingPlugin.d.ts +14 -0
- package/dist/types/plugins/builtin/RetryPlugin.d.ts +18 -0
- package/dist/types/plugins/index.d.ts +7 -0
- package/dist/types/plugins/types.d.ts +97 -0
- package/dist/types/services/CommandBuilder.d.ts +6 -1
- package/dist/types/services/ConnectionManager.d.ts +0 -1
- package/dist/types/services/PrintJobManager.d.ts +6 -2
- package/dist/types/services/interfaces/index.d.ts +0 -1
- package/dist/types/template/TemplateEngine.d.ts +0 -1
- package/package.json +16 -18
- package/src/adapters/AlipayAdapter.ts +8 -314
- package/src/adapters/BaiduAdapter.ts +8 -312
- package/src/adapters/BaseAdapter.ts +366 -0
- package/src/adapters/ByteDanceAdapter.ts +8 -316
- package/src/adapters/TaroAdapter.ts +8 -367
- package/src/core/EventEmitter.ts +9 -6
- package/src/drivers/TsplDriver.ts +417 -0
- package/src/encoding/gbk-data.ts +1911 -0
- package/src/encoding/gbk-table.ts +22 -498
- package/src/index.ts +14 -0
- package/src/plugins/PluginManager.ts +193 -0
- package/src/plugins/builtin/LoggingPlugin.ts +97 -0
- package/src/plugins/builtin/RetryPlugin.ts +109 -0
- package/src/plugins/index.ts +10 -0
- package/src/plugins/types.ts +119 -0
- package/src/preview/PreviewRenderer.ts +7 -1
- package/src/queue/PrintQueue.ts +10 -6
- package/src/services/CommandBuilder.ts +30 -0
- package/src/services/PrintJobManager.ts +51 -35
|
@@ -0,0 +1,417 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TSPL Driver
|
|
3
|
+
* TSC Printer Language driver for label/barcode printers
|
|
4
|
+
*
|
|
5
|
+
* TSPL is commonly used in thermal transfer label printers (TSC, Zebra, etc.)
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { Logger } from '@/utils/logger';
|
|
9
|
+
import { Encoding } from '@/utils/encoding';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Label size configuration
|
|
13
|
+
*/
|
|
14
|
+
export interface LabelSize {
|
|
15
|
+
/** Label width in mm */
|
|
16
|
+
width: number;
|
|
17
|
+
/** Label height in mm */
|
|
18
|
+
height: number;
|
|
19
|
+
/** Gap between labels in mm (default: 3) */
|
|
20
|
+
gap?: number;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Text position and style
|
|
25
|
+
*/
|
|
26
|
+
export interface TextOptions {
|
|
27
|
+
/** X position in dots */
|
|
28
|
+
x: number;
|
|
29
|
+
/** Y position in dots */
|
|
30
|
+
y: number;
|
|
31
|
+
/** Font size (1-8, default: 2) */
|
|
32
|
+
font?: number;
|
|
33
|
+
/** Rotation (0, 90, 180, 270, default: 0) */
|
|
34
|
+
rotation?: 0 | 90 | 180 | 270;
|
|
35
|
+
/** Horizontal multiplier (1-10, default: 1) */
|
|
36
|
+
xMultiplier?: number;
|
|
37
|
+
/** Vertical multiplier (1-10, default: 1) */
|
|
38
|
+
yMultiplier?: number;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Barcode options
|
|
43
|
+
*/
|
|
44
|
+
export interface BarcodeOptions {
|
|
45
|
+
/** X position in dots */
|
|
46
|
+
x: number;
|
|
47
|
+
/** Y position in dots */
|
|
48
|
+
y: number;
|
|
49
|
+
/** Barcode type */
|
|
50
|
+
type: '128' | '39' | 'EAN13' | 'EAN8' | 'UPCA' | 'QRCODE';
|
|
51
|
+
/** Height in dots (default: 100) */
|
|
52
|
+
height?: number;
|
|
53
|
+
/** Narrow bar width (default: 2) */
|
|
54
|
+
narrow?: number;
|
|
55
|
+
/** Wide bar width (default: 4) */
|
|
56
|
+
wide?: number;
|
|
57
|
+
/** Show human-readable text (default: true) */
|
|
58
|
+
showText?: boolean;
|
|
59
|
+
/** Rotation (0, 90, 180, 270, default: 0) */
|
|
60
|
+
rotation?: 0 | 90 | 180 | 270;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* QR Code options
|
|
65
|
+
*/
|
|
66
|
+
export interface QRCodeOptions {
|
|
67
|
+
/** X position in dots */
|
|
68
|
+
x: number;
|
|
69
|
+
/** Y position in dots */
|
|
70
|
+
y: number;
|
|
71
|
+
/** Error correction level (L, M, Q, H, default: M) */
|
|
72
|
+
eccLevel?: 'L' | 'M' | 'Q' | 'H';
|
|
73
|
+
/** Cell width (1-10, default: 6) */
|
|
74
|
+
cellWidth?: number;
|
|
75
|
+
/** Mode (A=Auto, M=Manual, default: A) */
|
|
76
|
+
mode?: 'A' | 'M';
|
|
77
|
+
/** Rotation (0, 90, 180, 270, default: 0) */
|
|
78
|
+
rotation?: 0 | 90 | 180 | 270;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Box/Rectangle options
|
|
83
|
+
*/
|
|
84
|
+
export interface BoxOptions {
|
|
85
|
+
/** X position in dots */
|
|
86
|
+
x: number;
|
|
87
|
+
/** Y position in dots */
|
|
88
|
+
y: number;
|
|
89
|
+
/** Width in dots */
|
|
90
|
+
width: number;
|
|
91
|
+
/** Height in dots */
|
|
92
|
+
height: number;
|
|
93
|
+
/** Line thickness (default: 2) */
|
|
94
|
+
thickness?: number;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Line options
|
|
99
|
+
*/
|
|
100
|
+
export interface LineOptions {
|
|
101
|
+
/** Start X position in dots */
|
|
102
|
+
x1: number;
|
|
103
|
+
/** Start Y position in dots */
|
|
104
|
+
y1: number;
|
|
105
|
+
/** End X position in dots */
|
|
106
|
+
x2: number;
|
|
107
|
+
/** End Y position in dots */
|
|
108
|
+
y2: number;
|
|
109
|
+
/** Line thickness (default: 2) */
|
|
110
|
+
thickness?: number;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* TSPL Driver for label printers
|
|
115
|
+
*
|
|
116
|
+
* @example
|
|
117
|
+
* ```typescript
|
|
118
|
+
* const tspl = new TsplDriver();
|
|
119
|
+
*
|
|
120
|
+
* const commands = tspl
|
|
121
|
+
* .size(60, 40)
|
|
122
|
+
* .gap(3)
|
|
123
|
+
* .clear()
|
|
124
|
+
* .text('Product Name', { x: 50, y: 50, font: 3 })
|
|
125
|
+
* .barcode('1234567890', { x: 50, y: 100, type: '128' })
|
|
126
|
+
* .print(1)
|
|
127
|
+
* .getBuffer();
|
|
128
|
+
* ```
|
|
129
|
+
*/
|
|
130
|
+
export class TsplDriver {
|
|
131
|
+
private commands: string[] = [];
|
|
132
|
+
private readonly logger = Logger.scope('TsplDriver');
|
|
133
|
+
private dpi = 203; // Default DPI for most label printers
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Set printer DPI (dots per inch)
|
|
137
|
+
* @param dpi - DPI value (203 or 300)
|
|
138
|
+
*/
|
|
139
|
+
setDPI(dpi: 203 | 300): this {
|
|
140
|
+
this.dpi = dpi;
|
|
141
|
+
return this;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Convert mm to dots
|
|
146
|
+
*/
|
|
147
|
+
mmToDots(mm: number): number {
|
|
148
|
+
return Math.round((mm * this.dpi) / 25.4);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Convert dots to mm
|
|
153
|
+
*/
|
|
154
|
+
dotsToMm(dots: number): number {
|
|
155
|
+
return (dots * 25.4) / this.dpi;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Set label size
|
|
160
|
+
* @param width - Label width in mm
|
|
161
|
+
* @param height - Label height in mm
|
|
162
|
+
*/
|
|
163
|
+
size(width: number, height: number): this {
|
|
164
|
+
this.commands.push(`SIZE ${width} mm, ${height} mm`);
|
|
165
|
+
return this;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Set gap between labels
|
|
170
|
+
* @param gap - Gap size in mm
|
|
171
|
+
* @param offset - Offset in mm (default: 0)
|
|
172
|
+
*/
|
|
173
|
+
gap(gap: number, offset = 0): this {
|
|
174
|
+
this.commands.push(`GAP ${gap} mm, ${offset} mm`);
|
|
175
|
+
return this;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Set print speed
|
|
180
|
+
* @param speed - Speed level (1-10)
|
|
181
|
+
*/
|
|
182
|
+
speed(speed: number): this {
|
|
183
|
+
this.commands.push(`SPEED ${Math.min(10, Math.max(1, speed))}`);
|
|
184
|
+
return this;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Set print density
|
|
189
|
+
* @param density - Density level (0-15)
|
|
190
|
+
*/
|
|
191
|
+
density(density: number): this {
|
|
192
|
+
this.commands.push(`DENSITY ${Math.min(15, Math.max(0, density))}`);
|
|
193
|
+
return this;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Set print direction
|
|
198
|
+
* @param direction - 0=normal, 1=reversed
|
|
199
|
+
*/
|
|
200
|
+
direction(direction: 0 | 1): this {
|
|
201
|
+
this.commands.push(`DIRECTION ${direction}`);
|
|
202
|
+
return this;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Clear image buffer
|
|
207
|
+
*/
|
|
208
|
+
clear(): this {
|
|
209
|
+
this.commands.push('CLS');
|
|
210
|
+
return this;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Add text to label
|
|
215
|
+
* @param content - Text content
|
|
216
|
+
* @param options - Text options
|
|
217
|
+
*/
|
|
218
|
+
text(content: string, options: TextOptions): this {
|
|
219
|
+
const {
|
|
220
|
+
x,
|
|
221
|
+
y,
|
|
222
|
+
font = 2,
|
|
223
|
+
rotation = 0,
|
|
224
|
+
xMultiplier = 1,
|
|
225
|
+
yMultiplier = 1,
|
|
226
|
+
} = options;
|
|
227
|
+
|
|
228
|
+
// TEXT x, y, "font", rotation, x-mul, y-mul, "content"
|
|
229
|
+
this.commands.push(
|
|
230
|
+
`TEXT ${x},${y},"${font}",${rotation},${xMultiplier},${yMultiplier},"${this.escapeString(content)}"`
|
|
231
|
+
);
|
|
232
|
+
return this;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Add barcode to label
|
|
237
|
+
* @param content - Barcode content
|
|
238
|
+
* @param options - Barcode options
|
|
239
|
+
*/
|
|
240
|
+
barcode(content: string, options: BarcodeOptions): this {
|
|
241
|
+
const {
|
|
242
|
+
x,
|
|
243
|
+
y,
|
|
244
|
+
type,
|
|
245
|
+
height = 100,
|
|
246
|
+
narrow = 2,
|
|
247
|
+
wide = 4,
|
|
248
|
+
showText = true,
|
|
249
|
+
rotation = 0,
|
|
250
|
+
} = options;
|
|
251
|
+
|
|
252
|
+
const readable = showText ? 1 : 0;
|
|
253
|
+
|
|
254
|
+
if (type === 'QRCODE') {
|
|
255
|
+
// Use QRCODE command instead
|
|
256
|
+
return this.qrcode(content, { x, y, rotation });
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// BARCODE x, y, "type", height, readable, rotation, narrow, wide, "content"
|
|
260
|
+
this.commands.push(
|
|
261
|
+
`BARCODE ${x},${y},"${type}",${height},${readable},${rotation},${narrow},${wide},"${content}"`
|
|
262
|
+
);
|
|
263
|
+
return this;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Add QR code to label
|
|
268
|
+
* @param content - QR code content
|
|
269
|
+
* @param options - QR code options
|
|
270
|
+
*/
|
|
271
|
+
qrcode(content: string, options: QRCodeOptions): this {
|
|
272
|
+
const {
|
|
273
|
+
x,
|
|
274
|
+
y,
|
|
275
|
+
eccLevel = 'M',
|
|
276
|
+
cellWidth = 6,
|
|
277
|
+
mode = 'A',
|
|
278
|
+
rotation = 0,
|
|
279
|
+
} = options;
|
|
280
|
+
|
|
281
|
+
// QRCODE x, y, ECC level, cell width, mode, rotation, "content"
|
|
282
|
+
this.commands.push(
|
|
283
|
+
`QRCODE ${x},${y},${eccLevel},${cellWidth},${mode},${rotation},"${this.escapeString(content)}"`
|
|
284
|
+
);
|
|
285
|
+
return this;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Draw a box/rectangle
|
|
290
|
+
* @param options - Box options
|
|
291
|
+
*/
|
|
292
|
+
box(options: BoxOptions): this {
|
|
293
|
+
const { x, y, width, height, thickness = 2 } = options;
|
|
294
|
+
this.commands.push(`BOX ${x},${y},${x + width},${y + height},${thickness}`);
|
|
295
|
+
return this;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Draw a line
|
|
300
|
+
* @param options - Line options
|
|
301
|
+
*/
|
|
302
|
+
line(options: LineOptions): this {
|
|
303
|
+
const { x1, y1, x2, y2, thickness = 2 } = options;
|
|
304
|
+
|
|
305
|
+
if (x1 === x2 || y1 === y2) {
|
|
306
|
+
// Horizontal or vertical line - use BAR command
|
|
307
|
+
const width = Math.abs(x2 - x1) || thickness;
|
|
308
|
+
const height = Math.abs(y2 - y1) || thickness;
|
|
309
|
+
this.commands.push(`BAR ${Math.min(x1, x2)},${Math.min(y1, y2)},${width},${height}`);
|
|
310
|
+
} else {
|
|
311
|
+
// Diagonal line - use DIAGONAL command if supported
|
|
312
|
+
this.commands.push(`DIAGONAL ${x1},${y1},${thickness},${Math.sqrt((x2-x1)**2 + (y2-y1)**2)},${Math.atan2(y2-y1, x2-x1) * 180 / Math.PI}`);
|
|
313
|
+
}
|
|
314
|
+
return this;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Fill a rectangular area
|
|
319
|
+
* @param x - X position
|
|
320
|
+
* @param y - Y position
|
|
321
|
+
* @param width - Width
|
|
322
|
+
* @param height - Height
|
|
323
|
+
*/
|
|
324
|
+
bar(x: number, y: number, width: number, height: number): this {
|
|
325
|
+
this.commands.push(`BAR ${x},${y},${width},${height}`);
|
|
326
|
+
return this;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Reverse a rectangular area (white becomes black and vice versa)
|
|
331
|
+
* @param x - X position
|
|
332
|
+
* @param y - Y position
|
|
333
|
+
* @param width - Width
|
|
334
|
+
* @param height - Height
|
|
335
|
+
*/
|
|
336
|
+
reverse(x: number, y: number, width: number, height: number): this {
|
|
337
|
+
this.commands.push(`REVERSE ${x},${y},${width},${height}`);
|
|
338
|
+
return this;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Print the label
|
|
343
|
+
* @param copies - Number of copies (default: 1)
|
|
344
|
+
* @param sets - Number of sets (default: 1)
|
|
345
|
+
*/
|
|
346
|
+
print(copies = 1, sets = 1): this {
|
|
347
|
+
this.commands.push(`PRINT ${copies},${sets}`);
|
|
348
|
+
return this;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* Feed labels
|
|
353
|
+
* @param count - Number of labels to feed
|
|
354
|
+
*/
|
|
355
|
+
feed(count = 1): this {
|
|
356
|
+
this.commands.push(`FORMFEED`);
|
|
357
|
+
for (let i = 1; i < count; i++) {
|
|
358
|
+
this.commands.push(`FORMFEED`);
|
|
359
|
+
}
|
|
360
|
+
return this;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Cut paper (if cutter available)
|
|
365
|
+
*/
|
|
366
|
+
cut(): this {
|
|
367
|
+
this.commands.push('CUT');
|
|
368
|
+
return this;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* Beep the buzzer
|
|
373
|
+
*/
|
|
374
|
+
beep(): this {
|
|
375
|
+
this.commands.push('BEEP');
|
|
376
|
+
return this;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
/**
|
|
380
|
+
* Home the print head
|
|
381
|
+
*/
|
|
382
|
+
home(): this {
|
|
383
|
+
this.commands.push('HOME');
|
|
384
|
+
return this;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* Escape special characters in string
|
|
389
|
+
*/
|
|
390
|
+
private escapeString(str: string): string {
|
|
391
|
+
return str.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* Get all commands as string
|
|
396
|
+
*/
|
|
397
|
+
getCommands(): string {
|
|
398
|
+
return this.commands.join('\r\n') + '\r\n';
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* Get commands as buffer for sending to printer
|
|
403
|
+
*/
|
|
404
|
+
getBuffer(): Uint8Array {
|
|
405
|
+
const commandString = this.getCommands();
|
|
406
|
+
this.logger.debug(`TSPL commands:\n${commandString}`);
|
|
407
|
+
return Encoding.encode(commandString, 'ASCII');
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* Clear all commands
|
|
412
|
+
*/
|
|
413
|
+
reset(): this {
|
|
414
|
+
this.commands = [];
|
|
415
|
+
return this;
|
|
416
|
+
}
|
|
417
|
+
}
|