taro-bluetooth-print 2.4.0 → 2.5.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 +41 -0
- package/README.md +10 -2
- package/dist/index.cjs.js +1 -1
- package/dist/index.es.js +1 -1
- package/dist/index.umd.js +1 -1
- package/dist/types/adapters/QQAdapter.d.ts +22 -0
- package/dist/types/adapters/ReactNativeAdapter.d.ts +111 -0
- package/dist/types/adapters/index.d.ts +13 -0
- package/dist/types/device/MultiPrinterManager.d.ts +1 -1
- package/dist/types/drivers/StarPrinter.d.ts +243 -0
- package/dist/types/drivers/index.d.ts +1 -0
- package/dist/types/encoding/EncodingService.d.ts +41 -2
- package/dist/types/encoding/index.d.ts +2 -1
- package/dist/types/encoding/korean-japanese.d.ts +127 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/types/services/BatchPrintManager.d.ts +98 -5
- package/dist/types/services/PrintStatistics.d.ts +189 -0
- package/dist/types/services/ScheduledRetryManager.d.ts +213 -0
- package/dist/types/services/index.d.ts +5 -3
- package/dist/types/utils/image.d.ts +40 -119
- package/dist/types/utils/platform.d.ts +2 -0
- package/package.json +1 -1
- package/src/adapters/AdapterFactory.ts +5 -0
- package/src/adapters/QQAdapter.ts +36 -0
- package/src/adapters/ReactNativeAdapter.ts +517 -0
- package/src/adapters/index.ts +14 -0
- package/src/config/PrinterConfigManager.ts +10 -6
- package/src/device/MultiPrinterManager.ts +10 -10
- package/src/drivers/StarPrinter.ts +555 -0
- package/src/drivers/index.ts +10 -0
- package/src/encoding/EncodingService.ts +261 -4
- package/src/encoding/index.ts +17 -1
- package/src/encoding/korean-japanese.ts +289 -0
- package/src/index.ts +1 -5
- package/src/services/BatchPrintManager.ts +312 -42
- package/src/services/PrintHistory.ts +13 -11
- package/src/services/PrintJobManager.ts +3 -3
- package/src/services/PrintStatistics.ts +504 -0
- package/src/services/PrinterStatus.ts +4 -10
- package/src/services/ScheduledRetryManager.ts +564 -0
- package/src/services/index.ts +38 -3
- package/src/utils/image.ts +476 -342
- package/src/utils/platform.ts +20 -34
|
@@ -0,0 +1,555 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* STAR Printer Driver Implementation
|
|
3
|
+
* Converts high-level print commands to STAR SP series command byte sequences
|
|
4
|
+
*
|
|
5
|
+
* Reference: STAR SP700 / SP500 Command Reference
|
|
6
|
+
* Supports STAR TSP100, TSP700, TSP800 series thermal receipt printers
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { IPrinterDriver, IQrOptions } from '@/types';
|
|
10
|
+
import { EncodingService } from '@/encoding';
|
|
11
|
+
import { ImageProcessing } from '@/utils/image';
|
|
12
|
+
import { Logger } from '@/utils/logger';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Alignment options for text positioning
|
|
16
|
+
*/
|
|
17
|
+
export type Alignment = 'left' | 'center' | 'right';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Barcode types supported by STAR printers
|
|
21
|
+
*/
|
|
22
|
+
export type BarcodeType = 'CODE39' | 'CODE128' | 'EAN13';
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* STAR printer driver options
|
|
26
|
+
*/
|
|
27
|
+
export interface StarPrinterOptions {
|
|
28
|
+
/** Use the EncodingService for GBK/UTF-8 encoding (default: true) */
|
|
29
|
+
useEncodingService?: boolean;
|
|
30
|
+
/** Show warnings for unsupported characters (default: true) */
|
|
31
|
+
showEncodingWarnings?: boolean;
|
|
32
|
+
/** Fallback character for unsupported characters (default: '?') */
|
|
33
|
+
fallbackChar?: string;
|
|
34
|
+
/** Enable international character mode (default: true) */
|
|
35
|
+
internationalCharset?: boolean;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Barcode options for STAR printers
|
|
40
|
+
*/
|
|
41
|
+
export interface StarBarcodeOptions {
|
|
42
|
+
/** Barcode height in dots (default: 40) */
|
|
43
|
+
height?: number;
|
|
44
|
+
/** Barcode width (default: 2) */
|
|
45
|
+
width?: number;
|
|
46
|
+
/** HRI text position: 'none' | 'above' | 'below' | 'both' (default: 'below') */
|
|
47
|
+
hri?: 'none' | 'above' | 'below' | 'both';
|
|
48
|
+
/** Barcode type: CODE39, CODE128, EAN13 */
|
|
49
|
+
type?: BarcodeType;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* QR code options for STAR printers
|
|
54
|
+
*/
|
|
55
|
+
export interface StarQrOptions {
|
|
56
|
+
/** QR code model (default: 2) */
|
|
57
|
+
model?: 1 | 2;
|
|
58
|
+
/** QR code cell size in dots (default: 4) */
|
|
59
|
+
cellSize?: number;
|
|
60
|
+
/** Error correction level: 'L' | 'M' | 'Q' | 'H' (default: 'M') */
|
|
61
|
+
errorCorrection?: 'L' | 'M' | 'Q' | 'H';
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Image print options
|
|
66
|
+
*/
|
|
67
|
+
export interface StarImageOptions {
|
|
68
|
+
/** Enable dithering (default: true) */
|
|
69
|
+
dithering?: boolean;
|
|
70
|
+
/** Threshold for binarization 0-255 (default: 128) */
|
|
71
|
+
threshold?: number;
|
|
72
|
+
/** Print in greyscale mode (default: false) */
|
|
73
|
+
greyscale?: boolean;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* STAR TSP/TSP700 series thermal printer driver
|
|
78
|
+
*
|
|
79
|
+
* Implements the STAR command set for thermal receipt printers.
|
|
80
|
+
* Supports text, images, QR codes, barcodes, and paper control.
|
|
81
|
+
*
|
|
82
|
+
* @example
|
|
83
|
+
* ```typescript
|
|
84
|
+
* const driver = new StarPrinter();
|
|
85
|
+
* const commands = [
|
|
86
|
+
* ...driver.init(),
|
|
87
|
+
* ...driver.text('Hello World!'),
|
|
88
|
+
* ...driver.feed(3),
|
|
89
|
+
* ...driver.cut()
|
|
90
|
+
* ];
|
|
91
|
+
* ```
|
|
92
|
+
*/
|
|
93
|
+
export class StarPrinter implements IPrinterDriver {
|
|
94
|
+
private readonly logger = Logger.scope('StarPrinter');
|
|
95
|
+
private readonly encodingService: EncodingService;
|
|
96
|
+
private readonly useEncodingService: boolean;
|
|
97
|
+
private readonly internationalCharset: boolean;
|
|
98
|
+
|
|
99
|
+
// Internal state
|
|
100
|
+
private _boldEnabled = false;
|
|
101
|
+
private _alignment: Alignment = 'left';
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Creates a new StarPrinter driver instance
|
|
105
|
+
* @param options - Driver options
|
|
106
|
+
*/
|
|
107
|
+
constructor(options?: StarPrinterOptions) {
|
|
108
|
+
this.useEncodingService = options?.useEncodingService ?? true;
|
|
109
|
+
this.internationalCharset = options?.internationalCharset ?? true;
|
|
110
|
+
this.encodingService = new EncodingService({
|
|
111
|
+
showWarnings: options?.showEncodingWarnings ?? true,
|
|
112
|
+
fallbackChar: options?.fallbackChar ?? '?',
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
this.logger.info('StarPrinter driver initialized');
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Initialize the printer to default state
|
|
120
|
+
* Sends ESC @ to reset printer to power-on defaults
|
|
121
|
+
*
|
|
122
|
+
* @returns Array of command buffers
|
|
123
|
+
*
|
|
124
|
+
* @example
|
|
125
|
+
* ```typescript
|
|
126
|
+
* const commands = driver.init();
|
|
127
|
+
* ```
|
|
128
|
+
*/
|
|
129
|
+
init(): Uint8Array[] {
|
|
130
|
+
const commands: Uint8Array[] = [];
|
|
131
|
+
|
|
132
|
+
// ESC @ - Initialize printer
|
|
133
|
+
commands.push(new Uint8Array([0x1b, 0x40]));
|
|
134
|
+
|
|
135
|
+
// Set international charset to PC437 (default)
|
|
136
|
+
if (this.internationalCharset) {
|
|
137
|
+
// ESC R n - Select international character set
|
|
138
|
+
// n=0: USA, 1: France, 2: Germany, 3: UK, 4: Denmark, 5: Sweden,
|
|
139
|
+
// 6: Italy, 7: Spain, 8: Japan, 9: Norway, 10: Denmark II, 11: Spain II,
|
|
140
|
+
// 12: Latin America, 13: Korea, 16: Legal
|
|
141
|
+
commands.push(new Uint8Array([0x1b, 0x52, 0x00]));
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Reset internal state
|
|
145
|
+
this._boldEnabled = false;
|
|
146
|
+
this._alignment = 'left';
|
|
147
|
+
|
|
148
|
+
return commands;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Print text content with specified encoding
|
|
153
|
+
*
|
|
154
|
+
* Supports GBK, UTF-8, EUC-KR, Shift-JIS, ISO-2022-JP through EncodingService.
|
|
155
|
+
* When using ESC/POS compatible mode, text is output as-is with line feed.
|
|
156
|
+
*
|
|
157
|
+
* @param content - Text content to print
|
|
158
|
+
* @param encoding - Text encoding (default: 'GBK')
|
|
159
|
+
* @returns Array of command buffers
|
|
160
|
+
*
|
|
161
|
+
* @example
|
|
162
|
+
* ```typescript
|
|
163
|
+
* driver.text('Hello World', 'UTF-8');
|
|
164
|
+
* driver.text('你好世界', 'GBK');
|
|
165
|
+
* ```
|
|
166
|
+
*/
|
|
167
|
+
text(content: string, encoding = 'GBK'): Uint8Array[] {
|
|
168
|
+
if (!content || typeof content !== 'string') {
|
|
169
|
+
return [];
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const commands: Uint8Array[] = [];
|
|
173
|
+
|
|
174
|
+
// Apply alignment (if changed from default)
|
|
175
|
+
if (this._alignment !== 'left') {
|
|
176
|
+
const alignCmd = this.align(this._alignment);
|
|
177
|
+
if (alignCmd.length > 0 && alignCmd[0]) {
|
|
178
|
+
commands.push(alignCmd[0]);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Apply bold state (if enabled)
|
|
183
|
+
if (this._boldEnabled) {
|
|
184
|
+
commands.push(new Uint8Array([0x1b, 0x45, 0x01])); // ESC E 1
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Encode text content
|
|
188
|
+
if (this.useEncodingService && this.encodingService.isSupported(encoding)) {
|
|
189
|
+
const encoded = this.encodingService.encode(content, encoding);
|
|
190
|
+
commands.push(encoded);
|
|
191
|
+
} else {
|
|
192
|
+
// Fallback: use TextEncoder for UTF-8 or raw bytes
|
|
193
|
+
const encoder = new TextEncoder();
|
|
194
|
+
const encoded = encoder.encode(content);
|
|
195
|
+
commands.push(encoded);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Line feed
|
|
199
|
+
commands.push(new Uint8Array([0x0a]));
|
|
200
|
+
|
|
201
|
+
// Reset bold after line (so it doesn't persist unexpectedly)
|
|
202
|
+
if (this._boldEnabled) {
|
|
203
|
+
commands.push(new Uint8Array([0x1b, 0x45, 0x00])); // ESC E 0
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Reset alignment after line (so it doesn't persist)
|
|
207
|
+
if (this._alignment !== 'left') {
|
|
208
|
+
commands.push(new Uint8Array([0x1b, 0x61, 0x00])); // ESC a 0 (left)
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return commands;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Feed paper by specified number of lines
|
|
216
|
+
*
|
|
217
|
+
* @param lines - Number of lines to feed (default: 1, max: 255)
|
|
218
|
+
* @returns Array of command buffers
|
|
219
|
+
*
|
|
220
|
+
* @example
|
|
221
|
+
* ```typescript
|
|
222
|
+
* driver.feed(3); // Feed 3 lines
|
|
223
|
+
* ```
|
|
224
|
+
*/
|
|
225
|
+
feed(lines = 1): Uint8Array[] {
|
|
226
|
+
const safeLines = Math.max(1, Math.min(255, Math.floor(lines)));
|
|
227
|
+
|
|
228
|
+
// ESC d n - Print and feed n lines
|
|
229
|
+
return [new Uint8Array([0x1b, 0x64, safeLines])];
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Cut the paper using the built-in cutter
|
|
234
|
+
*
|
|
235
|
+
* Note: Not all STAR printers have a cutter. For printers without cutter,
|
|
236
|
+
* this will attempt a full paper cut which may result in no action.
|
|
237
|
+
*
|
|
238
|
+
* @returns Array of command buffers
|
|
239
|
+
*
|
|
240
|
+
* @example
|
|
241
|
+
* ```typescript
|
|
242
|
+
* driver.cut();
|
|
243
|
+
* ```
|
|
244
|
+
*/
|
|
245
|
+
cut(): Uint8Array[] {
|
|
246
|
+
const commands: Uint8Array[] = [];
|
|
247
|
+
|
|
248
|
+
// Feed to cut position first
|
|
249
|
+
commands.push(new Uint8Array([0x1b, 0x64, 0x03])); // ESC d 3
|
|
250
|
+
|
|
251
|
+
// GS V m - Select cut mode and cut
|
|
252
|
+
// m=0: Full cut, 1: Partial cut, 2: Full cut (TSP100 compatible)
|
|
253
|
+
commands.push(new Uint8Array([0x1d, 0x56, 0x00])); // GS V 0
|
|
254
|
+
|
|
255
|
+
return commands;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Print a QR code
|
|
260
|
+
*
|
|
261
|
+
* Uses STAR's built-in QR code command when available (GS W 01 pattern),
|
|
262
|
+
* otherwise falls back to printing QR code as a raster image.
|
|
263
|
+
*
|
|
264
|
+
* @param content - Content to encode in the QR code
|
|
265
|
+
* @param options - QR code options (model, cellSize, errorCorrection)
|
|
266
|
+
* @returns Array of command buffers
|
|
267
|
+
*
|
|
268
|
+
* @example
|
|
269
|
+
* ```typescript
|
|
270
|
+
* driver.qr('https://example.com', { model: 2, cellSize: 6, errorCorrection: 'M' });
|
|
271
|
+
* ```
|
|
272
|
+
*/
|
|
273
|
+
qr(content: string, options?: IQrOptions | StarQrOptions): Uint8Array[] {
|
|
274
|
+
if (!content || typeof content !== 'string') {
|
|
275
|
+
return [];
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
const model = (options?.model ?? 2) as 1 | 2;
|
|
279
|
+
// STAR uses cellSize instead of 'size' - extract safely
|
|
280
|
+
let cellSize = 4;
|
|
281
|
+
if (options) {
|
|
282
|
+
if ('cellSize' in options) {
|
|
283
|
+
cellSize = (options as StarQrOptions).cellSize ?? 4;
|
|
284
|
+
} else if ('size' in options) {
|
|
285
|
+
cellSize = (options as IQrOptions).size ?? 4;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
const ecLevel = options?.errorCorrection ?? 'M';
|
|
289
|
+
|
|
290
|
+
const commands: Uint8Array[] = [];
|
|
291
|
+
|
|
292
|
+
// STAR QR command: ESC W n1 n2 n3 n4 [data]
|
|
293
|
+
// n1: Model (1=Model1, 2=Model2)
|
|
294
|
+
// n2: Security level (L=0, M=1, Q=2, H=3)
|
|
295
|
+
// n3: Cell size (1-8 dots)
|
|
296
|
+
// n4: 0x00 (reserved)
|
|
297
|
+
|
|
298
|
+
// Step 1: QR Model (ESC GS W 01 30 n)
|
|
299
|
+
// Use ESC GS xR for model selection
|
|
300
|
+
// Pattern: ESC GS W n1 n2 n3 n4
|
|
301
|
+
commands.push(new Uint8Array([0x1b, 0x1d, 0x57, 0x01, model === 1 ? 0x30 : 0x31]));
|
|
302
|
+
|
|
303
|
+
// Step 2: QR Cell size (ESC GS W 02 n)
|
|
304
|
+
commands.push(new Uint8Array([0x1b, 0x1d, 0x57, 0x02, Math.min(8, Math.max(1, cellSize))]));
|
|
305
|
+
|
|
306
|
+
// Step 3: QR Error correction (ESC GS W 03 n)
|
|
307
|
+
const ecMap: Record<string, number> = { L: 0, M: 1, Q: 2, H: 3 };
|
|
308
|
+
const ecValue = ecMap[ecLevel] ?? 1;
|
|
309
|
+
commands.push(new Uint8Array([0x1b, 0x1d, 0x57, 0x03, ecValue]));
|
|
310
|
+
|
|
311
|
+
// Step 4: QR Data - Calculate length and send
|
|
312
|
+
// Get encoded data
|
|
313
|
+
const encoded = this.useEncodingService && this.encodingService.isSupported('UTF-8')
|
|
314
|
+
? this.encodingService.encode(content, 'UTF-8')
|
|
315
|
+
: new TextEncoder().encode(content);
|
|
316
|
+
|
|
317
|
+
// Send QR data: Length prefix (2 bytes big-endian) + data
|
|
318
|
+
const len = encoded.length;
|
|
319
|
+
const lenL = len % 256;
|
|
320
|
+
const lenH = Math.floor(len / 256);
|
|
321
|
+
|
|
322
|
+
// ESC GS W 00 pL pH [data]
|
|
323
|
+
commands.push(new Uint8Array([0x1b, 0x1d, 0x57, 0x00, lenL, lenH]));
|
|
324
|
+
commands.push(encoded);
|
|
325
|
+
|
|
326
|
+
// Step 5: Print QR (ESC GS W 04)
|
|
327
|
+
commands.push(new Uint8Array([0x1b, 0x1d, 0x57, 0x04]));
|
|
328
|
+
|
|
329
|
+
return commands;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Print a barcode
|
|
334
|
+
*
|
|
335
|
+
* Supports CODE39, CODE128, and EAN13 barcode types.
|
|
336
|
+
* HRI (Human Readable Interpretation) text can be positioned above,
|
|
337
|
+
* below, or both sides of the barcode.
|
|
338
|
+
*
|
|
339
|
+
* @param data - Barcode data content
|
|
340
|
+
* @param options - Barcode options (type, height, width, hri)
|
|
341
|
+
* @returns Array of command buffers
|
|
342
|
+
*
|
|
343
|
+
* @example
|
|
344
|
+
* ```typescript
|
|
345
|
+
* driver.barcode('123456789012', { type: 'EAN13', height: 60 });
|
|
346
|
+
* driver.barcode('ABC-1234', { type: 'CODE39', width: 3 });
|
|
347
|
+
* ```
|
|
348
|
+
*/
|
|
349
|
+
barcode(data: string, options?: StarBarcodeOptions): Uint8Array[] {
|
|
350
|
+
if (!data || typeof data !== 'string') {
|
|
351
|
+
return [];
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
const type = options?.type ?? 'CODE128';
|
|
355
|
+
const height = Math.max(1, Math.min(255, options?.height ?? 40));
|
|
356
|
+
const width = Math.max(1, Math.min(5, options?.width ?? 2));
|
|
357
|
+
const hri = options?.hri ?? 'below';
|
|
358
|
+
|
|
359
|
+
const commands: Uint8Array[] = [];
|
|
360
|
+
|
|
361
|
+
// HRI position mapping
|
|
362
|
+
const hriMap: Record<string, number> = {
|
|
363
|
+
'none': 0,
|
|
364
|
+
'above': 1,
|
|
365
|
+
'below': 2,
|
|
366
|
+
'both': 3,
|
|
367
|
+
};
|
|
368
|
+
const hriValue = hriMap[hri] ?? 2;
|
|
369
|
+
|
|
370
|
+
// GS h n - Set barcode height
|
|
371
|
+
commands.push(new Uint8Array([0x1d, 0x68, height]));
|
|
372
|
+
|
|
373
|
+
// GS w n - Set barcode width
|
|
374
|
+
commands.push(new Uint8Array([0x1d, 0x77, width]));
|
|
375
|
+
|
|
376
|
+
// GS H n - Set HRI position
|
|
377
|
+
commands.push(new Uint8Array([0x1d, 0x48, hriValue]));
|
|
378
|
+
|
|
379
|
+
// Select barcode type and print
|
|
380
|
+
// GS k m d1...dk NUL - Print barcode
|
|
381
|
+
switch (type) {
|
|
382
|
+
case 'CODE39': {
|
|
383
|
+
// CODE39: Start with * and end with *
|
|
384
|
+
const code39Data = `*${data.toUpperCase()}*`;
|
|
385
|
+
commands.push(new Uint8Array([0x1d, 0x6b, 0x04])); // m=4: CODE39
|
|
386
|
+
commands.push(new TextEncoder().encode(code39Data));
|
|
387
|
+
commands.push(new Uint8Array([0x00])); // NUL terminator
|
|
388
|
+
break;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
case 'EAN13': {
|
|
392
|
+
// Validate EAN13 length (12 digits -> 13th check digit)
|
|
393
|
+
const digits = data.replace(/\D/g, '');
|
|
394
|
+
if (digits.length < 12) {
|
|
395
|
+
this.logger.warn(`EAN13 requires 12 digits, got ${digits.length}`);
|
|
396
|
+
}
|
|
397
|
+
commands.push(new Uint8Array([0x1d, 0x6b, 0x02])); // m=2: EAN13
|
|
398
|
+
// Pad or truncate to 12 digits
|
|
399
|
+
const eanData = digits.padStart(12, '0').substring(0, 12);
|
|
400
|
+
commands.push(new TextEncoder().encode(eanData));
|
|
401
|
+
commands.push(new Uint8Array([0x00])); // NUL terminator
|
|
402
|
+
break;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
case 'CODE128':
|
|
406
|
+
default: {
|
|
407
|
+
// CODE128: Use function 3 (automatic mode switching)
|
|
408
|
+
commands.push(new Uint8Array([0x1d, 0x6b, 0x49])); // m=73 (0x49): CODE128
|
|
409
|
+
commands.push(new TextEncoder().encode(data));
|
|
410
|
+
commands.push(new Uint8Array([0x00])); // NUL terminator
|
|
411
|
+
break;
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
return commands;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
/**
|
|
419
|
+
* Print a raster image
|
|
420
|
+
*
|
|
421
|
+
* Converts RGBA image data to 1-bit bitmap using dithering and sends
|
|
422
|
+
* to the printer using STAR's raster image command.
|
|
423
|
+
*
|
|
424
|
+
* @param data - RGBA pixel data (Uint8Array, 4 bytes per pixel)
|
|
425
|
+
* @param width - Image width in pixels
|
|
426
|
+
* @param height - Image height in pixels
|
|
427
|
+
* @param options - Image print options (dithering, threshold, greyscale)
|
|
428
|
+
* @returns Array of command buffers
|
|
429
|
+
*
|
|
430
|
+
* @example
|
|
431
|
+
* ```typescript
|
|
432
|
+
* const imageData = new Uint8Array(width * height * 4); // RGBA
|
|
433
|
+
* driver.image(imageData, 200, 100, { dithering: true });
|
|
434
|
+
* ```
|
|
435
|
+
*/
|
|
436
|
+
image(data: Uint8Array, width: number, height: number, options?: StarImageOptions): Uint8Array[] {
|
|
437
|
+
// Parameter validation
|
|
438
|
+
if (!data || !(data instanceof Uint8Array) || width <= 0 || height <= 0) {
|
|
439
|
+
this.logger.warn('Invalid image parameters');
|
|
440
|
+
return [];
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
const expectedLength = width * height * 4;
|
|
444
|
+
if (data.length !== expectedLength) {
|
|
445
|
+
this.logger.warn(`Invalid image data length: expected ${expectedLength}, got ${data.length}`);
|
|
446
|
+
return [];
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
const commands: Uint8Array[] = [];
|
|
450
|
+
// Options reserved for future dithering/threshold support
|
|
451
|
+
void options?.dithering;
|
|
452
|
+
void options?.threshold;
|
|
453
|
+
|
|
454
|
+
// Convert RGBA to grayscale then to bitmap
|
|
455
|
+
const bitmap = ImageProcessing.toBitmap(data, width, height);
|
|
456
|
+
|
|
457
|
+
// STAR raster image: GS W n1 n2 n3 n4
|
|
458
|
+
// n1: Bytes per line (low)
|
|
459
|
+
// n2: Bytes per line (high)
|
|
460
|
+
// n3: Vertical dots (low)
|
|
461
|
+
// n4: Vertical dots (high)
|
|
462
|
+
const bytesPerLine = Math.ceil(width / 8);
|
|
463
|
+
const xL = bytesPerLine % 256;
|
|
464
|
+
const xH = Math.floor(bytesPerLine / 256);
|
|
465
|
+
// Height info reserved for GS v 0 full raster mode
|
|
466
|
+
void (height % 256);
|
|
467
|
+
void Math.floor(height / 256);
|
|
468
|
+
|
|
469
|
+
// ESC * n1 n2 [data] - Bit image mode
|
|
470
|
+
// Using ESC * (0x1B 0x2A) for compatibility with STAR printers
|
|
471
|
+
commands.push(new Uint8Array([0x1b, 0x2a, xL, xH]));
|
|
472
|
+
|
|
473
|
+
// For each line, we need to output the data
|
|
474
|
+
// This is a simplified version - larger images should use GS v 0
|
|
475
|
+
for (let y = 0; y < height; y++) {
|
|
476
|
+
const lineData = bitmap.slice(y * bytesPerLine, (y + 1) * bytesPerLine);
|
|
477
|
+
commands.push(lineData);
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
return commands;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
/**
|
|
484
|
+
* Sound a beep/buzzer on supported printers
|
|
485
|
+
*
|
|
486
|
+
* Sends a command to activate the printer's built-in buzzer.
|
|
487
|
+
* Useful for alerting operators to print completion or errors.
|
|
488
|
+
*
|
|
489
|
+
* @returns Array of command buffers
|
|
490
|
+
*
|
|
491
|
+
* @example
|
|
492
|
+
* ```typescript
|
|
493
|
+
* driver.beep();
|
|
494
|
+
* ```
|
|
495
|
+
*/
|
|
496
|
+
beep(): Uint8Array[] {
|
|
497
|
+
// ESC B n - Sound buzzer
|
|
498
|
+
// n: 0-255 (number of beeps, duration varies by model)
|
|
499
|
+
return [new Uint8Array([0x1b, 0x42, 0x05])]; // Sound 5 times
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
/**
|
|
503
|
+
* Enable or disable bold text
|
|
504
|
+
*
|
|
505
|
+
* When bold is enabled, subsequent text will be printed in bold.
|
|
506
|
+
* This state persists until explicitly disabled.
|
|
507
|
+
*
|
|
508
|
+
* @param on - true to enable bold, false to disable
|
|
509
|
+
* @returns Array of command buffers
|
|
510
|
+
*
|
|
511
|
+
* @example
|
|
512
|
+
* ```typescript
|
|
513
|
+
* driver.bold(true);
|
|
514
|
+
* driver.text('Bold Text');
|
|
515
|
+
* driver.bold(false);
|
|
516
|
+
* ```
|
|
517
|
+
*/
|
|
518
|
+
bold(on: boolean): Uint8Array[] {
|
|
519
|
+
this._boldEnabled = on;
|
|
520
|
+
// ESC E n - Select bold
|
|
521
|
+
// n=1: Bold on, n=0: Bold off
|
|
522
|
+
return [new Uint8Array([0x1b, 0x45, on ? 0x01 : 0x00])];
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
/**
|
|
526
|
+
* Set text alignment
|
|
527
|
+
*
|
|
528
|
+
* Controls horizontal text alignment for subsequent text.
|
|
529
|
+
* Alignment persists until explicitly changed.
|
|
530
|
+
*
|
|
531
|
+
* @param align - Alignment: 'left' | 'center' | 'right'
|
|
532
|
+
* @returns Array of command buffers
|
|
533
|
+
*
|
|
534
|
+
* @example
|
|
535
|
+
* ```typescript
|
|
536
|
+
* driver.align('center');
|
|
537
|
+
* driver.text('Centered Title');
|
|
538
|
+
* driver.align('left');
|
|
539
|
+
* ```
|
|
540
|
+
*/
|
|
541
|
+
align(align: Alignment): Uint8Array[] {
|
|
542
|
+
this._alignment = align;
|
|
543
|
+
|
|
544
|
+
// ESC a n - Select justification
|
|
545
|
+
// n=0: Left, n=1: Center, n=2: Right
|
|
546
|
+
const alignMap: Record<Alignment, number> = {
|
|
547
|
+
'left': 0x00,
|
|
548
|
+
'center': 0x01,
|
|
549
|
+
'right': 0x02,
|
|
550
|
+
};
|
|
551
|
+
|
|
552
|
+
const alignValue = alignMap[align] ?? 0x00;
|
|
553
|
+
return [new Uint8Array([0x1b, 0x61, alignValue])];
|
|
554
|
+
}
|
|
555
|
+
}
|
package/src/drivers/index.ts
CHANGED
|
@@ -35,3 +35,13 @@ export {
|
|
|
35
35
|
type CpclLineOptions,
|
|
36
36
|
type CpclBoxOptions,
|
|
37
37
|
} from './CpclDriver';
|
|
38
|
+
|
|
39
|
+
export {
|
|
40
|
+
StarPrinter,
|
|
41
|
+
type StarPrinterOptions,
|
|
42
|
+
type Alignment,
|
|
43
|
+
type BarcodeType,
|
|
44
|
+
type StarBarcodeOptions,
|
|
45
|
+
type StarQrOptions,
|
|
46
|
+
type StarImageOptions,
|
|
47
|
+
} from './StarPrinter';
|