@thermal-label/brother-ql-core 0.0.1 → 0.2.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/dist/__tests__/colour.test.d.ts +2 -0
- package/dist/__tests__/colour.test.d.ts.map +1 -0
- package/dist/__tests__/colour.test.js +106 -0
- package/dist/__tests__/colour.test.js.map +1 -0
- package/dist/__tests__/devices.test.js +16 -2
- package/dist/__tests__/devices.test.js.map +1 -1
- package/dist/__tests__/media.test.js +17 -6
- package/dist/__tests__/media.test.js.map +1 -1
- package/dist/__tests__/preview.test.d.ts +2 -0
- package/dist/__tests__/preview.test.d.ts.map +1 -0
- package/dist/__tests__/preview.test.js +41 -0
- package/dist/__tests__/preview.test.js.map +1 -0
- package/dist/__tests__/status.test.js +28 -22
- package/dist/__tests__/status.test.js.map +1 -1
- package/dist/colour.d.ts +26 -0
- package/dist/colour.d.ts.map +1 -0
- package/dist/colour.js +84 -0
- package/dist/colour.js.map +1 -0
- package/dist/devices.d.ts +40 -21
- package/dist/devices.d.ts.map +1 -1
- package/dist/devices.js +38 -19
- package/dist/devices.js.map +1 -1
- package/dist/index.d.ts +6 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/media.d.ts +31 -4
- package/dist/media.d.ts.map +1 -1
- package/dist/media.js +73 -23
- package/dist/media.js.map +1 -1
- package/dist/preview.d.ts +13 -0
- package/dist/preview.d.ts.map +1 -0
- package/dist/preview.js +32 -0
- package/dist/preview.js.map +1 -0
- package/dist/protocol.d.ts +2 -2
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js +4 -4
- package/dist/protocol.js.map +1 -1
- package/dist/status.d.ts +20 -2
- package/dist/status.d.ts.map +1 -1
- package/dist/status.js +59 -44
- package/dist/status.js.map +1 -1
- package/dist/types.d.ts +36 -30
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +1 -0
- package/dist/types.js.map +1 -1
- package/package.json +3 -2
- package/src/__tests__/colour.test.ts +126 -0
- package/src/__tests__/devices.test.ts +18 -2
- package/src/__tests__/media.test.ts +17 -6
- package/src/__tests__/preview.test.ts +52 -0
- package/src/__tests__/status.test.ts +31 -22
- package/src/colour.ts +101 -0
- package/src/devices.ts +41 -22
- package/src/index.ts +31 -9
- package/src/media.ts +86 -27
- package/src/preview.ts +34 -0
- package/src/protocol.ts +6 -6
- package/src/status.ts +63 -47
- package/src/types.ts +38 -33
package/src/status.ts
CHANGED
|
@@ -1,67 +1,83 @@
|
|
|
1
|
-
|
|
1
|
+
/* eslint-disable import-x/consistent-type-specifier-style */
|
|
2
|
+
import type { PrinterError } from '@thermal-label/contracts';
|
|
3
|
+
import { type BrotherQLStatus } from './types.js';
|
|
4
|
+
import { findMediaByDimensions } from './media.js';
|
|
2
5
|
|
|
3
|
-
const
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
}
|
|
6
|
+
export const STATUS_REQUEST = new Uint8Array([0x1b, 0x69, 0x53]);
|
|
7
|
+
|
|
8
|
+
/** Error-info 1 bit → structured PrinterError code + human message. */
|
|
9
|
+
const ERROR_INFO_1: { bit: number; code: string; message: string }[] = [
|
|
10
|
+
{ bit: 0, code: 'no_media', message: 'No media' },
|
|
11
|
+
{ bit: 1, code: 'media_end', message: 'End of media' },
|
|
12
|
+
{ bit: 2, code: 'cutter_jam', message: 'Cutter jam' },
|
|
13
|
+
{ bit: 3, code: 'system_error', message: 'Weak battery' },
|
|
14
|
+
{ bit: 4, code: 'not_ready', message: 'Printer in use' },
|
|
15
|
+
{ bit: 6, code: 'system_error', message: 'High voltage adapter' },
|
|
16
|
+
{ bit: 7, code: 'system_error', message: 'Fan motor error' },
|
|
17
|
+
];
|
|
12
18
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}
|
|
19
|
+
/** Error-info 2 bit → structured PrinterError code + human message. */
|
|
20
|
+
const ERROR_INFO_2: { bit: number; code: string; message: string }[] = [
|
|
21
|
+
{ bit: 0, code: 'wrong_media', message: 'Replace media' },
|
|
22
|
+
{ bit: 1, code: 'system_error', message: 'Expansion buffer full' },
|
|
23
|
+
{ bit: 2, code: 'system_error', message: 'Transmission error' },
|
|
24
|
+
{ bit: 3, code: 'system_error', message: 'Communication buffer full' },
|
|
25
|
+
{ bit: 4, code: 'cover_open', message: 'Cover open' },
|
|
26
|
+
{ bit: 5, code: 'not_ready', message: 'Cancel key pressed' },
|
|
27
|
+
{ bit: 6, code: 'media_end', message: 'Media cannot be fed' },
|
|
28
|
+
{ bit: 7, code: 'system_error', message: 'System error' },
|
|
29
|
+
];
|
|
23
30
|
|
|
24
|
-
|
|
25
|
-
|
|
31
|
+
/**
|
|
32
|
+
* Parse a Brother QL 32-byte status response.
|
|
33
|
+
*
|
|
34
|
+
* Fields:
|
|
35
|
+
* byte 8 — error info 1 (bit mask, see ERROR_INFO_1)
|
|
36
|
+
* byte 9 — error info 2 (bit mask, see ERROR_INFO_2)
|
|
37
|
+
* byte 10 — media width (mm)
|
|
38
|
+
* byte 11 — media type (0x0A continuous, 0x0B die-cut)
|
|
39
|
+
* byte 17 — media length (mm), 0 for continuous
|
|
40
|
+
* byte 18 — status type (0x02 = error response)
|
|
41
|
+
*
|
|
42
|
+
* `detectedMedia` is resolved against the media registry via
|
|
43
|
+
* `findMediaByDimensions`. `editorLiteMode` is a driver-specific
|
|
44
|
+
* extension on `BrotherQLStatus` — the status-type byte doesn't
|
|
45
|
+
* actually report it, but keeping the field here means callers can
|
|
46
|
+
* set it from other signals (e.g. mass-storage PID detected during
|
|
47
|
+
* discovery) without changing the return type.
|
|
48
|
+
*/
|
|
49
|
+
export function parseStatus(bytes: Uint8Array): BrotherQLStatus {
|
|
50
|
+
if (bytes.length < 32) {
|
|
26
51
|
throw new Error(`Status response too short: ${bytes.length.toString()} bytes`);
|
|
52
|
+
}
|
|
27
53
|
|
|
28
|
-
// noUncheckedIndexedAccess forces `?? 0` fallbacks that are unreachable after
|
|
29
|
-
// the length check above. DataView avoids the issue: getUint8 returns number.
|
|
30
54
|
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
31
|
-
|
|
32
|
-
const errors: string[] = [];
|
|
33
55
|
const errInfo1 = view.getUint8(8);
|
|
34
56
|
const errInfo2 = view.getUint8(9);
|
|
35
|
-
|
|
36
|
-
for (const [bitStr, msg] of Object.entries(ERROR_INFO_1)) {
|
|
37
|
-
if (errInfo1 & (1 << Number(bitStr))) errors.push(msg);
|
|
38
|
-
}
|
|
39
|
-
for (const [bitStr, msg] of Object.entries(ERROR_INFO_2)) {
|
|
40
|
-
if (errInfo2 & (1 << Number(bitStr))) errors.push(msg);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
57
|
const mediaWidthMm = view.getUint8(10);
|
|
44
58
|
const mediaTypeByte = view.getUint8(11);
|
|
45
59
|
const mediaLengthMm = view.getUint8(17);
|
|
60
|
+
const statusType = view.getUint8(18);
|
|
46
61
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
62
|
+
const errors: PrinterError[] = [];
|
|
63
|
+
for (const { bit, code, message } of ERROR_INFO_1) {
|
|
64
|
+
if (errInfo1 & (1 << bit)) errors.push({ code, message });
|
|
65
|
+
}
|
|
66
|
+
for (const { bit, code, message } of ERROR_INFO_2) {
|
|
67
|
+
if (errInfo2 & (1 << bit)) errors.push({ code, message });
|
|
68
|
+
}
|
|
50
69
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
70
|
+
const mediaLoaded = mediaWidthMm > 0 && mediaTypeByte !== 0;
|
|
71
|
+
const detected = mediaLoaded
|
|
72
|
+
? findMediaByDimensions(mediaWidthMm, mediaLengthMm, false)
|
|
73
|
+
: undefined;
|
|
55
74
|
|
|
56
75
|
return {
|
|
57
|
-
ready,
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
mediaType,
|
|
76
|
+
ready: errors.length === 0 && statusType !== 0x02,
|
|
77
|
+
mediaLoaded,
|
|
78
|
+
...(detected === undefined ? {} : { detectedMedia: detected }),
|
|
61
79
|
errors,
|
|
62
80
|
editorLiteMode: false,
|
|
63
81
|
rawBytes: bytes,
|
|
64
82
|
};
|
|
65
83
|
}
|
|
66
|
-
|
|
67
|
-
export const STATUS_REQUEST = new Uint8Array([0x1b, 0x69, 0x53]);
|
package/src/types.ts
CHANGED
|
@@ -1,43 +1,71 @@
|
|
|
1
|
+
/* eslint-disable import-x/consistent-type-specifier-style */
|
|
1
2
|
import { type LabelBitmap } from '@mbtech-nl/bitmap';
|
|
3
|
+
import type { DeviceDescriptor, MediaDescriptor, PrinterStatus } from '@thermal-label/contracts';
|
|
2
4
|
|
|
3
5
|
export type MediaType = 'continuous' | 'die-cut';
|
|
4
6
|
export type HeadWidth = 720 | 1296;
|
|
5
7
|
export type ColorMode = 'single' | 'two-color';
|
|
6
8
|
export type NetworkSupport = 'none' | 'wifi' | 'wired' | 'wifi+wired';
|
|
7
9
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
+
/**
|
|
11
|
+
* Brother QL device descriptor.
|
|
12
|
+
*
|
|
13
|
+
* Extends the contracts base with QL-specific fields: head geometry,
|
|
14
|
+
* protocol feature flags, and the optional mass-storage PID for Editor
|
|
15
|
+
* Lite mode.
|
|
16
|
+
*
|
|
17
|
+
* **Bluetooth on the QL-820NWB / 820NWBc**: not exposed over GATT.
|
|
18
|
+
* Classic Bluetooth (SPP) is paired at the OS level; the kernel/driver
|
|
19
|
+
* exposes an RFCOMM serial port, reachable via the `'serial'` transport
|
|
20
|
+
* in Node.js and the `'web-serial'` transport in Chrome/Edge. macOS has
|
|
21
|
+
* dropped classic Bluetooth SPP — no serial route there.
|
|
22
|
+
*/
|
|
23
|
+
export interface BrotherQLDevice extends DeviceDescriptor {
|
|
24
|
+
family: 'brother-ql';
|
|
10
25
|
vid: number;
|
|
11
26
|
pid: number;
|
|
12
27
|
headPins: HeadWidth;
|
|
13
28
|
bytesPerRow: number;
|
|
14
29
|
twoColor: boolean;
|
|
15
30
|
network: NetworkSupport;
|
|
16
|
-
bluetooth: boolean;
|
|
17
31
|
autocut: boolean;
|
|
18
32
|
compression: boolean;
|
|
19
33
|
editorLite: boolean;
|
|
34
|
+
/** Alternate PID seen when the printer is in Editor Lite mass-storage mode. */
|
|
20
35
|
massStoragePid?: number;
|
|
21
36
|
}
|
|
22
37
|
|
|
23
|
-
|
|
38
|
+
/**
|
|
39
|
+
* Brother QL media descriptor.
|
|
40
|
+
*
|
|
41
|
+
* Extends `MediaDescriptor` with the dots-based geometry the raster
|
|
42
|
+
* encoder needs. `colorCapable: true` flips the driver into
|
|
43
|
+
* two-colour mode — only DK-22251 has this set in the registry.
|
|
44
|
+
*/
|
|
45
|
+
export interface BrotherQLMedia extends MediaDescriptor {
|
|
24
46
|
id: number;
|
|
25
|
-
name: string;
|
|
26
47
|
type: MediaType;
|
|
27
|
-
|
|
28
|
-
lengthMm: number;
|
|
48
|
+
colorCapable: boolean;
|
|
29
49
|
printAreaDots: number;
|
|
30
50
|
leftMarginPins: number;
|
|
31
51
|
rightMarginPins: number;
|
|
52
|
+
/** Die-cut masked area in dots (registration windows). */
|
|
32
53
|
dieCutMaskedAreaDots?: number;
|
|
33
|
-
|
|
34
|
-
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Brother QL status — contracts `PrinterStatus` plus the
|
|
58
|
+
* `editorLiteMode` flag (pre-paired QL-820NWB silently drops raster
|
|
59
|
+
* jobs when in Editor Lite mode; callers need to know).
|
|
60
|
+
*/
|
|
61
|
+
export interface BrotherQLStatus extends PrinterStatus {
|
|
62
|
+
editorLiteMode: boolean;
|
|
35
63
|
}
|
|
36
64
|
|
|
37
65
|
export interface PageData {
|
|
38
66
|
bitmap: LabelBitmap;
|
|
39
67
|
redBitmap?: LabelBitmap;
|
|
40
|
-
media:
|
|
68
|
+
media: BrotherQLMedia;
|
|
41
69
|
options?: PageOptions;
|
|
42
70
|
}
|
|
43
71
|
|
|
@@ -52,26 +80,3 @@ export interface PageOptions {
|
|
|
52
80
|
export interface JobOptions {
|
|
53
81
|
copies?: number;
|
|
54
82
|
}
|
|
55
|
-
|
|
56
|
-
export interface TextPrintOptions extends PageOptions {
|
|
57
|
-
invert?: boolean;
|
|
58
|
-
scaleX?: number;
|
|
59
|
-
scaleY?: number;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
export interface ImagePrintOptions extends PageOptions {
|
|
63
|
-
threshold?: number;
|
|
64
|
-
dither?: boolean;
|
|
65
|
-
invert?: boolean;
|
|
66
|
-
rotate?: 0 | 90 | 180 | 270;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
export interface PrinterStatus {
|
|
70
|
-
ready: boolean;
|
|
71
|
-
mediaWidthMm: number;
|
|
72
|
-
mediaLengthMm: number;
|
|
73
|
-
mediaType: MediaType | null;
|
|
74
|
-
errors: string[];
|
|
75
|
-
editorLiteMode: boolean;
|
|
76
|
-
rawBytes: Uint8Array;
|
|
77
|
-
}
|