taro-bluetooth-print 2.5.0 → 2.6.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/dist/index.cjs.js +1 -1
- package/dist/index.es.js +1 -1
- package/dist/index.umd.js +1 -1
- package/dist/logo.svg +17 -0
- package/dist/manifest.webmanifest +17 -0
- package/dist/types/adapters/WebBluetoothAdapter.d.ts +87 -1
- package/dist/types/adapters/index.d.ts +1 -1
- package/dist/types/barcode/BarcodeGenerator.d.ts +61 -4
- package/dist/types/barcode/index.d.ts +1 -1
- package/dist/types/template/TemplateEngine.d.ts +140 -1
- package/dist/types/utils/uuid.d.ts +191 -0
- package/package.json +1 -1
- package/src/adapters/ReactNativeAdapter.ts +19 -30
- package/src/adapters/WebBluetoothAdapter.ts +275 -14
- package/src/adapters/index.ts +6 -1
- package/src/barcode/BarcodeGenerator.ts +312 -6
- package/src/barcode/index.ts +1 -1
- package/src/drivers/StarPrinter.ts +14 -13
- package/src/encoding/EncodingService.ts +13 -6
- package/src/encoding/korean-japanese.ts +84 -48
- package/src/services/BatchPrintManager.ts +15 -16
- package/src/services/PrintStatistics.ts +8 -8
- package/src/services/ScheduledRetryManager.ts +15 -19
- package/src/template/TemplateEngine.ts +543 -4
- package/src/utils/image.ts +92 -43
- package/src/utils/platform.ts +28 -14
- package/src/utils/uuid.ts +522 -0
- package/src/utils/validation.ts +1155 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Barcode Generator
|
|
3
3
|
*
|
|
4
|
-
* Generates ESC/POS commands for printing 1D barcodes.
|
|
5
|
-
* Supports Code128, Code39, EAN-13, EAN-8, UPC-A, ITF, and
|
|
4
|
+
* Generates ESC/POS commands for printing 1D barcodes and 2D codes.
|
|
5
|
+
* Supports Code128, Code39, EAN-13, EAN-8, UPC-A, ITF, CODABAR, QR_CODE, and PDF417 formats.
|
|
6
6
|
*
|
|
7
7
|
* @example
|
|
8
8
|
* ```typescript
|
|
@@ -33,6 +33,10 @@ export enum BarcodeFormat {
|
|
|
33
33
|
ITF = 'ITF',
|
|
34
34
|
/** CODABAR - Numeric with special start/stop chars */
|
|
35
35
|
CODABAR = 'CODABAR',
|
|
36
|
+
/** QR Code - 2D matrix code */
|
|
37
|
+
QR_CODE = 'QR_CODE',
|
|
38
|
+
/** PDF417 - 2D stacked barcode */
|
|
39
|
+
PDF417 = 'PDF417',
|
|
36
40
|
}
|
|
37
41
|
|
|
38
42
|
/**
|
|
@@ -49,6 +53,16 @@ export interface BarcodeOptions {
|
|
|
49
53
|
showText?: boolean;
|
|
50
54
|
/** Text position */
|
|
51
55
|
textPosition?: 'above' | 'below' | 'both' | 'none';
|
|
56
|
+
/** QR code error correction level (L/M/Q/H, default: M) */
|
|
57
|
+
errorCorrection?: 'L' | 'M' | 'Q' | 'H';
|
|
58
|
+
/** QR code model (1 or 2, default: 2) */
|
|
59
|
+
qrModel?: 1 | 2;
|
|
60
|
+
/** PDF417 compression mode (0-3, default: 2) */
|
|
61
|
+
pdf417Compression?: 0 | 1 | 2 | 3;
|
|
62
|
+
/** PDF417 security level (0-8, default: 2) */
|
|
63
|
+
pdf417Security?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8;
|
|
64
|
+
/** PDF417 columns (1-30, default: 2) */
|
|
65
|
+
pdf417Columns?: number;
|
|
52
66
|
}
|
|
53
67
|
|
|
54
68
|
/**
|
|
@@ -81,9 +95,10 @@ export interface IBarcodeGenerator {
|
|
|
81
95
|
* ESC/POS command constants
|
|
82
96
|
*/
|
|
83
97
|
const GS = 0x1d;
|
|
98
|
+
const ESC = 0x1b;
|
|
84
99
|
|
|
85
100
|
/**
|
|
86
|
-
* ESC/POS barcode type codes
|
|
101
|
+
* ESC/POS barcode type codes (GS k m)
|
|
87
102
|
*/
|
|
88
103
|
const BARCODE_TYPES: Record<BarcodeFormat, number> = {
|
|
89
104
|
[BarcodeFormat.UPCA]: 65, // GS k 65
|
|
@@ -93,6 +108,26 @@ const BARCODE_TYPES: Record<BarcodeFormat, number> = {
|
|
|
93
108
|
[BarcodeFormat.ITF]: 70, // GS k 70
|
|
94
109
|
[BarcodeFormat.CODABAR]: 71, // GS k 71
|
|
95
110
|
[BarcodeFormat.CODE128]: 73, // GS k 73
|
|
111
|
+
[BarcodeFormat.QR_CODE]: 80, // GS k 80 (PDF moved to 81)
|
|
112
|
+
[BarcodeFormat.PDF417]: 81, // GS k 81
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* QR code error correction levels
|
|
117
|
+
*/
|
|
118
|
+
const QR_ERROR_CORRECTION_CODES: Record<string, number> = {
|
|
119
|
+
L: 48, // 0x30
|
|
120
|
+
M: 49, // 0x31
|
|
121
|
+
Q: 50, // 0x32
|
|
122
|
+
H: 51, // 0x33
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* QR code models
|
|
127
|
+
*/
|
|
128
|
+
const QR_MODEL_CODES: Record<number, number> = {
|
|
129
|
+
1: 49, // 0x31
|
|
130
|
+
2: 50, // 0x32
|
|
96
131
|
};
|
|
97
132
|
|
|
98
133
|
/**
|
|
@@ -105,9 +140,19 @@ const TEXT_POSITION_CODES: Record<string, number> = {
|
|
|
105
140
|
both: 3,
|
|
106
141
|
};
|
|
107
142
|
|
|
143
|
+
/**
|
|
144
|
+
* PDF417 compression modes
|
|
145
|
+
*/
|
|
146
|
+
const PDF417_COMPRESSION_CODES: Record<number, number> = {
|
|
147
|
+
0: 48, // 0x30 - linear
|
|
148
|
+
1: 49, // 0x31 - compressed
|
|
149
|
+
2: 50, // 0x32 - enhanced
|
|
150
|
+
3: 51, // 0x33 - auto
|
|
151
|
+
};
|
|
152
|
+
|
|
108
153
|
/**
|
|
109
154
|
* Barcode Generator class
|
|
110
|
-
* Generates ESC/POS commands for 1D barcodes
|
|
155
|
+
* Generates ESC/POS commands for 1D barcodes and 2D codes
|
|
111
156
|
*/
|
|
112
157
|
export class BarcodeGenerator implements IBarcodeGenerator {
|
|
113
158
|
/**
|
|
@@ -124,6 +169,23 @@ export class BarcodeGenerator implements IBarcodeGenerator {
|
|
|
124
169
|
return [];
|
|
125
170
|
}
|
|
126
171
|
|
|
172
|
+
// Handle 2D codes separately
|
|
173
|
+
if (options.format === BarcodeFormat.QR_CODE) {
|
|
174
|
+
return this.generateQRCode(content, options);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (options.format === BarcodeFormat.PDF417) {
|
|
178
|
+
return this.generatePDF417(content, options);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// 1D barcode generation
|
|
182
|
+
return this.generate1DBarcode(content, options);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Generate 1D barcode commands
|
|
187
|
+
*/
|
|
188
|
+
private generate1DBarcode(content: string, options: BarcodeOptions): Uint8Array[] {
|
|
127
189
|
const commands: Uint8Array[] = [];
|
|
128
190
|
|
|
129
191
|
// 1. Set barcode height: GS h n (n = 1-255)
|
|
@@ -148,6 +210,106 @@ export class BarcodeGenerator implements IBarcodeGenerator {
|
|
|
148
210
|
return commands;
|
|
149
211
|
}
|
|
150
212
|
|
|
213
|
+
/**
|
|
214
|
+
* Generate QR code commands using ESC/POS GS k 80-83
|
|
215
|
+
*
|
|
216
|
+
* @param content - QR code content
|
|
217
|
+
* @param options - QR code options
|
|
218
|
+
* @returns Array of ESC/POS command buffers
|
|
219
|
+
*/
|
|
220
|
+
private generateQRCode(content: string, options: BarcodeOptions): Uint8Array[] {
|
|
221
|
+
const commands: Uint8Array[] = [];
|
|
222
|
+
const model = options.qrModel ?? 2;
|
|
223
|
+
const size = this.clampQRSize(options.height ?? 6);
|
|
224
|
+
const errorLevel = options.errorCorrection ?? 'M';
|
|
225
|
+
|
|
226
|
+
// 1. Set QR code model: GS ( k 04 00 31 41 50 n
|
|
227
|
+
// pH=0, pL=4, cn=31 (1), fn=65 (A), 50=0, n=model(1 or 2)
|
|
228
|
+
const qrModelCode = QR_MODEL_CODES[model] ?? 50;
|
|
229
|
+
commands.push(new Uint8Array([GS, 0x28, 0x6b, 0x04, 0x00, 0x31, 0x41, 0x50, qrModelCode]));
|
|
230
|
+
|
|
231
|
+
// 2. Set QR code size (module size): GS ( k 03 00 31 43 n
|
|
232
|
+
commands.push(new Uint8Array([GS, 0x28, 0x6b, 0x03, 0x00, 0x31, 0x43, size]));
|
|
233
|
+
|
|
234
|
+
// 3. Set error correction level: GS ( k 03 00 31 45 n
|
|
235
|
+
const qrErrorCode = QR_ERROR_CORRECTION_CODES[errorLevel] ?? 49;
|
|
236
|
+
commands.push(new Uint8Array([GS, 0x28, 0x6b, 0x03, 0x00, 0x31, 0x45, qrErrorCode]));
|
|
237
|
+
|
|
238
|
+
// 4. Store data: GS ( k pL pH 31 50 30 d1...dk
|
|
239
|
+
const dataBytes = this.encodeUTF8(content);
|
|
240
|
+
const storeLen = dataBytes.length + 3;
|
|
241
|
+
commands.push(
|
|
242
|
+
new Uint8Array([
|
|
243
|
+
GS,
|
|
244
|
+
0x28,
|
|
245
|
+
0x6b,
|
|
246
|
+
storeLen & 0xff,
|
|
247
|
+
(storeLen >> 8) & 0xff,
|
|
248
|
+
0x31,
|
|
249
|
+
0x50,
|
|
250
|
+
0x30,
|
|
251
|
+
...dataBytes,
|
|
252
|
+
])
|
|
253
|
+
);
|
|
254
|
+
|
|
255
|
+
// 5. Print QR code: GS ( k 03 00 31 51 30
|
|
256
|
+
commands.push(new Uint8Array([GS, 0x28, 0x6b, 0x03, 0x00, 0x31, 0x51, 0x30]));
|
|
257
|
+
|
|
258
|
+
return commands;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Generate PDF417 commands using ESC/POS GS k 81
|
|
263
|
+
*
|
|
264
|
+
* PDF417 is a 2D stacked barcode format supported by many thermal printers.
|
|
265
|
+
* The printer will handle the actual encoding and generation of the PDF417 symbol.
|
|
266
|
+
*
|
|
267
|
+
* @param content - PDF417 content (text data)
|
|
268
|
+
* @param options - PDF417 options
|
|
269
|
+
* @returns Array of ESC/POS command buffers
|
|
270
|
+
*/
|
|
271
|
+
private generatePDF417(content: string, options: BarcodeOptions): Uint8Array[] {
|
|
272
|
+
const commands: Uint8Array[] = [];
|
|
273
|
+
const compression = options.pdf417Compression ?? 2;
|
|
274
|
+
const security = options.pdf417Security ?? 2;
|
|
275
|
+
const columns = this.clampPDF417Columns(options.pdf417Columns ?? 2);
|
|
276
|
+
|
|
277
|
+
// 1. Set PDF417 parameters: GS ( k 07 00 31 30 n1 n2 n3 n4
|
|
278
|
+
// n1 = columns (1-30), n2 = security level (0-8), n3 = truncation (0=none), n4 = reserved
|
|
279
|
+
commands.push(
|
|
280
|
+
new Uint8Array([
|
|
281
|
+
GS,
|
|
282
|
+
0x28,
|
|
283
|
+
0x6b,
|
|
284
|
+
0x07,
|
|
285
|
+
0x00,
|
|
286
|
+
0x31,
|
|
287
|
+
0x30, // Function 30 (PDF417 set)
|
|
288
|
+
columns,
|
|
289
|
+
security,
|
|
290
|
+
0, // truncation off
|
|
291
|
+
0, // reserved
|
|
292
|
+
])
|
|
293
|
+
);
|
|
294
|
+
|
|
295
|
+
// 2. Set data compression mode: ESC ] 04 n
|
|
296
|
+
const pdfCompressionCode = PDF417_COMPRESSION_CODES[compression] ?? 50;
|
|
297
|
+
commands.push(new Uint8Array([ESC, 0x5d, 0x04, pdfCompressionCode]));
|
|
298
|
+
|
|
299
|
+
// 3. Store PDF417 data: GS k 81 pL pH d1...dk
|
|
300
|
+
const dataBytes = this.encodeUTF8(content);
|
|
301
|
+
const storeLen = dataBytes.length + 3;
|
|
302
|
+
commands.push(
|
|
303
|
+
new Uint8Array([GS, 0x6b, 81, storeLen & 0xff, (storeLen >> 8) & 0xff, ...dataBytes])
|
|
304
|
+
);
|
|
305
|
+
|
|
306
|
+
// 4. Print PDF417: GS k 81 00 (alternative form if needed)
|
|
307
|
+
// Actually for PDF417 print: GS ( k 03 00 31 51 31
|
|
308
|
+
commands.push(new Uint8Array([GS, 0x28, 0x6b, 0x03, 0x00, 0x31, 0x51, 0x31]));
|
|
309
|
+
|
|
310
|
+
return commands;
|
|
311
|
+
}
|
|
312
|
+
|
|
151
313
|
/**
|
|
152
314
|
* Validate barcode content for the specified format
|
|
153
315
|
*
|
|
@@ -158,7 +320,8 @@ export class BarcodeGenerator implements IBarcodeGenerator {
|
|
|
158
320
|
validate(content: string, format: BarcodeFormat): ValidationResult {
|
|
159
321
|
const errors: ValidationResult['errors'] = [];
|
|
160
322
|
|
|
161
|
-
|
|
323
|
+
// Common validation for all formats
|
|
324
|
+
if (content === null || content === undefined) {
|
|
162
325
|
errors.push({
|
|
163
326
|
field: 'content',
|
|
164
327
|
message: 'Barcode content is required',
|
|
@@ -167,6 +330,41 @@ export class BarcodeGenerator implements IBarcodeGenerator {
|
|
|
167
330
|
return { valid: false, errors };
|
|
168
331
|
}
|
|
169
332
|
|
|
333
|
+
if (typeof content !== 'string') {
|
|
334
|
+
errors.push({
|
|
335
|
+
field: 'content',
|
|
336
|
+
message: 'Barcode content must be a string',
|
|
337
|
+
code: 'INVALID_TYPE',
|
|
338
|
+
});
|
|
339
|
+
return { valid: false, errors };
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
if (content.length === 0) {
|
|
343
|
+
errors.push({
|
|
344
|
+
field: 'content',
|
|
345
|
+
message: 'Barcode content cannot be empty',
|
|
346
|
+
code: 'REQUIRED',
|
|
347
|
+
});
|
|
348
|
+
return { valid: false, errors };
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// Validate minimum content length for 2D codes
|
|
352
|
+
if (format === BarcodeFormat.QR_CODE && content.length > 7089) {
|
|
353
|
+
errors.push({
|
|
354
|
+
field: 'content',
|
|
355
|
+
message: 'QR code content exceeds maximum length (7089 bytes for binary mode)',
|
|
356
|
+
code: 'CONTENT_TOO_LONG',
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
if (format === BarcodeFormat.PDF417 && content.length > 1850) {
|
|
361
|
+
errors.push({
|
|
362
|
+
field: 'content',
|
|
363
|
+
message: 'PDF417 content exceeds maximum length (~1850 bytes)',
|
|
364
|
+
code: 'CONTENT_TOO_LONG',
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
|
|
170
368
|
switch (format) {
|
|
171
369
|
case BarcodeFormat.EAN13:
|
|
172
370
|
this.validateEAN13(content, errors);
|
|
@@ -189,8 +387,13 @@ export class BarcodeGenerator implements IBarcodeGenerator {
|
|
|
189
387
|
case BarcodeFormat.CODABAR:
|
|
190
388
|
this.validateCodabar(content, errors);
|
|
191
389
|
break;
|
|
390
|
+
case BarcodeFormat.QR_CODE:
|
|
391
|
+
this.validateQRCode(content, errors);
|
|
392
|
+
break;
|
|
393
|
+
case BarcodeFormat.PDF417:
|
|
394
|
+
this.validatePDF417(content, errors);
|
|
395
|
+
break;
|
|
192
396
|
default: {
|
|
193
|
-
// This should never happen if all enum cases are handled
|
|
194
397
|
errors.push({
|
|
195
398
|
field: 'format',
|
|
196
399
|
message: `Unsupported barcode format: ${String(format)}`,
|
|
@@ -202,6 +405,69 @@ export class BarcodeGenerator implements IBarcodeGenerator {
|
|
|
202
405
|
return { valid: errors.length === 0, errors };
|
|
203
406
|
}
|
|
204
407
|
|
|
408
|
+
/**
|
|
409
|
+
* Validate QR code content
|
|
410
|
+
*/
|
|
411
|
+
private validateQRCode(content: string, errors: ValidationResult['errors']): void {
|
|
412
|
+
if (content.length === 0) {
|
|
413
|
+
errors.push({
|
|
414
|
+
field: 'content',
|
|
415
|
+
message: 'QR code content cannot be empty',
|
|
416
|
+
code: 'REQUIRED',
|
|
417
|
+
});
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// Check for invalid characters in numeric/alphanumeric mode (if we were using those)
|
|
421
|
+
// For byte mode, all characters 0-255 are valid
|
|
422
|
+
if (content.length > 7089) {
|
|
423
|
+
errors.push({
|
|
424
|
+
field: 'content',
|
|
425
|
+
message: 'QR code content exceeds maximum binary mode length',
|
|
426
|
+
code: 'CONTENT_TOO_LONG',
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* Validate PDF417 content
|
|
433
|
+
*/
|
|
434
|
+
private validatePDF417(content: string, errors: ValidationResult['errors']): void {
|
|
435
|
+
if (content.length === 0) {
|
|
436
|
+
errors.push({
|
|
437
|
+
field: 'content',
|
|
438
|
+
message: 'PDF417 content cannot be empty',
|
|
439
|
+
code: 'REQUIRED',
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
if (content.length > 1850) {
|
|
444
|
+
errors.push({
|
|
445
|
+
field: 'content',
|
|
446
|
+
message: 'PDF417 content exceeds maximum length',
|
|
447
|
+
code: 'CONTENT_TOO_LONG',
|
|
448
|
+
});
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
// PDF417 supports ASCII 0-127 in text mode
|
|
452
|
+
let hasHighChars = false;
|
|
453
|
+
for (let i = 0; i < content.length; i++) {
|
|
454
|
+
const code = content.charCodeAt(i);
|
|
455
|
+
if (code > 127) {
|
|
456
|
+
hasHighChars = true;
|
|
457
|
+
break;
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
if (hasHighChars) {
|
|
462
|
+
errors.push({
|
|
463
|
+
field: 'content',
|
|
464
|
+
message:
|
|
465
|
+
'PDF417 standard mode only supports ASCII characters. Consider using compression mode.',
|
|
466
|
+
code: 'INVALID_CHARACTERS',
|
|
467
|
+
});
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
|
|
205
471
|
/**
|
|
206
472
|
* Get list of supported barcode formats
|
|
207
473
|
*
|
|
@@ -450,6 +716,32 @@ export class BarcodeGenerator implements IBarcodeGenerator {
|
|
|
450
716
|
return content.split('').map(c => c.charCodeAt(0));
|
|
451
717
|
}
|
|
452
718
|
|
|
719
|
+
/**
|
|
720
|
+
* Encode string to UTF-8 bytes
|
|
721
|
+
*/
|
|
722
|
+
private encodeUTF8(str: string): number[] {
|
|
723
|
+
const result: number[] = [];
|
|
724
|
+
for (let i = 0; i < str.length; i++) {
|
|
725
|
+
const charCode = str.charCodeAt(i);
|
|
726
|
+
if (charCode < 0x80) {
|
|
727
|
+
result.push(charCode);
|
|
728
|
+
} else if (charCode < 0x800) {
|
|
729
|
+
result.push(0xc0 | (charCode >> 6));
|
|
730
|
+
result.push(0x80 | (charCode & 0x3f));
|
|
731
|
+
} else if (charCode < 0x10000) {
|
|
732
|
+
result.push(0xe0 | (charCode >> 12));
|
|
733
|
+
result.push(0x80 | ((charCode >> 6) & 0x3f));
|
|
734
|
+
result.push(0x80 | (charCode & 0x3f));
|
|
735
|
+
} else {
|
|
736
|
+
result.push(0xf0 | (charCode >> 18));
|
|
737
|
+
result.push(0x80 | ((charCode >> 12) & 0x3f));
|
|
738
|
+
result.push(0x80 | ((charCode >> 6) & 0x3f));
|
|
739
|
+
result.push(0x80 | (charCode & 0x3f));
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
return result;
|
|
743
|
+
}
|
|
744
|
+
|
|
453
745
|
/**
|
|
454
746
|
* Get text position code
|
|
455
747
|
*/
|
|
@@ -475,6 +767,20 @@ export class BarcodeGenerator implements IBarcodeGenerator {
|
|
|
475
767
|
private clampWidth(width: number): number {
|
|
476
768
|
return Math.max(2, Math.min(6, Math.floor(width)));
|
|
477
769
|
}
|
|
770
|
+
|
|
771
|
+
/**
|
|
772
|
+
* Clamp QR code size to valid range (1-16)
|
|
773
|
+
*/
|
|
774
|
+
private clampQRSize(size: number): number {
|
|
775
|
+
return Math.max(1, Math.min(16, Math.floor(size)));
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
/**
|
|
779
|
+
* Clamp PDF417 columns to valid range (1-30)
|
|
780
|
+
*/
|
|
781
|
+
private clampPDF417Columns(columns: number): number {
|
|
782
|
+
return Math.max(1, Math.min(30, Math.floor(columns)));
|
|
783
|
+
}
|
|
478
784
|
}
|
|
479
785
|
|
|
480
786
|
// Export singleton instance for convenience
|
package/src/barcode/index.ts
CHANGED
|
@@ -275,14 +275,14 @@ export class StarPrinter implements IPrinterDriver {
|
|
|
275
275
|
return [];
|
|
276
276
|
}
|
|
277
277
|
|
|
278
|
-
const model =
|
|
278
|
+
const model = options?.model ?? 2;
|
|
279
279
|
// STAR uses cellSize instead of 'size' - extract safely
|
|
280
280
|
let cellSize = 4;
|
|
281
281
|
if (options) {
|
|
282
282
|
if ('cellSize' in options) {
|
|
283
|
-
cellSize =
|
|
283
|
+
cellSize = options.cellSize ?? 4;
|
|
284
284
|
} else if ('size' in options) {
|
|
285
|
-
cellSize =
|
|
285
|
+
cellSize = options.size ?? 4;
|
|
286
286
|
}
|
|
287
287
|
}
|
|
288
288
|
const ecLevel = options?.errorCorrection ?? 'M';
|
|
@@ -310,9 +310,10 @@ export class StarPrinter implements IPrinterDriver {
|
|
|
310
310
|
|
|
311
311
|
// Step 4: QR Data - Calculate length and send
|
|
312
312
|
// Get encoded data
|
|
313
|
-
const encoded =
|
|
314
|
-
|
|
315
|
-
|
|
313
|
+
const encoded =
|
|
314
|
+
this.useEncodingService && this.encodingService.isSupported('UTF-8')
|
|
315
|
+
? this.encodingService.encode(content, 'UTF-8')
|
|
316
|
+
: new TextEncoder().encode(content);
|
|
316
317
|
|
|
317
318
|
// Send QR data: Length prefix (2 bytes big-endian) + data
|
|
318
319
|
const len = encoded.length;
|
|
@@ -360,10 +361,10 @@ export class StarPrinter implements IPrinterDriver {
|
|
|
360
361
|
|
|
361
362
|
// HRI position mapping
|
|
362
363
|
const hriMap: Record<string, number> = {
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
364
|
+
none: 0,
|
|
365
|
+
above: 1,
|
|
366
|
+
below: 2,
|
|
367
|
+
both: 3,
|
|
367
368
|
};
|
|
368
369
|
const hriValue = hriMap[hri] ?? 2;
|
|
369
370
|
|
|
@@ -544,9 +545,9 @@ export class StarPrinter implements IPrinterDriver {
|
|
|
544
545
|
// ESC a n - Select justification
|
|
545
546
|
// n=0: Left, n=1: Center, n=2: Right
|
|
546
547
|
const alignMap: Record<Alignment, number> = {
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
548
|
+
left: 0x00,
|
|
549
|
+
center: 0x01,
|
|
550
|
+
right: 0x02,
|
|
550
551
|
};
|
|
551
552
|
|
|
552
553
|
const alignValue = alignMap[align] ?? 0x00;
|
|
@@ -279,10 +279,19 @@ export class EncodingService {
|
|
|
279
279
|
|
|
280
280
|
const normalized = this.normalizeEncoding(encoding);
|
|
281
281
|
return [
|
|
282
|
-
'GBK',
|
|
283
|
-
'
|
|
284
|
-
'
|
|
285
|
-
'
|
|
282
|
+
'GBK',
|
|
283
|
+
'GB2312',
|
|
284
|
+
'BIG5',
|
|
285
|
+
'UTF8',
|
|
286
|
+
'UTF-8',
|
|
287
|
+
'EUCKR',
|
|
288
|
+
'EUC-KR',
|
|
289
|
+
'SHIFTJIS',
|
|
290
|
+
'SHIFT-JIS',
|
|
291
|
+
'SJIS',
|
|
292
|
+
'ISO2022JP',
|
|
293
|
+
'ISO-2022-JP',
|
|
294
|
+
'JIS',
|
|
286
295
|
].includes(normalized);
|
|
287
296
|
}
|
|
288
297
|
|
|
@@ -487,8 +496,6 @@ export class EncodingService {
|
|
|
487
496
|
// Try native TextEncoder first (full coverage)
|
|
488
497
|
try {
|
|
489
498
|
const native = new TextEncoder();
|
|
490
|
-
// @ts-ignore - TextEncoder supports EUC-KR in some environments
|
|
491
|
-
const encoded = native.encodeInto ? null : null;
|
|
492
499
|
// Use native EUC-KR encoding if available
|
|
493
500
|
const test = native.encode('가');
|
|
494
501
|
if (test.length > 0) {
|