taro-bluetooth-print 2.2.1 → 2.3.1
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 +57 -165
- package/README.md +142 -285
- package/dist/index.cjs.js +1 -1
- package/dist/index.es.js +1 -81644
- package/dist/index.umd.js +1 -1
- package/dist/types/adapters/AdapterFactory.d.ts +0 -1
- package/dist/types/adapters/AlipayAdapter.d.ts +0 -1
- package/dist/types/adapters/BaiduAdapter.d.ts +0 -1
- package/dist/types/adapters/BaseAdapter.d.ts +0 -1
- package/dist/types/adapters/ByteDanceAdapter.d.ts +0 -1
- package/dist/types/adapters/TaroAdapter.d.ts +0 -1
- 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 +1 -2
- package/dist/types/core/EventEmitter.d.ts +6 -26
- package/dist/types/core/index.d.ts +6 -0
- package/dist/types/drivers/CpclDriver.d.ts +304 -0
- package/dist/types/drivers/EscPos.d.ts +0 -1
- package/dist/types/drivers/GPrinterDriver.d.ts +63 -0
- package/dist/types/drivers/TsplDriver.d.ts +251 -0
- package/dist/types/drivers/ZplDriver.d.ts +325 -0
- package/dist/types/drivers/index.d.ts +9 -0
- package/dist/types/encoding/gbk-lite.d.ts +8 -0
- package/dist/types/encoding/gbk-table.d.ts +8 -30
- package/dist/types/index.d.ts +10 -5
- 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 +0 -1
- package/dist/types/services/ConnectionManager.d.ts +1 -2
- package/dist/types/services/PrintJobManager.d.ts +0 -1
- package/dist/types/services/index.d.ts +8 -0
- package/dist/types/services/interfaces/index.d.ts +0 -1
- package/dist/types/template/TemplateEngine.d.ts +0 -1
- package/package.json +36 -20
- package/src/adapters/BaseAdapter.ts +6 -8
- package/src/core/BluetoothPrinter.ts +15 -15
- package/src/core/EventEmitter.ts +15 -15
- package/src/core/index.ts +7 -0
- package/src/drivers/CpclDriver.ts +549 -0
- package/src/drivers/GPrinterDriver.ts +115 -0
- package/src/drivers/TsplDriver.ts +405 -0
- package/src/drivers/ZplDriver.ts +543 -0
- package/src/drivers/index.ts +37 -0
- package/src/encoding/gbk-lite.ts +108 -0
- package/src/encoding/gbk-table.ts +80 -58
- package/src/index.ts +27 -23
- package/src/plugins/PluginManager.ts +195 -0
- package/src/plugins/builtin/LoggingPlugin.ts +99 -0
- package/src/plugins/builtin/RetryPlugin.ts +103 -0
- package/src/plugins/index.ts +10 -0
- package/src/plugins/types.ts +119 -0
- package/src/services/ConnectionManager.ts +22 -22
- package/src/services/index.ts +16 -0
|
@@ -0,0 +1,549 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CPCL Driver
|
|
3
|
+
* PCL commands for Compact Print Language - HP/Honeywell/Toddler printers
|
|
4
|
+
*
|
|
5
|
+
* CPCL is commonly used in mobile/portable thermal printers
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { Logger } from '@/utils/logger';
|
|
9
|
+
import { Encoding } from '@/utils/encoding';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* CPCL page size presets
|
|
13
|
+
*/
|
|
14
|
+
export type CPCLPageSize = 'A4' | 'A5' | 'LETTER' | '4X6' | '4X2' | '4X4' | '2.25X1.25' | 'CUSTOM';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* CPCL Text options
|
|
18
|
+
*/
|
|
19
|
+
export interface CpclTextOptions {
|
|
20
|
+
/** X position in dots */
|
|
21
|
+
x?: number;
|
|
22
|
+
/** Y position in dots */
|
|
23
|
+
y?: number;
|
|
24
|
+
/** Font type: 0-6 (0=OCR, 1=OCR-B, 2=CG Triumvirate, etc.) */
|
|
25
|
+
font?: number;
|
|
26
|
+
/** Horizontal multiplier (1-8) */
|
|
27
|
+
xMulti?: number;
|
|
28
|
+
/** Vertical multiplier (1-8) */
|
|
29
|
+
yMulti?: number;
|
|
30
|
+
/** Rotation: 0, 90, 180, 270 */
|
|
31
|
+
rotation?: 0 | 90 | 180 | 270;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* CPCL Barcode options
|
|
36
|
+
*/
|
|
37
|
+
export interface CpclBarcodeOptions {
|
|
38
|
+
/** X position in dots */
|
|
39
|
+
x?: number;
|
|
40
|
+
/** Y position in dots */
|
|
41
|
+
y?: number;
|
|
42
|
+
/** Barcode type */
|
|
43
|
+
type:
|
|
44
|
+
| '128'
|
|
45
|
+
| '39'
|
|
46
|
+
| 'EAN13'
|
|
47
|
+
| 'EAN8'
|
|
48
|
+
| 'UPCA'
|
|
49
|
+
| 'UPCE'
|
|
50
|
+
| 'MSI'
|
|
51
|
+
| 'PLESSEY'
|
|
52
|
+
| 'PDF417'
|
|
53
|
+
| 'DATAMATRIX'
|
|
54
|
+
| 'QR';
|
|
55
|
+
/** Barcode height in dots (default: 50) */
|
|
56
|
+
height?: number;
|
|
57
|
+
/** Wide bar width for 39 (default: 2) */
|
|
58
|
+
wide?: number;
|
|
59
|
+
/** Narrow bar width (default: 1) */
|
|
60
|
+
narrow?: number;
|
|
61
|
+
/** Show human-readable text (default: true) */
|
|
62
|
+
readable?: boolean;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* CPCL QR Code options
|
|
67
|
+
*/
|
|
68
|
+
export interface CpclQRCodeOptions {
|
|
69
|
+
/** X position in dots */
|
|
70
|
+
x?: number;
|
|
71
|
+
/** Y position in dots */
|
|
72
|
+
y?: number;
|
|
73
|
+
/** Model (default: 2) */
|
|
74
|
+
model?: 1 | 2;
|
|
75
|
+
/** Error correction level: L, M, Q, H */
|
|
76
|
+
errorCorrection?: 'L' | 'M' | 'Q' | 'H';
|
|
77
|
+
/** Cell size (default: 4) */
|
|
78
|
+
cellSize?: number;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* CPCL Line options
|
|
83
|
+
*/
|
|
84
|
+
export interface CpclLineOptions {
|
|
85
|
+
/** Start X position */
|
|
86
|
+
x1: number;
|
|
87
|
+
/** Start Y position */
|
|
88
|
+
y1: number;
|
|
89
|
+
/** End X position */
|
|
90
|
+
x2: number;
|
|
91
|
+
/** End Y position */
|
|
92
|
+
y2: number;
|
|
93
|
+
/** Line width/thickness */
|
|
94
|
+
width?: number;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* CPCL Box options
|
|
99
|
+
*/
|
|
100
|
+
export interface CpclBoxOptions {
|
|
101
|
+
/** X position */
|
|
102
|
+
x: number;
|
|
103
|
+
/** Y position */
|
|
104
|
+
y: number;
|
|
105
|
+
/** Width */
|
|
106
|
+
width: number;
|
|
107
|
+
/** Height */
|
|
108
|
+
height: number;
|
|
109
|
+
/** Border thickness */
|
|
110
|
+
thickness?: number;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* CPCL Driver for HP, Honeywell, and Toddler portable printers
|
|
115
|
+
*
|
|
116
|
+
* @example
|
|
117
|
+
* ```typescript
|
|
118
|
+
* const cpcl = new CpclDriver();
|
|
119
|
+
*
|
|
120
|
+
* const commands = cpcl
|
|
121
|
+
* .pageStart()
|
|
122
|
+
* .text('Hello World!', { x: 50, y: 50 })
|
|
123
|
+
* .barcode('1234567890', { x: 50, y: 150, type: '128' })
|
|
124
|
+
* .qrcode('https://example.com', { x: 300, y: 50 })
|
|
125
|
+
* .pageEnd()
|
|
126
|
+
* .getCommands();
|
|
127
|
+
* ```
|
|
128
|
+
*/
|
|
129
|
+
export class CpclDriver {
|
|
130
|
+
private commands: string[] = [];
|
|
131
|
+
private readonly logger = Logger.scope('CpclDriver');
|
|
132
|
+
|
|
133
|
+
// Page dimensions
|
|
134
|
+
private pageWidth = 576; // 4" at 144 DPI (common)
|
|
135
|
+
private pageHeight = 0; // 0 = continuous
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Initialize CPCL driver
|
|
139
|
+
* @param width - Page width in dots
|
|
140
|
+
* @param height - Page height in dots (0 = continuous)
|
|
141
|
+
*/
|
|
142
|
+
constructor(width = 576, height = 0) {
|
|
143
|
+
this.pageWidth = width;
|
|
144
|
+
this.pageHeight = height;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Set page size
|
|
149
|
+
* @param width - Width in dots
|
|
150
|
+
* @param height - Height in dots (0 = continuous)
|
|
151
|
+
*/
|
|
152
|
+
setPageSize(width: number, height = 0): this {
|
|
153
|
+
this.pageWidth = width;
|
|
154
|
+
this.pageHeight = height;
|
|
155
|
+
return this;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Use standard page size
|
|
160
|
+
* @param size - Preset size name
|
|
161
|
+
*/
|
|
162
|
+
usePageSize(size: CPCLPageSize): this {
|
|
163
|
+
const sizes: Record<CPCLPageSize, [number, number]> = {
|
|
164
|
+
'4X6': [576, 864],
|
|
165
|
+
'4X4': [576, 576],
|
|
166
|
+
'4X2': [576, 288],
|
|
167
|
+
'2.25X1.25': [324, 180],
|
|
168
|
+
A4: [992, 1406],
|
|
169
|
+
A5: [701, 992],
|
|
170
|
+
LETTER: [1050, 1500],
|
|
171
|
+
CUSTOM: [576, 0],
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
const [width, height] = sizes[size] || [576, 0];
|
|
175
|
+
this.pageWidth = width;
|
|
176
|
+
this.pageHeight = height;
|
|
177
|
+
return this;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Start a new page
|
|
182
|
+
*/
|
|
183
|
+
pageStart(): this {
|
|
184
|
+
this.commands.push(`! U1 SETLP 5 0 30`);
|
|
185
|
+
this.commands.push(`! U1 PAGE WIDTH ${this.pageWidth}`);
|
|
186
|
+
if (this.pageHeight > 0) {
|
|
187
|
+
this.commands.push(`! U1 PAGE HEIGHT ${this.pageHeight}`);
|
|
188
|
+
}
|
|
189
|
+
this.commands.push('START');
|
|
190
|
+
return this;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* End current page
|
|
195
|
+
*/
|
|
196
|
+
pageEnd(): this {
|
|
197
|
+
this.commands.push('END');
|
|
198
|
+
return this;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Form feed to next label
|
|
203
|
+
*/
|
|
204
|
+
formFeed(): this {
|
|
205
|
+
this.commands.push('FORM');
|
|
206
|
+
return this;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Set line print mode
|
|
211
|
+
* @param font - Font number (0-6)
|
|
212
|
+
* @param xMulti - Horizontal multiplier
|
|
213
|
+
* @param yMulti - Vertical multiplier
|
|
214
|
+
*/
|
|
215
|
+
setLinePrint(font = 5, xMulti = 0, yMulti = 30): this {
|
|
216
|
+
this.commands.push(`! U1 SETLP ${font} ${xMulti} ${yMulti}`);
|
|
217
|
+
return this;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Set text font
|
|
222
|
+
* @param font - Font: 0=OCR, 1=OCR-B, 2=CG Triumvirate Bold, 3-6=Various sizes
|
|
223
|
+
* @param xMulti - Horizontal multiplier (1-8)
|
|
224
|
+
* @param yMulti - Vertical multiplier (1-8)
|
|
225
|
+
* @param rotation - Rotation angle
|
|
226
|
+
*/
|
|
227
|
+
setFont(font = 3, xMulti = 1, yMulti = 1, rotation: 0 | 90 | 180 | 270 = 0): this {
|
|
228
|
+
const rotationMap: Record<number, string> = { 0: 'N', 90: 'R', 180: 'B', 270: 'I' };
|
|
229
|
+
const rot = rotationMap[rotation] || 'N';
|
|
230
|
+
this.commands.push(`! U1 SETLP ${font} ${xMulti} ${yMulti} ${rot}`);
|
|
231
|
+
return this;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Add text at current position
|
|
236
|
+
* @param content - Text content
|
|
237
|
+
*/
|
|
238
|
+
text(content: string): this {
|
|
239
|
+
this.commands.push(`! U1 TEXT ${this.escapeText(content)}`);
|
|
240
|
+
return this;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Add positioned text
|
|
245
|
+
* @param content - Text content
|
|
246
|
+
* @param options - Text options
|
|
247
|
+
*/
|
|
248
|
+
textAt(content: string, options: CpclTextOptions): this {
|
|
249
|
+
const { x = 0, y = 0, font = 3, xMulti = 1, yMulti = 1, rotation = 0 } = options;
|
|
250
|
+
const rotationMap: Record<number, string> = { 0: 'N', 90: 'R', 180: 'B', 270: 'I' };
|
|
251
|
+
const rot = rotationMap[rotation] || 'N';
|
|
252
|
+
|
|
253
|
+
this.commands.push(`! U1 SETLP ${font} ${xMulti} ${yMulti} ${rot}`);
|
|
254
|
+
this.commands.push(`! U1 ${x} ${y} TEXT ${this.escapeText(content)}`);
|
|
255
|
+
return this;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Add text using legacy command
|
|
260
|
+
* @param content - Text content
|
|
261
|
+
* @param x - X position
|
|
262
|
+
* @param y - Y position
|
|
263
|
+
* @param font - Font number
|
|
264
|
+
*/
|
|
265
|
+
legacyText(content: string, x: number, y: number, font = 3): this {
|
|
266
|
+
this.commands.push(`TEXT ${x} ${y} ${font} "${this.escapeText(content)}"`);
|
|
267
|
+
return this;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Add barcode at position
|
|
272
|
+
* @param content - Barcode content
|
|
273
|
+
* @param options - Barcode options
|
|
274
|
+
*/
|
|
275
|
+
barcode(content: string, options: CpclBarcodeOptions): this {
|
|
276
|
+
const {
|
|
277
|
+
x = 0,
|
|
278
|
+
y = 0,
|
|
279
|
+
type = '128',
|
|
280
|
+
height = 50,
|
|
281
|
+
wide = 2,
|
|
282
|
+
narrow = 1,
|
|
283
|
+
readable = true,
|
|
284
|
+
} = options;
|
|
285
|
+
|
|
286
|
+
const readableFlag = readable ? 'B' : 'N';
|
|
287
|
+
|
|
288
|
+
// CPCL barcode commands
|
|
289
|
+
switch (type) {
|
|
290
|
+
case '128':
|
|
291
|
+
this.commands.push(
|
|
292
|
+
`BARCODE 128 ${x} ${y} ${height} ${readableFlag} 0 ${narrow} ${wide} "${content}"`
|
|
293
|
+
);
|
|
294
|
+
break;
|
|
295
|
+
case '39':
|
|
296
|
+
this.commands.push(
|
|
297
|
+
`BARCODE 39 ${x} ${y} ${height} ${readableFlag} ${wide} ${narrow} "${content}"`
|
|
298
|
+
);
|
|
299
|
+
break;
|
|
300
|
+
case 'EAN13':
|
|
301
|
+
this.commands.push(`BARCODE EAN13 ${x} ${y} ${height} ${readableFlag} "${content}"`);
|
|
302
|
+
break;
|
|
303
|
+
case 'EAN8':
|
|
304
|
+
this.commands.push(`BARCODE EAN8 ${x} ${y} ${height} ${readableFlag} "${content}"`);
|
|
305
|
+
break;
|
|
306
|
+
case 'UPCA':
|
|
307
|
+
this.commands.push(`BARCODE UPCA ${x} ${y} ${height} ${readableFlag} "${content}"`);
|
|
308
|
+
break;
|
|
309
|
+
case 'UPCE':
|
|
310
|
+
this.commands.push(`BARCODE UPCE ${x} ${y} ${height} ${readableFlag} "${content}"`);
|
|
311
|
+
break;
|
|
312
|
+
case 'PDF417':
|
|
313
|
+
this.commands.push(`BARCODE PDF417 ${x} ${y} 6 200 "${content}"`);
|
|
314
|
+
break;
|
|
315
|
+
case 'DATAMATRIX':
|
|
316
|
+
this.commands.push(`BARCODE DATAMATRIX ${x} ${y} 200 "${content}"`);
|
|
317
|
+
break;
|
|
318
|
+
case 'QR':
|
|
319
|
+
// QR uses separate command
|
|
320
|
+
this.qrcode(content, { x, y });
|
|
321
|
+
break;
|
|
322
|
+
default:
|
|
323
|
+
this.commands.push(
|
|
324
|
+
`BARCODE 128 ${x} ${y} ${height} ${readableFlag} 0 ${narrow} ${wide} "${content}"`
|
|
325
|
+
);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
return this;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* Add Code 128 barcode (most common)
|
|
333
|
+
* @param content - Barcode content
|
|
334
|
+
* @param x - X position
|
|
335
|
+
* @param y - Y position
|
|
336
|
+
* @param height - Barcode height
|
|
337
|
+
*/
|
|
338
|
+
code128(content: string, x = 0, y = 0, height = 50): this {
|
|
339
|
+
return this.barcode(content, { x, y, type: '128', height });
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Add Code 39 barcode
|
|
344
|
+
* @param content - Barcode content
|
|
345
|
+
* @param x - X position
|
|
346
|
+
* @param y - Y position
|
|
347
|
+
* @param height - Barcode height
|
|
348
|
+
*/
|
|
349
|
+
code39(content: string, x = 0, y = 0, height = 50): this {
|
|
350
|
+
return this.barcode(content, { x, y, type: '39', height, wide: 2, narrow: 1 });
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* Add QR code
|
|
355
|
+
* @param content - QR code content
|
|
356
|
+
* @param options - QR options
|
|
357
|
+
*/
|
|
358
|
+
qrcode(content: string, options?: CpclQRCodeOptions): this {
|
|
359
|
+
const { x = 0, y = 0, model = 2, errorCorrection = 'M', cellSize = 4 } = options || {};
|
|
360
|
+
this.commands.push(
|
|
361
|
+
`BARCODE QR ${x} ${y} ${model} ${cellSize} A ${errorCorrection} "${content}"`
|
|
362
|
+
);
|
|
363
|
+
return this;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* Add 2D barcode (PDF417 or DataMatrix)
|
|
368
|
+
* @param content - Content
|
|
369
|
+
* @param type - Type: PDF417 or DATAMATRIX
|
|
370
|
+
* @param x - X position
|
|
371
|
+
* @param y - Y position
|
|
372
|
+
* @param height - Height
|
|
373
|
+
*/
|
|
374
|
+
twoDBarcode(content: string, type: 'PDF417' | 'DATAMATRIX', x = 0, y = 0, height = 200): this {
|
|
375
|
+
if (type === 'PDF417') {
|
|
376
|
+
this.commands.push(`BARCODE PDF417 ${x} ${y} 6 ${height} "${content}"`);
|
|
377
|
+
} else {
|
|
378
|
+
this.commands.push(`BARCODE DATAMATRIX ${x} ${y} ${height} "${content}"`);
|
|
379
|
+
}
|
|
380
|
+
return this;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/**
|
|
384
|
+
* Add line
|
|
385
|
+
* @param options - Line options
|
|
386
|
+
*/
|
|
387
|
+
line(options: CpclLineOptions): this {
|
|
388
|
+
const { x1, y1, x2, y2, width = 1 } = options;
|
|
389
|
+
this.commands.push(`LINE ${x1} ${y1} ${x2} ${y2} ${width}`);
|
|
390
|
+
return this;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* Add box/rectangle
|
|
395
|
+
* @param options - Box options
|
|
396
|
+
*/
|
|
397
|
+
box(options: CpclBoxOptions): this {
|
|
398
|
+
const { x, y, width, height, thickness = 1 } = options;
|
|
399
|
+
this.commands.push(`BOX ${x} ${y} ${width} ${height} ${thickness}`);
|
|
400
|
+
return this;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* Add inverse area (white on black)
|
|
405
|
+
* @param x - X position
|
|
406
|
+
* @param y - Y position
|
|
407
|
+
* @param width - Width
|
|
408
|
+
* @param height - Height
|
|
409
|
+
*/
|
|
410
|
+
inverse(x: number, y: number, width: number, height: number): this {
|
|
411
|
+
this.commands.push(`INVERSE ${width} ${height} ${x} ${y}`);
|
|
412
|
+
return this;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* Set print density
|
|
417
|
+
* @param density - Density value (-50 to +50)
|
|
418
|
+
*/
|
|
419
|
+
setDensity(density: number): this {
|
|
420
|
+
const safeDensity = Math.min(50, Math.max(-50, density));
|
|
421
|
+
this.commands.push(`! U1 SETDENSITY ${safeDensity}`);
|
|
422
|
+
return this;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
/**
|
|
426
|
+
* Set speed
|
|
427
|
+
* @param speed - Speed: 2-6 (2=50%, 3=75%, 4=100%, 5=125%, 6=150%)
|
|
428
|
+
*/
|
|
429
|
+
setSpeed(speed: number): this {
|
|
430
|
+
const safeSpeed = Math.min(6, Math.max(2, speed));
|
|
431
|
+
this.commands.push(`! U1 SETSPEED ${safeSpeed}`);
|
|
432
|
+
return this;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
/**
|
|
436
|
+
* Cut paper (if cutter installed)
|
|
437
|
+
*/
|
|
438
|
+
cut(): this {
|
|
439
|
+
this.commands.push('CUT');
|
|
440
|
+
return this;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
/**
|
|
444
|
+
* Partial cut
|
|
445
|
+
*/
|
|
446
|
+
partialCut(): this {
|
|
447
|
+
this.commands.push('PCUT');
|
|
448
|
+
return this;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
/**
|
|
452
|
+
* Feed and cut
|
|
453
|
+
*/
|
|
454
|
+
feedCut(): this {
|
|
455
|
+
this.commands.push('FEED CUT');
|
|
456
|
+
return this;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
/**
|
|
460
|
+
* Sound beep
|
|
461
|
+
* @param count - Number of beeps
|
|
462
|
+
* @param duration - Duration in 10ms units
|
|
463
|
+
*/
|
|
464
|
+
beep(count = 1, duration = 5): this {
|
|
465
|
+
this.commands.push(`BEEP ${count} ${duration}`);
|
|
466
|
+
return this;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
/**
|
|
470
|
+
* Add logo/image
|
|
471
|
+
* @param x - X position
|
|
472
|
+
* @param y - Y position
|
|
473
|
+
* @param logoName - Stored logo name
|
|
474
|
+
*/
|
|
475
|
+
logo(x: number, y: number, logoName: string): this {
|
|
476
|
+
this.commands.push(`LOGO ${x} ${y} "${logoName}"`);
|
|
477
|
+
return this;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
/**
|
|
481
|
+
* Download logo to printer (store in memory)
|
|
482
|
+
* Note: This is a placeholder for future implementation
|
|
483
|
+
* @param logoName - Name to store logo as
|
|
484
|
+
* @param _bitmap - Logo bitmap data (placeholder)
|
|
485
|
+
*/
|
|
486
|
+
downloadLogo(logoName: string, _bitmap: Uint8Array): this {
|
|
487
|
+
this.commands.push(`! DF ${logoName}`);
|
|
488
|
+
// TODO: Encode bitmap to CPCL format
|
|
489
|
+
this.logger.debug('CPCL logo download not fully implemented');
|
|
490
|
+
this.commands.push(`! DF`);
|
|
491
|
+
return this;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
/**
|
|
495
|
+
* Print stored logo
|
|
496
|
+
* @param logoName - Logo name
|
|
497
|
+
* @param x - X position
|
|
498
|
+
* @param y - Y position
|
|
499
|
+
*/
|
|
500
|
+
printLogo(logoName: string, x = 0, y = 0): this {
|
|
501
|
+
this.commands.push(`LOGO ${x} ${y} "${logoName}"`);
|
|
502
|
+
return this;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
/**
|
|
506
|
+
* Get all commands as string
|
|
507
|
+
*/
|
|
508
|
+
getCommands(): string {
|
|
509
|
+
return this.commands.join('\r\n') + '\r\n';
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
/**
|
|
513
|
+
* Get commands as buffer for sending to printer
|
|
514
|
+
*/
|
|
515
|
+
getBuffer(): Uint8Array {
|
|
516
|
+
const commandString = this.getCommands();
|
|
517
|
+
this.logger.debug(`CPCL commands:\n${commandString}`);
|
|
518
|
+
return Encoding.encode(commandString, 'ASCII');
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
/**
|
|
522
|
+
* Get raw command list
|
|
523
|
+
*/
|
|
524
|
+
getCommandList(): string[] {
|
|
525
|
+
return [...this.commands];
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
/**
|
|
529
|
+
* Clear all commands
|
|
530
|
+
*/
|
|
531
|
+
reset(): this {
|
|
532
|
+
this.commands = [];
|
|
533
|
+
return this;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
/**
|
|
537
|
+
* Get page width
|
|
538
|
+
*/
|
|
539
|
+
getPageWidth(): number {
|
|
540
|
+
return this.pageWidth;
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
/**
|
|
544
|
+
* Escape special characters in text
|
|
545
|
+
*/
|
|
546
|
+
private escapeText(str: string): string {
|
|
547
|
+
return str.replace(/"/g, '""');
|
|
548
|
+
}
|
|
549
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GPrinter (佳博) Driver
|
|
3
|
+
*
|
|
4
|
+
* 佳博 (GPrinter) 热敏打印机兼容 ESC/POS 指令集
|
|
5
|
+
* 大部分命令与标准 ESC/POS 兼容,部分特殊命令需要额外处理
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { EscPos, type EscPosOptions } from './EscPos';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* GPrinter Driver for GiaoBao (佳博) thermal printers
|
|
12
|
+
*
|
|
13
|
+
* 佳博打印机基于 ESC/POS 指令集,此驱动扩展了标准 EscPos
|
|
14
|
+
* 添加了佳博特定的命令支持
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```typescript
|
|
18
|
+
* import { GPrinterDriver } from 'taro-bluetooth-print';
|
|
19
|
+
*
|
|
20
|
+
* const driver = new GPrinterDriver();
|
|
21
|
+
* let commands = driver.init();
|
|
22
|
+
* commands = driver.text('Hello 佳博!', 'GBK');
|
|
23
|
+
* commands = driver.cut();
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* GPrinter options (same as EscPosOptions)
|
|
29
|
+
*/
|
|
30
|
+
export type GPrinterOptions = EscPosOptions;
|
|
31
|
+
|
|
32
|
+
export class GPrinterDriver extends EscPos {
|
|
33
|
+
/**
|
|
34
|
+
* 佳博打印机特定: 打开钱箱
|
|
35
|
+
* @param pin 钱箱引脚 (0 或 1, 默认 0)
|
|
36
|
+
*/
|
|
37
|
+
openCashDrawer(pin = 0): Uint8Array[] {
|
|
38
|
+
// ESC p m t1 t2
|
|
39
|
+
// m = 0 or 1 (pin)
|
|
40
|
+
// t1 = 50 (on time)
|
|
41
|
+
// t2 = 200 (off time)
|
|
42
|
+
return [new Uint8Array([0x1b, 0x70, pin, 50, 200])];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* 佳博打印机特定: 发送声响
|
|
47
|
+
* @param times 次数 (1-9)
|
|
48
|
+
* @param duration 持续时间
|
|
49
|
+
*/
|
|
50
|
+
beep(times = 3, duration = 50): Uint8Array[] {
|
|
51
|
+
// ESC B n t
|
|
52
|
+
return [new Uint8Array([0x1b, 0x42, times, duration])];
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* 佳博打印机特定: 打印自检页
|
|
57
|
+
*/
|
|
58
|
+
selfTest(): Uint8Array[] {
|
|
59
|
+
// ESC i
|
|
60
|
+
return [new Uint8Array([0x1b, 0x69])];
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* 佳博打印机特定: 获取状态
|
|
65
|
+
* 返回打印机状态字节
|
|
66
|
+
*/
|
|
67
|
+
getStatus(): Uint8Array[] {
|
|
68
|
+
// DLE EOT n (n = 1-4 获取不同状态)
|
|
69
|
+
// 状态 1: 打印机状态
|
|
70
|
+
// 状态 2: 脱机状态
|
|
71
|
+
// 状态 3: 错误状态
|
|
72
|
+
// 状态 4: 纸张状态
|
|
73
|
+
const buffers: Uint8Array[] = [];
|
|
74
|
+
for (let i = 1; i <= 4; i++) {
|
|
75
|
+
buffers.push(new Uint8Array([0x10, 0x04, i]));
|
|
76
|
+
}
|
|
77
|
+
return buffers;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* 佳博打印机特定: 设置字符代码页
|
|
82
|
+
* @param codePage 代码页编号
|
|
83
|
+
*/
|
|
84
|
+
setCodePage(codePage: number): Uint8Array[] {
|
|
85
|
+
// ESC t n
|
|
86
|
+
return [new Uint8Array([0x1b, 0x74, codePage])];
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* 佳博打印机特定: 打印并走纸
|
|
91
|
+
* @param lines 走纸行数
|
|
92
|
+
*/
|
|
93
|
+
printAndFeed(lines: number): Uint8Array[] {
|
|
94
|
+
// ESC d n
|
|
95
|
+
return [new Uint8Array([0x1b, 0x64, lines])];
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* 佳博打印机特定: 设置左边界
|
|
100
|
+
* @param n 左边界字符数
|
|
101
|
+
*/
|
|
102
|
+
setLeftMargin(n: number): Uint8Array[] {
|
|
103
|
+
// ESC l n
|
|
104
|
+
return [new Uint8Array([0x1b, 0x6c, n])];
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* 佳博打印机特定: 设置打印区域宽度
|
|
109
|
+
* @param n 宽度 (字符数)
|
|
110
|
+
*/
|
|
111
|
+
setPrintWidth(n: number): Uint8Array[] {
|
|
112
|
+
// ESC W n
|
|
113
|
+
return [new Uint8Array([0x1b, 0x57, n])];
|
|
114
|
+
}
|
|
115
|
+
}
|