@thermal-label/brother-ql-core 0.2.1 → 0.4.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 +1 -1
- package/data/devices.json +823 -0
- package/data/media.json +823 -0
- package/dist/__tests__/devices.test.js +112 -31
- package/dist/__tests__/devices.test.js.map +1 -1
- package/dist/__tests__/media.test.js +274 -4
- package/dist/__tests__/media.test.js.map +1 -1
- package/dist/__tests__/pack-bits.test.d.ts +2 -0
- package/dist/__tests__/pack-bits.test.d.ts.map +1 -0
- package/dist/__tests__/pack-bits.test.js +90 -0
- package/dist/__tests__/pack-bits.test.js.map +1 -0
- package/dist/__tests__/preview.test.js +1 -1
- package/dist/__tests__/preview.test.js.map +1 -1
- package/dist/__tests__/protocol.test.js +214 -2
- package/dist/__tests__/protocol.test.js.map +1 -1
- package/dist/__tests__/status.test.js +71 -0
- package/dist/__tests__/status.test.js.map +1 -1
- package/dist/devices.d.ts +14 -271
- package/dist/devices.d.ts.map +1 -1
- package/dist/devices.generated.d.ts +696 -0
- package/dist/devices.generated.d.ts.map +1 -0
- package/dist/devices.generated.js +831 -0
- package/dist/devices.generated.js.map +1 -0
- package/dist/devices.js +28 -273
- package/dist/devices.js.map +1 -1
- package/dist/index.d.ts +10 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -6
- package/dist/index.js.map +1 -1
- package/dist/media.d.ts +37 -10
- package/dist/media.d.ts.map +1 -1
- package/dist/media.generated.d.ts +4 -0
- package/dist/media.generated.d.ts.map +1 -0
- package/dist/media.generated.js +1640 -0
- package/dist/media.generated.js.map +1 -0
- package/dist/media.js +75 -264
- package/dist/media.js.map +1 -1
- package/dist/orientation.d.ts +11 -0
- package/dist/orientation.d.ts.map +1 -0
- package/dist/orientation.js +10 -0
- package/dist/orientation.js.map +1 -0
- package/dist/pack-bits.d.ts +20 -0
- package/dist/pack-bits.d.ts.map +1 -0
- package/dist/pack-bits.js +61 -0
- package/dist/pack-bits.js.map +1 -0
- package/dist/preview.d.ts +6 -6
- package/dist/preview.d.ts.map +1 -1
- package/dist/preview.js +11 -12
- package/dist/preview.js.map +1 -1
- package/dist/protocol.d.ts +54 -3
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js +125 -20
- package/dist/protocol.js.map +1 -1
- package/dist/status.d.ts +5 -2
- package/dist/status.d.ts.map +1 -1
- package/dist/status.js +6 -3
- package/dist/status.js.map +1 -1
- package/dist/types.d.ts +106 -31
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +1 -2
- package/dist/types.js.map +1 -1
- package/package.json +13 -9
- package/src/__tests__/devices.test.ts +122 -32
- package/src/__tests__/media.test.ts +312 -4
- package/src/__tests__/pack-bits.test.ts +92 -0
- package/src/__tests__/preview.test.ts +1 -1
- package/src/__tests__/protocol.test.ts +256 -1
- package/src/__tests__/status.test.ts +87 -0
- package/src/devices.generated.ts +840 -0
- package/src/devices.ts +31 -273
- package/src/index.ts +36 -8
- package/src/media.generated.ts +1644 -0
- package/src/media.ts +87 -264
- package/src/orientation.ts +11 -0
- package/src/pack-bits.ts +64 -0
- package/src/preview.ts +13 -12
- package/src/protocol.ts +204 -19
- package/src/status.ts +11 -5
- package/src/types.ts +113 -32
- package/dist/__tests__/colour.test.d.ts +0 -2
- package/dist/__tests__/colour.test.d.ts.map +0 -1
- package/dist/__tests__/colour.test.js +0 -106
- package/dist/__tests__/colour.test.js.map +0 -1
- package/dist/colour.d.ts +0 -26
- package/dist/colour.d.ts.map +0 -1
- package/dist/colour.js +0 -84
- package/dist/colour.js.map +0 -1
- package/src/__tests__/colour.test.ts +0 -126
- package/src/colour.ts +0 -101
|
@@ -32,7 +32,7 @@ describe('createPreviewOffline', () => {
|
|
|
32
32
|
expect(preview.assumed).toBe(false);
|
|
33
33
|
});
|
|
34
34
|
|
|
35
|
-
it('returns black + red planes for
|
|
35
|
+
it('returns black + red planes for multi-ink media (DK-22251)', () => {
|
|
36
36
|
const image = solidRgba(8, 8, [255, 0, 0, 255]);
|
|
37
37
|
const preview = createPreviewOffline(image, MEDIA[251]!);
|
|
38
38
|
expect(preview.planes.map(p => p.name)).toEqual(['black', 'red']);
|
|
@@ -13,8 +13,10 @@ import {
|
|
|
13
13
|
buildVariousMode,
|
|
14
14
|
buildExpandedMode,
|
|
15
15
|
encodeJob,
|
|
16
|
+
encodeJobForEngine,
|
|
17
|
+
type EncoderEngine,
|
|
16
18
|
} from '../protocol.js';
|
|
17
|
-
import {
|
|
19
|
+
import type { PageData } from '../types.js';
|
|
18
20
|
import { MEDIA } from '../media.js';
|
|
19
21
|
|
|
20
22
|
describe('buildInvalidate', () => {
|
|
@@ -170,6 +172,52 @@ describe('encodeJob', () => {
|
|
|
170
172
|
}
|
|
171
173
|
expect(found).toBe(true);
|
|
172
174
|
});
|
|
175
|
+
|
|
176
|
+
it('compress option PackBits-encodes each raster row (LEN < uncompressed size)', () => {
|
|
177
|
+
// All-zero bitmap → every row is 90 zero bytes. PackBits collapses each
|
|
178
|
+
// row to 2 wire bytes (0xA7 0x00), so the LEN byte in the row header
|
|
179
|
+
// should be 2 instead of 90.
|
|
180
|
+
const page: PageData = { ...makePage(696, 3), options: { compress: true } };
|
|
181
|
+
const buf = encodeJob([page]);
|
|
182
|
+
// Find single-color row headers (0x67 0x00 LEN). With compression on,
|
|
183
|
+
// LEN should be 2 for each row, and the payload byte should be 0x00.
|
|
184
|
+
const rowHeaders: number[] = [];
|
|
185
|
+
for (let i = 0; i < buf.length - 4; i++) {
|
|
186
|
+
if (buf[i] === 0x67 && buf[i + 1] === 0x00) {
|
|
187
|
+
// Skip the byte sequence inside the 200-byte invalidate block
|
|
188
|
+
// (which is also all zeros, so 0x67 won't appear there). Heuristic:
|
|
189
|
+
// verify the next byte after LEN is the PackBits header 0xA7.
|
|
190
|
+
if (buf[i + 2] === 2 && buf[i + 3] === 0xa7 && buf[i + 4] === 0x00) {
|
|
191
|
+
rowHeaders.push(i);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
expect(rowHeaders.length).toBe(3);
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
it('compress option two-color: both planes PackBits-encoded per row', () => {
|
|
199
|
+
const bitmap = createBitmap(696, 2);
|
|
200
|
+
const redBitmap = createBitmap(696, 2);
|
|
201
|
+
const page: PageData = {
|
|
202
|
+
bitmap,
|
|
203
|
+
redBitmap,
|
|
204
|
+
media: media62,
|
|
205
|
+
options: { compress: true },
|
|
206
|
+
};
|
|
207
|
+
const buf = encodeJob([page]);
|
|
208
|
+
const blackRows: number[] = [];
|
|
209
|
+
const redRows: number[] = [];
|
|
210
|
+
for (let i = 0; i < buf.length - 4; i++) {
|
|
211
|
+
// Each compressed row is [0x77 0x01|0x02 0x02 0xA7 0x00] for an empty
|
|
212
|
+
// 696-pin row (90 zero bytes → 2 PackBits bytes).
|
|
213
|
+
if (buf[i] === 0x77 && buf[i + 2] === 2 && buf[i + 3] === 0xa7 && buf[i + 4] === 0x00) {
|
|
214
|
+
if (buf[i + 1] === 0x01) blackRows.push(i);
|
|
215
|
+
if (buf[i + 1] === 0x02) redRows.push(i);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
expect(blackRows.length).toBe(2);
|
|
219
|
+
expect(redRows.length).toBe(2);
|
|
220
|
+
});
|
|
173
221
|
});
|
|
174
222
|
|
|
175
223
|
describe('buildRasterMode', () => {
|
|
@@ -239,3 +287,210 @@ describe('buildCompression', () => {
|
|
|
239
287
|
expect(Array.from(buildCompression(false))).toEqual([0x4d, 0x00]);
|
|
240
288
|
});
|
|
241
289
|
});
|
|
290
|
+
|
|
291
|
+
describe('encodeJobForEngine — dispatch + invalidate-byte derivation', () => {
|
|
292
|
+
const TZE_12MM = MEDIA[404]!;
|
|
293
|
+
const DK_62MM = MEDIA[259]!;
|
|
294
|
+
|
|
295
|
+
const NARROW_PT_ENGINE: EncoderEngine = {
|
|
296
|
+
protocol: 'pt-raster',
|
|
297
|
+
headDots: 128,
|
|
298
|
+
capabilities: { autocut: true, mediaDetection: true, highResDpi: 360 },
|
|
299
|
+
};
|
|
300
|
+
const WIDE_PT_ENGINE: EncoderEngine = {
|
|
301
|
+
protocol: 'pt-raster',
|
|
302
|
+
headDots: 560,
|
|
303
|
+
capabilities: { autocut: true, mediaDetection: true, highResDpi: 720 },
|
|
304
|
+
};
|
|
305
|
+
const QL_TWO_COLOR_ENGINE: EncoderEngine = {
|
|
306
|
+
protocol: 'ql-raster',
|
|
307
|
+
headDots: 720,
|
|
308
|
+
capabilities: { autocut: true, mediaDetection: true, twoColor: true },
|
|
309
|
+
};
|
|
310
|
+
const QL_SINGLE_COLOR_ENGINE: EncoderEngine = {
|
|
311
|
+
protocol: 'ql-raster',
|
|
312
|
+
headDots: 720,
|
|
313
|
+
capabilities: { autocut: true, mediaDetection: true },
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
it('PT-raster job uses 200-byte invalidate', () => {
|
|
317
|
+
const bitmap = createBitmap(70, 5);
|
|
318
|
+
const out = encodeJobForEngine([{ bitmap, media: TZE_12MM }], {}, NARROW_PT_ENGINE);
|
|
319
|
+
// After buildRasterMode (4 bytes: 0x1B 0x69 0x61 0x01), invalidate begins.
|
|
320
|
+
expect(out.slice(0, 4)).toEqual(new Uint8Array([0x1b, 0x69, 0x61, 0x01]));
|
|
321
|
+
// Confirm the next 200 bytes are zero (PT invalidate).
|
|
322
|
+
for (let i = 4; i < 204; i++) expect(out[i]).toBe(0);
|
|
323
|
+
// The byte after the 200-zero invalidate should be 0x1B (start of buildInitialize),
|
|
324
|
+
// proving the invalidate is not 400 bytes.
|
|
325
|
+
expect(out[204]).toBe(0x1b);
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
it('QL two-colour engine bumps invalidate to 400 bytes', () => {
|
|
329
|
+
const bitmap = createBitmap(696, 5);
|
|
330
|
+
const out = encodeJobForEngine([{ bitmap, media: DK_62MM }], {}, QL_TWO_COLOR_ENGINE);
|
|
331
|
+
expect(out.slice(0, 4)).toEqual(new Uint8Array([0x1b, 0x69, 0x61, 0x01]));
|
|
332
|
+
for (let i = 4; i < 404; i++) expect(out[i]).toBe(0);
|
|
333
|
+
expect(out[404]).toBe(0x1b);
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
it('QL single-colour engine keeps invalidate at 200 bytes', () => {
|
|
337
|
+
const bitmap = createBitmap(696, 5);
|
|
338
|
+
const out = encodeJobForEngine([{ bitmap, media: DK_62MM }], {}, QL_SINGLE_COLOR_ENGINE);
|
|
339
|
+
for (let i = 4; i < 204; i++) expect(out[i]).toBe(0);
|
|
340
|
+
expect(out[204]).toBe(0x1b);
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
it('PT-raster expanded-mode uses bit 6 (0x40) for high-res', () => {
|
|
344
|
+
const bitmap = createBitmap(70, 5);
|
|
345
|
+
const out = encodeJobForEngine(
|
|
346
|
+
[{ bitmap, media: TZE_12MM, options: { highResolution: true } }],
|
|
347
|
+
{},
|
|
348
|
+
NARROW_PT_ENGINE,
|
|
349
|
+
);
|
|
350
|
+
// Locate the ESC i K command (0x1B 0x69 0x4B FLAGS).
|
|
351
|
+
let idx = -1;
|
|
352
|
+
for (let i = 0; i < out.length - 3; i++) {
|
|
353
|
+
if (out[i] === 0x1b && out[i + 1] === 0x69 && out[i + 2] === 0x4b) {
|
|
354
|
+
idx = i;
|
|
355
|
+
break;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
expect(idx).toBeGreaterThanOrEqual(0);
|
|
359
|
+
expect((out[idx + 3] ?? 0) & 0x40).toBe(0x40);
|
|
360
|
+
expect((out[idx + 3] ?? 0) & 0x10).toBe(0x00);
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
it('PT-raster feed-margin defaults to 14 dots', () => {
|
|
364
|
+
const bitmap = createBitmap(70, 5);
|
|
365
|
+
const out = encodeJobForEngine([{ bitmap, media: TZE_12MM }], {}, NARROW_PT_ENGINE);
|
|
366
|
+
// Locate ESC i d (0x1B 0x69 0x64 LO HI).
|
|
367
|
+
let idx = -1;
|
|
368
|
+
for (let i = 0; i < out.length - 4; i++) {
|
|
369
|
+
if (out[i] === 0x1b && out[i + 1] === 0x69 && out[i + 2] === 0x64) {
|
|
370
|
+
idx = i;
|
|
371
|
+
break;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
expect(idx).toBeGreaterThanOrEqual(0);
|
|
375
|
+
const lo = out[idx + 3] ?? 0;
|
|
376
|
+
const hi = out[idx + 4] ?? 0;
|
|
377
|
+
expect(lo | (hi << 8)).toBe(14);
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
it('QL feed-margin defaults to 35 dots', () => {
|
|
381
|
+
const bitmap = createBitmap(696, 5);
|
|
382
|
+
const out = encodeJobForEngine([{ bitmap, media: DK_62MM }], {}, QL_SINGLE_COLOR_ENGINE);
|
|
383
|
+
let idx = -1;
|
|
384
|
+
for (let i = 0; i < out.length - 4; i++) {
|
|
385
|
+
if (out[i] === 0x1b && out[i + 1] === 0x69 && out[i + 2] === 0x64) {
|
|
386
|
+
idx = i;
|
|
387
|
+
break;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
expect(idx).toBeGreaterThanOrEqual(0);
|
|
391
|
+
const lo = out[idx + 3] ?? 0;
|
|
392
|
+
const hi = out[idx + 4] ?? 0;
|
|
393
|
+
expect(lo | (hi << 8)).toBe(35);
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
it('PT high-res duplicates each raster line', () => {
|
|
397
|
+
const bitmap = createBitmap(70, 3);
|
|
398
|
+
const noHighRes = encodeJobForEngine([{ bitmap, media: TZE_12MM }], {}, NARROW_PT_ENGINE);
|
|
399
|
+
const highRes = encodeJobForEngine(
|
|
400
|
+
[{ bitmap, media: TZE_12MM, options: { highResolution: true } }],
|
|
401
|
+
{},
|
|
402
|
+
NARROW_PT_ENGINE,
|
|
403
|
+
);
|
|
404
|
+
// Counting the 0x67 raster opcodes is the cleanest signal: 3 lines
|
|
405
|
+
// native vs 6 lines high-res.
|
|
406
|
+
const countOp = (buf: Uint8Array, op: number): number => {
|
|
407
|
+
let n = 0;
|
|
408
|
+
for (const b of buf) if (b === op) n++;
|
|
409
|
+
return n;
|
|
410
|
+
};
|
|
411
|
+
// 0x67 happens to also be present in invalidate-zero adjacency rare;
|
|
412
|
+
// we instead compare relative line counts (delta = doubled rows).
|
|
413
|
+
expect(countOp(highRes, 0x67) - countOp(noHighRes, 0x67)).toBe(3);
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
it('PT high-res doubles the feed-margin field', () => {
|
|
417
|
+
const bitmap = createBitmap(70, 3);
|
|
418
|
+
const out = encodeJobForEngine(
|
|
419
|
+
[{ bitmap, media: TZE_12MM, options: { highResolution: true } }],
|
|
420
|
+
{},
|
|
421
|
+
NARROW_PT_ENGINE,
|
|
422
|
+
);
|
|
423
|
+
let idx = -1;
|
|
424
|
+
for (let i = 0; i < out.length - 4; i++) {
|
|
425
|
+
if (out[i] === 0x1b && out[i + 1] === 0x69 && out[i + 2] === 0x64) {
|
|
426
|
+
idx = i;
|
|
427
|
+
break;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
const lo = out[idx + 3] ?? 0;
|
|
431
|
+
const hi = out[idx + 4] ?? 0;
|
|
432
|
+
expect(lo | (hi << 8)).toBe(28);
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
it('encodeJobForEngine resolves narrow vs wide TZe geometry', () => {
|
|
436
|
+
const bitmap = createBitmap(70, 1);
|
|
437
|
+
const narrow = encodeJobForEngine([{ bitmap, media: TZE_12MM }], {}, NARROW_PT_ENGINE);
|
|
438
|
+
const bitmap2 = createBitmap(150, 1);
|
|
439
|
+
const wide = encodeJobForEngine([{ bitmap: bitmap2, media: TZE_12MM }], {}, WIDE_PT_ENGINE);
|
|
440
|
+
// narrow: 128 pin head → row payload 16 bytes per raster line.
|
|
441
|
+
// wide: 560 pin head → row payload 70 bytes per raster line.
|
|
442
|
+
// Find the 0x67 raster opcode and check the LEN byte.
|
|
443
|
+
const findRowLen = (buf: Uint8Array): number => {
|
|
444
|
+
for (let i = 0; i < buf.length - 2; i++) {
|
|
445
|
+
if (buf[i] === 0x67 && buf[i + 1] === 0x00) return buf[i + 2] ?? -1;
|
|
446
|
+
}
|
|
447
|
+
return -1;
|
|
448
|
+
};
|
|
449
|
+
expect(findRowLen(narrow)).toBe(16);
|
|
450
|
+
expect(findRowLen(wide)).toBe(70);
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
it('throws when high-res requested on an engine without highResDpi', () => {
|
|
454
|
+
const bitmap = createBitmap(696, 1);
|
|
455
|
+
expect(() =>
|
|
456
|
+
encodeJobForEngine(
|
|
457
|
+
[{ bitmap, media: DK_62MM, options: { highResolution: true } }],
|
|
458
|
+
{},
|
|
459
|
+
QL_SINGLE_COLOR_ENGINE,
|
|
460
|
+
),
|
|
461
|
+
).toThrow(/high-res/);
|
|
462
|
+
});
|
|
463
|
+
|
|
464
|
+
it('PT-E550W rejects autocut with compression disabled (cutter quirk)', () => {
|
|
465
|
+
const bitmap = createBitmap(70, 3);
|
|
466
|
+
expect(() =>
|
|
467
|
+
encodeJobForEngine(
|
|
468
|
+
[{ bitmap, media: TZE_12MM, options: { autoCut: true, compress: false } }],
|
|
469
|
+
{},
|
|
470
|
+
NARROW_PT_ENGINE,
|
|
471
|
+
'PT-E550W',
|
|
472
|
+
),
|
|
473
|
+
).toThrow(/PT-E550W/);
|
|
474
|
+
});
|
|
475
|
+
|
|
476
|
+
it('PT-P750W (same head family, different name) does NOT trigger the E550W cutter quirk', () => {
|
|
477
|
+
const bitmap = createBitmap(70, 3);
|
|
478
|
+
expect(() =>
|
|
479
|
+
encodeJobForEngine(
|
|
480
|
+
[{ bitmap, media: TZE_12MM, options: { autoCut: true, compress: false } }],
|
|
481
|
+
{},
|
|
482
|
+
NARROW_PT_ENGINE,
|
|
483
|
+
'PT-P750W',
|
|
484
|
+
),
|
|
485
|
+
).not.toThrow();
|
|
486
|
+
});
|
|
487
|
+
});
|
|
488
|
+
|
|
489
|
+
describe('encodeJob legacy entry point — back compat', () => {
|
|
490
|
+
it('still produces the original 200-byte single-colour invalidate', () => {
|
|
491
|
+
const bitmap = createBitmap(696, 1);
|
|
492
|
+
const out = encodeJob([{ bitmap, media: MEDIA[259]! }], {});
|
|
493
|
+
for (let i = 4; i < 204; i++) expect(out[i]).toBe(0);
|
|
494
|
+
expect(out[204]).toBe(0x1b);
|
|
495
|
+
});
|
|
496
|
+
});
|
|
@@ -9,6 +9,7 @@ function makeStatusBytes(
|
|
|
9
9
|
mediaLengthMm: number;
|
|
10
10
|
mediaTypeByte: number;
|
|
11
11
|
statusType: number;
|
|
12
|
+
byte25: number;
|
|
12
13
|
}>,
|
|
13
14
|
): Uint8Array {
|
|
14
15
|
const bytes = new Uint8Array(32);
|
|
@@ -22,9 +23,40 @@ function makeStatusBytes(
|
|
|
22
23
|
bytes[11] = overrides?.mediaTypeByte ?? 0x0a;
|
|
23
24
|
bytes[17] = overrides?.mediaLengthMm ?? 0;
|
|
24
25
|
bytes[18] = overrides?.statusType ?? 0x00;
|
|
26
|
+
bytes[25] = overrides?.byte25 ?? 0;
|
|
25
27
|
return bytes;
|
|
26
28
|
}
|
|
27
29
|
|
|
30
|
+
/**
|
|
31
|
+
* Verbatim 32-byte captures from scripts/STATUS-CAPTURE.md. These lock
|
|
32
|
+
* in the byte-25 two-color flag against the registry — without them,
|
|
33
|
+
* the DK-22251/DK-22205 detection ambiguity could silently regress.
|
|
34
|
+
*/
|
|
35
|
+
function captureBytes(values: readonly number[]): Uint8Array {
|
|
36
|
+
if (values.length !== 32) throw new Error(`expected 32 bytes, got ${values.length.toString()}`);
|
|
37
|
+
return Uint8Array.from(values);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const CAPTURE_DK_22251 = captureBytes([
|
|
41
|
+
0x80, 0x20, 0x42, 0x34, 0x41, 0x30, 0x04, 0x00, 0x00, 0x00, 0x3e, 0x0a, 0x00, 0x00, 0x23, 0x00,
|
|
42
|
+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
43
|
+
]);
|
|
44
|
+
|
|
45
|
+
const CAPTURE_DK_22205 = captureBytes([
|
|
46
|
+
0x80, 0x20, 0x42, 0x34, 0x41, 0x30, 0x04, 0x00, 0x00, 0x00, 0x3e, 0x0a, 0x00, 0x00, 0x15, 0x00,
|
|
47
|
+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
48
|
+
]);
|
|
49
|
+
|
|
50
|
+
const CAPTURE_DK_11201 = captureBytes([
|
|
51
|
+
0x80, 0x20, 0x42, 0x34, 0x41, 0x30, 0x04, 0x00, 0x00, 0x00, 0x1d, 0x0b, 0x00, 0x00, 0x01, 0x00,
|
|
52
|
+
0x00, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
53
|
+
]);
|
|
54
|
+
|
|
55
|
+
const CAPTURE_DK_22214 = captureBytes([
|
|
56
|
+
0x80, 0x20, 0x42, 0x34, 0x41, 0x30, 0x04, 0x00, 0x00, 0x00, 0x0c, 0x0a, 0x00, 0x00, 0x1a, 0x00,
|
|
57
|
+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
58
|
+
]);
|
|
59
|
+
|
|
28
60
|
describe('parseStatus', () => {
|
|
29
61
|
it('throws on short input', () => {
|
|
30
62
|
expect(() => parseStatus(new Uint8Array(10))).toThrow('too short');
|
|
@@ -95,6 +127,61 @@ describe('parseStatus', () => {
|
|
|
95
127
|
const status = parseStatus(makeStatusBytes());
|
|
96
128
|
expect(status.editorLiteMode).toBe(false);
|
|
97
129
|
});
|
|
130
|
+
|
|
131
|
+
it('byte 25 bit 7 set → twoColorRoll=true and resolves to DK-22251', () => {
|
|
132
|
+
const status = parseStatus(
|
|
133
|
+
makeStatusBytes({ mediaWidthMm: 62, mediaTypeByte: 0x0a, byte25: 0x81 }),
|
|
134
|
+
);
|
|
135
|
+
expect(status.twoColorRoll).toBe(true);
|
|
136
|
+
expect(status.detectedMedia?.id).toBe(251);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it('byte 25 bit 7 clear → twoColorRoll=false and resolves to DK-22205', () => {
|
|
140
|
+
const status = parseStatus(
|
|
141
|
+
makeStatusBytes({ mediaWidthMm: 62, mediaTypeByte: 0x0a, byte25: 0x01 }),
|
|
142
|
+
);
|
|
143
|
+
expect(status.twoColorRoll).toBe(false);
|
|
144
|
+
expect(status.detectedMedia?.id).toBe(259);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it('twoColorRoll is omitted when no media is loaded', () => {
|
|
148
|
+
const status = parseStatus(makeStatusBytes({ mediaWidthMm: 0, mediaTypeByte: 0 }));
|
|
149
|
+
expect(status.twoColorRoll).toBeUndefined();
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
describe('parseStatus — real hardware captures', () => {
|
|
154
|
+
it('DK-22251 (62mm two-color continuous) resolves to id 251', () => {
|
|
155
|
+
const status = parseStatus(CAPTURE_DK_22251);
|
|
156
|
+
expect(status.mediaLoaded).toBe(true);
|
|
157
|
+
expect(status.detectedMedia?.id).toBe(251);
|
|
158
|
+
expect(status.twoColorRoll).toBe(true);
|
|
159
|
+
expect(status.errors).toEqual([]);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it('DK-22205 (62mm single-color continuous) resolves to id 259, NOT 251', () => {
|
|
163
|
+
const status = parseStatus(CAPTURE_DK_22205);
|
|
164
|
+
expect(status.mediaLoaded).toBe(true);
|
|
165
|
+
expect(status.detectedMedia?.id).toBe(259);
|
|
166
|
+
expect(status.twoColorRoll).toBe(false);
|
|
167
|
+
expect(status.errors).toEqual([]);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it('DK-11201 (29×90mm die-cut) resolves to id 271', () => {
|
|
171
|
+
const status = parseStatus(CAPTURE_DK_11201);
|
|
172
|
+
expect(status.mediaLoaded).toBe(true);
|
|
173
|
+
expect(status.detectedMedia?.id).toBe(271);
|
|
174
|
+
expect(status.twoColorRoll).toBe(false);
|
|
175
|
+
expect(status.errors).toEqual([]);
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
it('DK-22214 (12mm continuous) resolves to id 257', () => {
|
|
179
|
+
const status = parseStatus(CAPTURE_DK_22214);
|
|
180
|
+
expect(status.mediaLoaded).toBe(true);
|
|
181
|
+
expect(status.detectedMedia?.id).toBe(257);
|
|
182
|
+
expect(status.twoColorRoll).toBe(false);
|
|
183
|
+
expect(status.errors).toEqual([]);
|
|
184
|
+
});
|
|
98
185
|
});
|
|
99
186
|
|
|
100
187
|
describe('STATUS_REQUEST', () => {
|