@thermal-label/brother-ql-core 0.6.0 → 0.6.2
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 +3 -3
- package/data/devices.json +250 -24
- package/data/media.json +12 -12
- package/dist/__tests__/devices.test.js +12 -20
- package/dist/__tests__/devices.test.js.map +1 -1
- package/dist/__tests__/network-status.test.d.ts +2 -0
- package/dist/__tests__/network-status.test.d.ts.map +1 -0
- package/dist/__tests__/network-status.test.js +132 -0
- package/dist/__tests__/network-status.test.js.map +1 -0
- package/dist/__tests__/protocol.test.js +103 -0
- package/dist/__tests__/protocol.test.js.map +1 -1
- package/dist/__tests__/status.test.js +13 -1
- package/dist/__tests__/status.test.js.map +1 -1
- package/dist/devices.d.ts +31 -5
- package/dist/devices.d.ts.map +1 -1
- package/dist/devices.generated.d.ts +31 -4
- package/dist/devices.generated.d.ts.map +1 -1
- package/dist/devices.generated.js +38 -4
- package/dist/devices.generated.js.map +1 -1
- package/dist/devices.js +0 -12
- package/dist/devices.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/media.generated.js +24 -24
- package/dist/media.generated.js.map +1 -1
- package/dist/network-status.d.ts +45 -0
- package/dist/network-status.d.ts.map +1 -0
- package/dist/network-status.js +140 -0
- package/dist/network-status.js.map +1 -0
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js +34 -6
- package/dist/protocol.js.map +1 -1
- package/dist/status.d.ts +5 -3
- package/dist/status.d.ts.map +1 -1
- package/dist/status.js +21 -6
- package/dist/status.js.map +1 -1
- package/dist/types.d.ts +5 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/__tests__/devices.test.ts +13 -23
- package/src/__tests__/network-status.test.ts +165 -0
- package/src/__tests__/protocol.test.ts +129 -2
- package/src/__tests__/status.test.ts +15 -1
- package/src/devices.generated.ts +38 -4
- package/src/devices.ts +0 -16
- package/src/index.ts +3 -1
- package/src/media.generated.ts +24 -24
- package/src/network-status.ts +173 -0
- package/src/protocol.ts +41 -7
- package/src/status.ts +26 -6
- package/src/types.ts +5 -1
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import type { PrintEngine, PrinterError, StatusDetail } from '@thermal-label/contracts';
|
|
2
|
+
import type { BrotherQLMedia, BrotherQLStatus } from './types.js';
|
|
3
|
+
import { findMediaByDimensions, MEDIA } from './media.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Values read from the standard Printer-MIB / Host-Resources-MIB
|
|
7
|
+
* (RFC 3805 / RFC 2790) of a network print server. Port 9100 carries
|
|
8
|
+
* no status, so this is the only status source on TCP; see plan 17.
|
|
9
|
+
*/
|
|
10
|
+
export interface PrinterMibStatus {
|
|
11
|
+
/** hrPrinterStatus: 1 other, 2 unknown, 3 idle, 4 printing, 5 warmup. */
|
|
12
|
+
printerStatus: number;
|
|
13
|
+
/** hrPrinterDetectedErrorState: 0–2 octets, bit 0 = MSB of octet 0. */
|
|
14
|
+
errorState: Uint8Array;
|
|
15
|
+
/** prtInputMediaName, e.g. `29mm x 90mm / 1.1" x 3.5"` or `62mm / 2.4"`. */
|
|
16
|
+
mediaName: string;
|
|
17
|
+
/** prtInputMediaDimFeedDir (length) / XFeedDir (width) in `dimUnit`; negative = unknown. */
|
|
18
|
+
feedDir?: number;
|
|
19
|
+
xFeedDir?: number;
|
|
20
|
+
/** prtInputDimUnit: 3 = ten-thousandths of an inch, 4 = micrometres. */
|
|
21
|
+
dimUnit?: number;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const PRINTER_STATE: Record<number, string> = {
|
|
25
|
+
1: 'other',
|
|
26
|
+
2: 'unknown',
|
|
27
|
+
3: 'idle',
|
|
28
|
+
4: 'printing',
|
|
29
|
+
5: 'warmup',
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/** RFC 2790 hrPrinterDetectedErrorState bit names, index = bit. */
|
|
33
|
+
const ERROR_STATE_BITS = [
|
|
34
|
+
'lowPaper',
|
|
35
|
+
'noPaper',
|
|
36
|
+
'lowToner',
|
|
37
|
+
'noToner',
|
|
38
|
+
'doorOpen',
|
|
39
|
+
'jammed',
|
|
40
|
+
'offline',
|
|
41
|
+
'serviceRequested',
|
|
42
|
+
'inputTrayMissing',
|
|
43
|
+
'outputTrayMissing',
|
|
44
|
+
'markerSupplyMissing',
|
|
45
|
+
'outputNearFull',
|
|
46
|
+
'outputFull',
|
|
47
|
+
'inputTrayEmpty',
|
|
48
|
+
'overduePreventMaint',
|
|
49
|
+
] as const;
|
|
50
|
+
|
|
51
|
+
/** Bit → contracts error code + message; unlisted set bits become `system_error`. */
|
|
52
|
+
const ERROR_STATE_CODES: Record<number, { code: string; message: string }> = {
|
|
53
|
+
1: { code: 'no_media', message: 'No media' },
|
|
54
|
+
4: { code: 'cover_open', message: 'Cover open' },
|
|
55
|
+
5: { code: 'cutter_jam', message: 'Jammed' },
|
|
56
|
+
6: { code: 'not_ready', message: 'Offline' },
|
|
57
|
+
7: { code: 'system_error', message: 'Service requested' },
|
|
58
|
+
13: { code: 'no_media', message: 'Input tray empty' },
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
/** Warning-only bits: reported as a details row, never an error. */
|
|
62
|
+
const ERROR_STATE_WARNINGS: Record<number, string> = {
|
|
63
|
+
0: 'Media low',
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
function errorStateBit(errorState: Uint8Array, bit: number): boolean {
|
|
67
|
+
const octet = errorState[bit >> 3];
|
|
68
|
+
return octet !== undefined && (octet & (0x80 >> (bit & 7))) !== 0;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Parse the metric half of a Brother `prtInputMediaName`.
|
|
73
|
+
* `"29mm x 90mm / …"` → 29×90 die-cut, `"62mm / …"` → 62 continuous.
|
|
74
|
+
* The `Dia` form is the reference's naming for round labels and is
|
|
75
|
+
* unverified on the wire. Anything else → `undefined`.
|
|
76
|
+
*/
|
|
77
|
+
export function parseMediaName(name: string): { widthMm: number; heightMm?: number } | undefined {
|
|
78
|
+
const metric = name.split('/')[0] ?? '';
|
|
79
|
+
const m = /^\s*(\d+(?:\.\d+)?)\s*mm(?:\s*(?:[x×]\s*(\d+(?:\.\d+)?)\s*mm|(dia)))?\s*$/i.exec(
|
|
80
|
+
metric,
|
|
81
|
+
);
|
|
82
|
+
if (!m?.[1]) return undefined;
|
|
83
|
+
const widthMm = Number(m[1]);
|
|
84
|
+
if (m[3]) return { widthMm, heightMm: widthMm };
|
|
85
|
+
return m[2] === undefined ? { widthMm } : { widthMm, heightMm: Number(m[2]) };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function dimToMm(value: number, unit: number): number | undefined {
|
|
89
|
+
if (unit === 3) return Math.round(value * 0.00254);
|
|
90
|
+
if (unit === 4) return Math.round(value / 1000);
|
|
91
|
+
return undefined;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* The name string wins; the Dim OIDs are the fallback. Bench
|
|
96
|
+
* (QL-820NWB, DK-11201): `prtInputDimUnit` = 4 (micrometres) with
|
|
97
|
+
* `2900 × 9000`, which is 1/100 mm, so trusting the unit yields a
|
|
98
|
+
* 3 × 9 mm label. The name is the only value measured to be right.
|
|
99
|
+
*/
|
|
100
|
+
function resolveDimensions(
|
|
101
|
+
input: PrinterMibStatus,
|
|
102
|
+
): { widthMm: number; heightMm: number } | undefined {
|
|
103
|
+
const parsed = parseMediaName(input.mediaName);
|
|
104
|
+
if (parsed) return { widthMm: parsed.widthMm, heightMm: parsed.heightMm ?? 0 };
|
|
105
|
+
const { xFeedDir, feedDir, dimUnit } = input;
|
|
106
|
+
if (xFeedDir === undefined || xFeedDir < 0 || dimUnit === undefined) return undefined;
|
|
107
|
+
const widthMm = dimToMm(xFeedDir, dimUnit);
|
|
108
|
+
if (widthMm === undefined) return undefined;
|
|
109
|
+
const heightMm = feedDir !== undefined && feedDir >= 0 ? dimToMm(feedDir, dimUnit) : 0;
|
|
110
|
+
return { widthMm, heightMm: heightMm ?? 0 };
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Whether a roll of this width and type also exists as a two-colour
|
|
115
|
+
* variant (62 mm continuous: DK-22205 vs DK-22251). Those are
|
|
116
|
+
* indistinguishable over the network, so callers warn.
|
|
117
|
+
*/
|
|
118
|
+
export function hasTwoColourSibling(media: BrotherQLMedia): boolean {
|
|
119
|
+
return Object.values(MEDIA).some(
|
|
120
|
+
m => m.palette !== undefined && m.type === media.type && m.widthMm === media.widthMm,
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Map Printer-MIB values onto the driver's status. `ready` requires
|
|
126
|
+
* an idle or printing state and no error bits. Two-colour rolls are
|
|
127
|
+
* invisible on every network surface (DK-22251 reads as 62 mm
|
|
128
|
+
* continuous), so the single-colour sibling is resolved and a `warn`
|
|
129
|
+
* row says so.
|
|
130
|
+
*/
|
|
131
|
+
export function statusFromPrinterMib(
|
|
132
|
+
input: PrinterMibStatus,
|
|
133
|
+
engine?: Pick<PrintEngine, 'headDots' | 'mediaCompatibility'>,
|
|
134
|
+
): BrotherQLStatus {
|
|
135
|
+
const errors: PrinterError[] = [];
|
|
136
|
+
const details: StatusDetail[] = [
|
|
137
|
+
{ label: 'Printer state', value: PRINTER_STATE[input.printerStatus] ?? 'unknown' },
|
|
138
|
+
];
|
|
139
|
+
|
|
140
|
+
for (const [bit, name] of ERROR_STATE_BITS.entries()) {
|
|
141
|
+
if (!errorStateBit(input.errorState, bit)) continue;
|
|
142
|
+
const warning = ERROR_STATE_WARNINGS[bit];
|
|
143
|
+
if (warning !== undefined) {
|
|
144
|
+
details.push({ label: 'Media', value: warning, severity: 'warn' });
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
errors.push(ERROR_STATE_CODES[bit] ?? { code: 'system_error', message: name });
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const dims = resolveDimensions(input);
|
|
151
|
+
const detected = dims
|
|
152
|
+
? findMediaByDimensions(dims.widthMm, dims.heightMm, false, engine)
|
|
153
|
+
: undefined;
|
|
154
|
+
if (detected === undefined) {
|
|
155
|
+
details.push({
|
|
156
|
+
label: 'Media',
|
|
157
|
+
value: `not recognised: ${JSON.stringify(input.mediaName)}`,
|
|
158
|
+
severity: 'warn',
|
|
159
|
+
});
|
|
160
|
+
} else if (hasTwoColourSibling(detected)) {
|
|
161
|
+
details.push({ label: 'Two-colour', value: 'not detectable over network', severity: 'warn' });
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const state = input.printerStatus;
|
|
165
|
+
return {
|
|
166
|
+
ready: (state === 3 || state === 4) && errors.length === 0,
|
|
167
|
+
mediaLoaded: detected !== undefined,
|
|
168
|
+
...(detected === undefined ? {} : { detectedMedia: detected }),
|
|
169
|
+
errors,
|
|
170
|
+
details,
|
|
171
|
+
rawBytes: new Uint8Array(0),
|
|
172
|
+
};
|
|
173
|
+
}
|
package/src/protocol.ts
CHANGED
|
@@ -249,6 +249,26 @@ function resolveEncoderGeometry(
|
|
|
249
249
|
return resolveTapeGeometry(media, engine);
|
|
250
250
|
}
|
|
251
251
|
|
|
252
|
+
/**
|
|
253
|
+
* Page length for die-cut media: `dieCutMaskedAreaDots` is the
|
|
254
|
+
* reference's "print area length" (§2.3.2(b) column 4) at 300 dpi;
|
|
255
|
+
* `doubleRows` is set when the caller supplied 600 dpi rows (QL
|
|
256
|
+
* high-res, where the encoder does not duplicate lines).
|
|
257
|
+
*/
|
|
258
|
+
function resolveDieCutRows(
|
|
259
|
+
media: BrotherQLMedia,
|
|
260
|
+
doubleRows: boolean,
|
|
261
|
+
): { rows: number; highRes: boolean } | undefined {
|
|
262
|
+
if (media.type !== 'die-cut') return undefined;
|
|
263
|
+
const area = media.dieCutMaskedAreaDots;
|
|
264
|
+
if (typeof area !== 'number') {
|
|
265
|
+
throw new Error(
|
|
266
|
+
`die-cut media ${media.id.toString()} (${media.name}) has no dieCutMaskedAreaDots; page length unknown`,
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
return { rows: doubleRows ? area * 2 : area, highRes: doubleRows };
|
|
270
|
+
}
|
|
271
|
+
|
|
252
272
|
interface EncodeContext {
|
|
253
273
|
config: RasterProtocolConfig;
|
|
254
274
|
engine?: EncoderEngine | undefined;
|
|
@@ -302,7 +322,21 @@ function encodeRasterJob(pages: PageData[], options: JobOptions, ctx: EncodeCont
|
|
|
302
322
|
// Per §7.3: PT high-res doubles the feed margin and duplicates
|
|
303
323
|
// each raster line. QL high-res leaves both untouched.
|
|
304
324
|
const baseMargin = opts.marginDots ?? config.feedMarginDots;
|
|
305
|
-
const
|
|
325
|
+
const feedMargin = config.duplicateRasterLines && highRes ? baseMargin * 2 : baseMargin;
|
|
326
|
+
|
|
327
|
+
// Die-cut pages are fixed-length (reference §2.3.3 / §2.3.4): the
|
|
328
|
+
// printer cuts at raster count + margin, not at the gap, so the page
|
|
329
|
+
// is always the print-area length with `ESC i d` 0 and the bitmap
|
|
330
|
+
// sits centred in it. Continuous pages are the bitmap's own height.
|
|
331
|
+
const dieCut = resolveDieCutRows(media, highRes && !config.duplicateRasterLines);
|
|
332
|
+
const rowCount = dieCut?.rows ?? bitmap.heightPx;
|
|
333
|
+
const marginDots = dieCut ? 0 : feedMargin;
|
|
334
|
+
if (dieCut && bitmap.heightPx > rowCount) {
|
|
335
|
+
throw new Error(
|
|
336
|
+
`Bitmap is ${bitmap.heightPx.toString()} rows but ${media.name} prints exactly ${rowCount.toString()}${dieCut.highRes ? ' (high-res)' : ''}; resize the image`,
|
|
337
|
+
);
|
|
338
|
+
}
|
|
339
|
+
const padTop = dieCut ? Math.floor((rowCount - bitmap.heightPx) / 2) : 0;
|
|
306
340
|
|
|
307
341
|
// Multi-ink media (e.g. DK-22251) requires two-color mode even for black-only jobs.
|
|
308
342
|
// Auto-create an empty red plane when the tape demands it but caller didn't supply one.
|
|
@@ -317,8 +351,6 @@ function encodeRasterJob(pages: PageData[], options: JobOptions, ctx: EncodeCont
|
|
|
317
351
|
}
|
|
318
352
|
}
|
|
319
353
|
|
|
320
|
-
const rowCount = bitmap.heightPx;
|
|
321
|
-
|
|
322
354
|
chunks.push(buildRasterMode());
|
|
323
355
|
chunks.push(buildStatusRequest());
|
|
324
356
|
chunks.push(buildPrintInfo(media, rowCount, i));
|
|
@@ -343,18 +375,20 @@ function encodeRasterJob(pages: PageData[], options: JobOptions, ctx: EncodeCont
|
|
|
343
375
|
// Per §7.3: PT high-res duplicates each raster line. QL doesn't.
|
|
344
376
|
const duplicate = config.duplicateRasterLines && highRes;
|
|
345
377
|
for (let r = 0; r < rowCount; r++) {
|
|
346
|
-
|
|
378
|
+
// Rows outside the bitmap (die-cut padding) stay blank.
|
|
379
|
+
const src = r - padTop;
|
|
380
|
+
const inBitmap = src >= 0 && src < bitmap.heightPx;
|
|
347
381
|
const blackBytes = new Uint8Array(rowByteLen);
|
|
348
|
-
placeBits(
|
|
382
|
+
if (inBitmap) placeBits(getRow(bitmap, src), bitmap.widthPx, blackBytes, leftMarginPins);
|
|
349
383
|
const blackPayload = compress ? packBits(blackBytes) : blackBytes;
|
|
350
384
|
const blackChunk = buildRasterRow(blackPayload, 'black', twoColor);
|
|
351
385
|
chunks.push(blackChunk);
|
|
352
386
|
if (duplicate) chunks.push(blackChunk);
|
|
353
387
|
|
|
354
388
|
if (twoColor && redBitmap !== undefined) {
|
|
355
|
-
const redSrc = getRow(redBitmap, r);
|
|
356
389
|
const redBytes = new Uint8Array(rowByteLen);
|
|
357
|
-
|
|
390
|
+
if (inBitmap)
|
|
391
|
+
placeBits(getRow(redBitmap, src), redBitmap.widthPx, redBytes, leftMarginPins);
|
|
358
392
|
const redPayload = compress ? packBits(redBytes) : redBytes;
|
|
359
393
|
const redChunk = buildRasterRow(redPayload, 'red', twoColor);
|
|
360
394
|
chunks.push(redChunk);
|
package/src/status.ts
CHANGED
|
@@ -31,6 +31,8 @@ const ERROR_INFO_2: { bit: number; code: string; message: string }[] = [
|
|
|
31
31
|
* Parse a Brother QL 32-byte status response.
|
|
32
32
|
*
|
|
33
33
|
* Fields:
|
|
34
|
+
* byte 3 — series code (`'4'` for QL-800/810W/820NWB)
|
|
35
|
+
* byte 4 — model code (`'8'` QL-800, `'9'` QL-810W, `'A'` QL-820NWB)
|
|
34
36
|
* byte 8 — error info 1 (bit mask, see ERROR_INFO_1)
|
|
35
37
|
* byte 9 — error info 2 (bit mask, see ERROR_INFO_2)
|
|
36
38
|
* byte 10 — media width (mm)
|
|
@@ -46,9 +48,9 @@ const ERROR_INFO_2: { bit: number; code: string; message: string }[] = [
|
|
|
46
48
|
* `findMediaByDimensions`.
|
|
47
49
|
*
|
|
48
50
|
* `details` carries the contracts-standard `StatusDetail[]` diagnostic
|
|
49
|
-
* rows the harness renders verbatim: the
|
|
50
|
-
* and the head-cooling notification (only when
|
|
51
|
-
* one).
|
|
51
|
+
* rows the harness renders verbatim: the model code and the print
|
|
52
|
+
* phase (always present) and the head-cooling notification (only when
|
|
53
|
+
* the printer reports one).
|
|
52
54
|
*/
|
|
53
55
|
export function parseStatus(
|
|
54
56
|
bytes: Uint8Array,
|
|
@@ -59,6 +61,7 @@ export function parseStatus(
|
|
|
59
61
|
}
|
|
60
62
|
|
|
61
63
|
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
64
|
+
const modelCode = formatModelCode(view.getUint8(3), view.getUint8(4));
|
|
62
65
|
const errInfo1 = view.getUint8(8);
|
|
63
66
|
const errInfo2 = view.getUint8(9);
|
|
64
67
|
const mediaWidthMm = view.getUint8(10);
|
|
@@ -87,15 +90,28 @@ export function parseStatus(
|
|
|
87
90
|
mediaLoaded,
|
|
88
91
|
...(detected === undefined ? {} : { detectedMedia: detected }),
|
|
89
92
|
errors,
|
|
90
|
-
details: buildStatusDetails(phaseType, notification),
|
|
93
|
+
details: buildStatusDetails(modelCode, phaseType, notification),
|
|
91
94
|
rawBytes: bytes,
|
|
92
95
|
};
|
|
93
96
|
}
|
|
94
97
|
|
|
98
|
+
/**
|
|
99
|
+
* Bytes 3/4 as Brother prints them in the reference (ASCII, e.g. `4A`
|
|
100
|
+
* for the QL-820NWB); hex for anything unprintable. Informational
|
|
101
|
+
* only: the USB/serial model signal a serial-port identify can use.
|
|
102
|
+
*/
|
|
103
|
+
function formatModelCode(series: number, model: number): string {
|
|
104
|
+
const printable = (b: number): boolean => b >= 0x21 && b <= 0x7e;
|
|
105
|
+
return printable(series) && printable(model)
|
|
106
|
+
? String.fromCharCode(series, model)
|
|
107
|
+
: `0x${series.toString(16).padStart(2, '0')} 0x${model.toString(16).padStart(2, '0')}`;
|
|
108
|
+
}
|
|
109
|
+
|
|
95
110
|
/**
|
|
96
111
|
* Build the contracts-standard `StatusDetail[]` rows for a parsed
|
|
97
112
|
* Brother QL status.
|
|
98
113
|
*
|
|
114
|
+
* - The model-code row (bytes 3/4) is always emitted.
|
|
99
115
|
* - The print-phase row (byte 19) is always emitted — it is the most
|
|
100
116
|
* useful "is the device doing anything" signal for a stuck report.
|
|
101
117
|
* - The head-cooling row (byte 22) is emitted only when the printer
|
|
@@ -103,8 +119,12 @@ export function parseStatus(
|
|
|
103
119
|
* normal idle status stays uncluttered. "Cooling started" is a
|
|
104
120
|
* `warn` (printing is paused), "cooling finished" is plain `info`.
|
|
105
121
|
*/
|
|
106
|
-
function buildStatusDetails(
|
|
107
|
-
|
|
122
|
+
function buildStatusDetails(
|
|
123
|
+
modelCode: string,
|
|
124
|
+
phaseType: number,
|
|
125
|
+
notification: number,
|
|
126
|
+
): StatusDetail[] {
|
|
127
|
+
const details: StatusDetail[] = [{ label: 'Model code', value: modelCode }];
|
|
108
128
|
|
|
109
129
|
details.push({
|
|
110
130
|
label: 'Print phase',
|
package/src/types.ts
CHANGED
|
@@ -107,7 +107,11 @@ export interface BrotherQLMedia extends MediaDescriptor {
|
|
|
107
107
|
printableDots?: number;
|
|
108
108
|
leftMarginPins?: number;
|
|
109
109
|
rightMarginPins?: number;
|
|
110
|
-
/**
|
|
110
|
+
/**
|
|
111
|
+
* Die-cut page length in dots at 300 dpi: the reference's "print area
|
|
112
|
+
* length" (§2.3.2(b) column 4). The encoder sends exactly this many
|
|
113
|
+
* rows for die-cut media; see DECISIONS D20.
|
|
114
|
+
*/
|
|
111
115
|
dieCutMaskedAreaDots?: number;
|
|
112
116
|
}
|
|
113
117
|
|