@thermal-label/brother-ql-core 0.3.0 → 0.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.
Files changed (54) hide show
  1. package/README.md +1 -1
  2. package/data/devices.json +823 -0
  3. package/data/media.json +823 -0
  4. package/dist/__tests__/devices.test.js +112 -31
  5. package/dist/__tests__/devices.test.js.map +1 -1
  6. package/dist/__tests__/media.test.js +251 -1
  7. package/dist/__tests__/media.test.js.map +1 -1
  8. package/dist/__tests__/protocol.test.js +168 -1
  9. package/dist/__tests__/protocol.test.js.map +1 -1
  10. package/dist/__tests__/status.test.js +71 -0
  11. package/dist/__tests__/status.test.js.map +1 -1
  12. package/dist/devices.d.ts +13 -270
  13. package/dist/devices.d.ts.map +1 -1
  14. package/dist/devices.generated.d.ts +696 -0
  15. package/dist/devices.generated.d.ts.map +1 -0
  16. package/dist/devices.generated.js +831 -0
  17. package/dist/devices.generated.js.map +1 -0
  18. package/dist/devices.js +28 -272
  19. package/dist/devices.js.map +1 -1
  20. package/dist/index.d.ts +13 -5
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.js +10 -3
  23. package/dist/index.js.map +1 -1
  24. package/dist/media.d.ts +37 -22
  25. package/dist/media.d.ts.map +1 -1
  26. package/dist/media.generated.d.ts +4 -0
  27. package/dist/media.generated.d.ts.map +1 -0
  28. package/dist/media.generated.js +1640 -0
  29. package/dist/media.generated.js.map +1 -0
  30. package/dist/media.js +74 -281
  31. package/dist/media.js.map +1 -1
  32. package/dist/protocol.d.ts +54 -3
  33. package/dist/protocol.d.ts.map +1 -1
  34. package/dist/protocol.js +117 -18
  35. package/dist/protocol.js.map +1 -1
  36. package/dist/status.d.ts +4 -1
  37. package/dist/status.d.ts.map +1 -1
  38. package/dist/status.js +6 -2
  39. package/dist/status.js.map +1 -1
  40. package/dist/types.d.ts +92 -27
  41. package/dist/types.d.ts.map +1 -1
  42. package/package.json +13 -9
  43. package/src/__tests__/devices.test.ts +122 -32
  44. package/src/__tests__/media.test.ts +287 -1
  45. package/src/__tests__/protocol.test.ts +209 -0
  46. package/src/__tests__/status.test.ts +87 -0
  47. package/src/devices.generated.ts +840 -0
  48. package/src/devices.ts +30 -272
  49. package/src/index.ts +28 -4
  50. package/src/media.generated.ts +1644 -0
  51. package/src/media.ts +86 -282
  52. package/src/protocol.ts +196 -18
  53. package/src/status.ts +10 -3
  54. package/src/types.ts +93 -27
package/src/status.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { PrinterError } from '@thermal-label/contracts';
1
+ import type { PrintEngine, PrinterError } from '@thermal-label/contracts';
2
2
  import type { BrotherQLStatus } from './types.js';
3
3
  import { findMediaByDimensions } from './media.js';
4
4
 
@@ -37,6 +37,8 @@ const ERROR_INFO_2: { bit: number; code: string; message: string }[] = [
37
37
  * byte 11 — media type (0x0A continuous, 0x0B die-cut)
38
38
  * byte 17 — media length (mm), 0 for continuous
39
39
  * byte 18 — status type (0x02 = error response)
40
+ * byte 25 — bit 7 set when the loaded roll is two-color (DK-22251);
41
+ * clear on single-color rolls. See scripts/STATUS-CAPTURE.md.
40
42
  *
41
43
  * `detectedMedia` is resolved against the media registry via
42
44
  * `findMediaByDimensions`. `editorLiteMode` is a driver-specific
@@ -45,7 +47,10 @@ const ERROR_INFO_2: { bit: number; code: string; message: string }[] = [
45
47
  * set it from other signals (e.g. mass-storage PID detected during
46
48
  * discovery) without changing the return type.
47
49
  */
48
- export function parseStatus(bytes: Uint8Array): BrotherQLStatus {
50
+ export function parseStatus(
51
+ bytes: Uint8Array,
52
+ engine?: Pick<PrintEngine, 'headDots' | 'mediaCompatibility'>,
53
+ ): BrotherQLStatus {
49
54
  if (bytes.length < 32) {
50
55
  throw new Error(`Status response too short: ${bytes.length.toString()} bytes`);
51
56
  }
@@ -57,6 +62,7 @@ export function parseStatus(bytes: Uint8Array): BrotherQLStatus {
57
62
  const mediaTypeByte = view.getUint8(11);
58
63
  const mediaLengthMm = view.getUint8(17);
59
64
  const statusType = view.getUint8(18);
65
+ const twoColorFlag = (view.getUint8(25) & 0x80) !== 0;
60
66
 
61
67
  const errors: PrinterError[] = [];
62
68
  for (const { bit, code, message } of ERROR_INFO_1) {
@@ -68,13 +74,14 @@ export function parseStatus(bytes: Uint8Array): BrotherQLStatus {
68
74
 
69
75
  const mediaLoaded = mediaWidthMm > 0 && mediaTypeByte !== 0;
70
76
  const detected = mediaLoaded
71
- ? findMediaByDimensions(mediaWidthMm, mediaLengthMm, false)
77
+ ? findMediaByDimensions(mediaWidthMm, mediaLengthMm, twoColorFlag, engine)
72
78
  : undefined;
73
79
 
74
80
  return {
75
81
  ready: errors.length === 0 && statusType !== 0x02,
76
82
  mediaLoaded,
77
83
  ...(detected === undefined ? {} : { detectedMedia: detected }),
84
+ ...(mediaLoaded ? { twoColorRoll: twoColorFlag } : {}),
78
85
  errors,
79
86
  editorLiteMode: false,
80
87
  rawBytes: bytes,
package/src/types.ts CHANGED
@@ -1,7 +1,8 @@
1
1
  import type { LabelBitmap } from '@mbtech-nl/bitmap';
2
2
  import type {
3
- DeviceDescriptor,
3
+ DeviceEntry,
4
4
  MediaDescriptor,
5
+ PrintEngineCapabilities,
5
6
  PrintOptions,
6
7
  PrinterStatus,
7
8
  } from '@thermal-label/contracts';
@@ -9,36 +10,73 @@ import type {
9
10
  export type MediaType = 'continuous' | 'die-cut';
10
11
  export type HeadWidth = 720 | 1296;
11
12
  export type ColorMode = 'single' | 'two-color';
12
- export type NetworkSupport = 'none' | 'wifi' | 'wired' | 'wifi+wired';
13
13
 
14
14
  /**
15
- * Brother QL device descriptor.
15
+ * Tape-system discriminator on `BrotherQLMedia`. DK is the QL series'
16
+ * paper-label system; TZe is the laminated-tape system used by the
17
+ * PT-P / PT-E line; HSe 2:1 and HSe 3:1 are heat-shrink tubing systems
18
+ * supported by most P900-series and PT-E550W. Lookup paths gate on
19
+ * this so a QL printer never resolves a TZe entry, and vice versa.
20
+ */
21
+ export type TapeSystem = 'dk' | 'tze' | 'hse-2to1' | 'hse-3to1';
22
+
23
+ /**
24
+ * Per-head-family geometry on `BrotherQLMedia`.
16
25
  *
17
- * Extends the contracts base with QL-specific fields: head geometry,
18
- * protocol feature flags, and the optional mass-storage PID for Editor
19
- * Lite mode.
26
+ * Brother's PT-P / PT-E line ships two head families with different
27
+ * per-tape pin layouts. The same TZe id maps to different
28
+ * `printAreaDots` / `leftMarginPins` / `rightMarginPins` values on a
29
+ * 128-pin head (PT-E550W, PT-P750W) versus a 560-pin head (PT-P900,
30
+ * P900W, P950NW, P910BT). DK media leaves these unset and resolves via
31
+ * the flat fields on `BrotherQLMedia` directly.
32
+ */
33
+ export interface TapeGeometry {
34
+ printAreaDots: number;
35
+ leftMarginPins: number;
36
+ rightMarginPins: number;
37
+ }
38
+
39
+ /**
40
+ * Brother-specific engine capabilities.
20
41
  *
21
- * **Bluetooth on the QL-820NWB / 820NWBc**: not exposed over GATT.
22
- * Classic Bluetooth (SPP) is paired at the OS level; the kernel/driver
23
- * exposes an RFCOMM serial port, reachable via the `'serial'` transport
24
- * in Node.js and the `'web-serial'` transport in Chrome/Edge. macOS has
25
- * dropped classic Bluetooth SPP no serial route there.
42
+ * Extends the contracts-defined `PrintEngineCapabilities` (which
43
+ * carries the multi-vendor named flags `autocut` and `mediaDetection`)
44
+ * with the driver-side `twoColor` flag Brother-only today, so it
45
+ * lands here via the contracts open index signature. Promote to a
46
+ * named contracts key when a second vendor implements the same
47
+ * capability with compatible semantics.
26
48
  */
27
- export interface BrotherQLDevice extends DeviceDescriptor {
28
- family: 'brother-ql';
29
- vid: number;
30
- pid: number;
31
- headPins: HeadWidth;
32
- bytesPerRow: number;
33
- twoColor: boolean;
34
- network: NetworkSupport;
35
- autocut: boolean;
36
- compression: boolean;
37
- editorLite: boolean;
38
- /** Alternate PID seen when the printer is in Editor Lite mass-storage mode. */
39
- massStoragePid?: number;
49
+ export interface BrotherEngineCapabilities extends PrintEngineCapabilities {
50
+ /** Two-colour ribbon path — black + red plane raster encoding. */
51
+ twoColor?: boolean;
52
+ /**
53
+ * Doubled-density mode along the feed axis (`ESC i K` bit 6).
54
+ * `360` on PT-E550W / PT-P750W (native 180); `720` on the PT-P900
55
+ * family (native 360). Undefined on QL and PT models that don't
56
+ * support high-res. The encoder branches on this when
57
+ * `BrotherQLPrintOptions.highRes` is set.
58
+ */
59
+ highResDpi?: 360 | 720;
40
60
  }
41
61
 
62
+ /**
63
+ * Brother QL device entry — alias for the contracts `DeviceEntry`
64
+ * shape. Re-exported under a driver-named type so consumers don't
65
+ * have to import contracts directly. Per-device chassis-level
66
+ * capabilities (`editorLite`, `massStoragePid`) ride on the open
67
+ * index signature of `DeviceEntry.capabilities`; engine-level flags
68
+ * (`autocut`, `mediaDetection`, `twoColor`) ride on
69
+ * `engines[].capabilities`.
70
+ *
71
+ * **Bluetooth on the QL-820NWB / 820NWBc**: not exposed over GATT.
72
+ * Classic Bluetooth SPP is paired at the OS level — declared as the
73
+ * `bluetooth-spp` transport. The runtime's serial implementation
74
+ * satisfies that transport key by opening the OS-paired RFCOMM
75
+ * device path. macOS dropped classic Bluetooth SPP — no SPP route
76
+ * there.
77
+ */
78
+ export type BrotherQLDevice = DeviceEntry;
79
+
42
80
  /**
43
81
  * Brother QL media descriptor.
44
82
  *
@@ -49,9 +87,26 @@ export interface BrotherQLDevice extends DeviceDescriptor {
49
87
  export interface BrotherQLMedia extends MediaDescriptor {
50
88
  id: number;
51
89
  type: MediaType;
52
- printAreaDots: number;
53
- leftMarginPins: number;
54
- rightMarginPins: number;
90
+ /**
91
+ * Tape system this entry belongs to. Drives lookup gating in
92
+ * `findMediaByDimensions(width, height, engine)` so QL engines never
93
+ * resolve TZe / HSe entries and vice versa.
94
+ */
95
+ tapeSystem: TapeSystem;
96
+ /**
97
+ * Per-head-family geometry. `narrow` = 128-pin head (PT-E550W,
98
+ * PT-P750W); `wide` = 560-pin head (PT-P900 family). DK entries
99
+ * leave both unset and use the flat fields below; TZe / HSe entries
100
+ * leave the flat fields undefined and populate `narrow` and/or
101
+ * `wide` per the *Raster Command Reference* PDFs. `undefined` on a
102
+ * head family means "this tape doesn't fit this head" (e.g. 36 mm
103
+ * TZe and 31 mm HSe-3:1 have no `narrow` entry).
104
+ */
105
+ geometry?: { narrow?: TapeGeometry; wide?: TapeGeometry };
106
+ /** DK-only flat geometry. PT-* entries populate `geometry` instead. */
107
+ printAreaDots?: number;
108
+ leftMarginPins?: number;
109
+ rightMarginPins?: number;
55
110
  /** Die-cut masked area in dots (registration windows). */
56
111
  dieCutMaskedAreaDots?: number;
57
112
  }
@@ -63,6 +118,11 @@ export interface BrotherQLMedia extends MediaDescriptor {
63
118
  */
64
119
  export interface BrotherQLStatus extends PrinterStatus {
65
120
  editorLiteMode: boolean;
121
+ /**
122
+ * True when the loaded roll reports two-color capability via byte 25
123
+ * bit 7 of the status response. Undefined when no media is loaded.
124
+ */
125
+ twoColorRoll?: boolean;
66
126
  }
67
127
 
68
128
  export interface PageData {
@@ -94,4 +154,10 @@ export interface JobOptions {
94
154
  */
95
155
  export interface BrotherQLPrintOptions extends PrintOptions {
96
156
  rotate?: 'auto' | 0 | 90 | 180 | 270;
157
+ /**
158
+ * Opt into high-resolution mode (doubles dpi along the feed axis).
159
+ * Requires the engine's `capabilities.highResDpi` to be set; throws
160
+ * at job-build time otherwise. PT-* only — QL ignores the option.
161
+ */
162
+ highRes?: boolean;
97
163
  }