@bobfrankston/brother-label 1.1.9 → 1.1.10
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/cli.js +91 -3
- package/cli.js.map +1 -1
- package/cli.ts +84 -3
- package/package.json +4 -1
- package/ql.d.ts +1 -1
- package/ql.d.ts.map +1 -1
- package/ql.js +6 -4
- package/ql.js.map +1 -1
- package/ql.ts +5 -3
package/cli.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
import * as fs from "fs";
|
|
7
7
|
import * as os from "os";
|
|
8
8
|
import * as path from "path";
|
|
9
|
+
import * as dns from "dns/promises";
|
|
9
10
|
import { preprocessSingleQuotes, parseArgs, } from "@bobfrankston/label-core";
|
|
10
11
|
import { print, render, renderSegments, printSegments, getConfig, setConfig, getConfigPath, listPrinters, detectTapeSize, brotherPrinter, } from "./api.js";
|
|
11
12
|
const VERSION = "1.1.0";
|
|
@@ -36,6 +37,7 @@ const BOOLEAN = [
|
|
|
36
37
|
{ names: ["-c", "-clip"], key: "clip" },
|
|
37
38
|
{ names: ["-no-wait", "-nowait"], key: "nowait" },
|
|
38
39
|
{ names: ["-mdns"], key: "mdns" },
|
|
40
|
+
{ names: ["-vb", "-verbose"], key: "verbose" },
|
|
39
41
|
{ names: ["-help", "-h", "-?"], key: "help" },
|
|
40
42
|
{ names: ["-version", "-v"], key: "version" },
|
|
41
43
|
];
|
|
@@ -76,6 +78,7 @@ Options:
|
|
|
76
78
|
(bypasses Windows queue; e.g. -ql ql720 or -ql 1.2.3.4)
|
|
77
79
|
-ql-media <id> QL media id (e.g. 271=DK-11201 29×90mm, 259=DK-22205 62mm)
|
|
78
80
|
Required when the printer doesn't respond to status
|
|
81
|
+
-vb, -verbose Print extra QL diagnostic messages (status, media source)
|
|
79
82
|
-help Show this help
|
|
80
83
|
-version Show version`);
|
|
81
84
|
}
|
|
@@ -163,12 +166,97 @@ if ($p -and $p.PrinterHostAddress) { Write-Output $p.PrinterHostAddress }
|
|
|
163
166
|
`;
|
|
164
167
|
try {
|
|
165
168
|
const out = (await runScriptOrThrow(script)).trim();
|
|
166
|
-
|
|
169
|
+
if (!out)
|
|
170
|
+
return undefined;
|
|
171
|
+
return await resolvableHost(out);
|
|
167
172
|
}
|
|
168
173
|
catch {
|
|
169
174
|
return undefined;
|
|
170
175
|
}
|
|
171
176
|
}
|
|
177
|
+
/**
|
|
178
|
+
* Brother's installer often writes a bare NetBIOS-style hostname (BRWxxxx)
|
|
179
|
+
* as the port's PrinterHostAddress, which won't resolve in environments where
|
|
180
|
+
* the DNS suffix isn't auto-appended. Resolution tiers, in order:
|
|
181
|
+
* 1. as-is via the OS resolver
|
|
182
|
+
* 2. <host>.<suffix> for every DNS suffix the OS knows about (global search
|
|
183
|
+
* list, per-connection suffix, computer domain) — covers the case where
|
|
184
|
+
* the user pings as FQDN manually because Windows isn't auto-appending
|
|
185
|
+
* 3. <host>.local via the OS resolver (Windows 10 1803+ native mDNS)
|
|
186
|
+
* 4. multicast mDNS query directly (bypasses OS resolver — works even when
|
|
187
|
+
* Windows .local resolution is disabled, common in AD/domain environments)
|
|
188
|
+
*/
|
|
189
|
+
async function resolvableHost(host) {
|
|
190
|
+
if (/^\d{1,3}(\.\d{1,3}){3}$/.test(host))
|
|
191
|
+
return host;
|
|
192
|
+
try {
|
|
193
|
+
await dns.lookup(host);
|
|
194
|
+
return host;
|
|
195
|
+
}
|
|
196
|
+
catch { }
|
|
197
|
+
if (!host.includes(".")) {
|
|
198
|
+
const suffixes = await getDnsSuffixes();
|
|
199
|
+
const { default_fqdn } = await import("@bobfrankston/miscinfo");
|
|
200
|
+
if (default_fqdn && !suffixes.includes(default_fqdn.toLowerCase())) {
|
|
201
|
+
suffixes.push(default_fqdn.toLowerCase());
|
|
202
|
+
}
|
|
203
|
+
for (const suffix of suffixes) {
|
|
204
|
+
const candidate = `${host}.${suffix}`;
|
|
205
|
+
try {
|
|
206
|
+
await dns.lookup(candidate);
|
|
207
|
+
return candidate;
|
|
208
|
+
}
|
|
209
|
+
catch { }
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
const mdnsName = host.includes(".") ? host : host + ".local";
|
|
213
|
+
try {
|
|
214
|
+
await dns.lookup(mdnsName);
|
|
215
|
+
return mdnsName;
|
|
216
|
+
}
|
|
217
|
+
catch { }
|
|
218
|
+
try {
|
|
219
|
+
const { discoverPdlPrinters } = await import("./mdns.js");
|
|
220
|
+
const printers = await discoverPdlPrinters(2500);
|
|
221
|
+
const target = host.toLowerCase();
|
|
222
|
+
const hit = printers.find(p => {
|
|
223
|
+
const h = p.hostname.toLowerCase().replace(/\.$/, "");
|
|
224
|
+
return h === target || h === target + ".local" || h.startsWith(target + ".");
|
|
225
|
+
});
|
|
226
|
+
if (hit && hit.addresses.length > 0)
|
|
227
|
+
return hit.addresses[0];
|
|
228
|
+
}
|
|
229
|
+
catch { }
|
|
230
|
+
return host;
|
|
231
|
+
}
|
|
232
|
+
let dnsSuffixCache;
|
|
233
|
+
/**
|
|
234
|
+
* Read every DNS suffix the system knows about: the global SuffixSearchList,
|
|
235
|
+
* each adapter's ConnectionSpecificSuffix, and the computer's AD/workgroup
|
|
236
|
+
* domain. Cached per-process. Returns lowercased, de-duplicated, in priority
|
|
237
|
+
* order (search list first since that's what the user explicitly configured).
|
|
238
|
+
*/
|
|
239
|
+
async function getDnsSuffixes() {
|
|
240
|
+
if (dnsSuffixCache)
|
|
241
|
+
return dnsSuffixCache;
|
|
242
|
+
const { runScriptOrThrow } = await import("@bobfrankston/label-core");
|
|
243
|
+
const script = `
|
|
244
|
+
$ErrorActionPreference = 'SilentlyContinue'
|
|
245
|
+
$out = @()
|
|
246
|
+
$out += (Get-DnsClientGlobalSetting).SuffixSearchList
|
|
247
|
+
$out += Get-DnsClient | Select-Object -ExpandProperty ConnectionSpecificSuffix
|
|
248
|
+
$out += (Get-CimInstance Win32_ComputerSystem).Domain
|
|
249
|
+
$out | Where-Object { $_ -and $_ -ne 'WORKGROUP' } | ForEach-Object { $_.ToLower() } | Select-Object -Unique
|
|
250
|
+
`;
|
|
251
|
+
try {
|
|
252
|
+
const out = await runScriptOrThrow(script);
|
|
253
|
+
dnsSuffixCache = out.split(/\r?\n/).map(s => s.trim()).filter(Boolean);
|
|
254
|
+
}
|
|
255
|
+
catch {
|
|
256
|
+
dnsSuffixCache = [];
|
|
257
|
+
}
|
|
258
|
+
return dnsSuffixCache;
|
|
259
|
+
}
|
|
172
260
|
/**
|
|
173
261
|
* Resolve a -p value to an installed Windows queue name. Accepts:
|
|
174
262
|
* #N 1-based index into the list shown by `brother-label list`
|
|
@@ -483,7 +571,7 @@ async function cmdPrint(opts, segments, inputs) {
|
|
|
483
571
|
else if (qlHost) {
|
|
484
572
|
const buffer = await renderSegments(segments, tape, opts.height, opts.space);
|
|
485
573
|
const { printPngToQL } = await import("./ql.js");
|
|
486
|
-
await printPngToQL(qlHost, buffer, qlMediaId, opts.printer);
|
|
574
|
+
await printPngToQL(qlHost, buffer, qlMediaId, opts.printer, !!opts.verbose);
|
|
487
575
|
console.log(`Printed ${segments.length} segment(s) to QL @ ${qlHost}`);
|
|
488
576
|
}
|
|
489
577
|
else {
|
|
@@ -522,7 +610,7 @@ async function cmdPrint(opts, segments, inputs) {
|
|
|
522
610
|
if (qlHost) {
|
|
523
611
|
const buffer = await render(printOpts);
|
|
524
612
|
const { printPngToQL } = await import("./ql.js");
|
|
525
|
-
await printPngToQL(qlHost, buffer, qlMediaId);
|
|
613
|
+
await printPngToQL(qlHost, buffer, qlMediaId, opts.printer, !!opts.verbose);
|
|
526
614
|
console.log(`Printed to QL @ ${qlHost}`);
|
|
527
615
|
return;
|
|
528
616
|
}
|
package/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["cli.ts"],"names":[],"mappings":"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","sourcesContent":["#!/usr/bin/env node\n/**\n * Brother Label Printer CLI\n * Thin wrapper around api.ts using shared CLI primitives from label-core.\n */\n\nimport * as fs from \"fs\";\nimport * as os from \"os\";\nimport * as path from \"path\";\nimport {\n preprocessSingleQuotes,\n parseArgs,\n} from \"@bobfrankston/label-core\";\nimport type {\n PrintOptions as CorePrintOptions,\n PrinterStatus,\n Segment,\n ValuedOptionSpec,\n BooleanFlagSpec,\n} from \"@bobfrankston/label-core\";\nimport {\n print,\n render,\n renderSegments,\n printSegments,\n getConfig,\n setConfig,\n getConfigPath,\n listPrinters,\n detectTapeSize,\n brotherPrinter,\n} from \"./api.js\";\nimport type { TapeSize, PrintOptions, SegmentOptions } from \"./api.js\";\n\nconst VERSION = \"1.1.0\";\nconst VALID_TAPES: TapeSize[] = [6, 9, 12, 18, 24];\n\nfunction parseTape(value: string): TapeSize {\n const num = parseInt(value.replace(\"mm\", \"\"), 10) as TapeSize;\n if (!VALID_TAPES.includes(num)) {\n throw new Error(`Invalid tape size: ${value}. Valid: ${VALID_TAPES.join(\", \")}`);\n }\n return num;\n}\n\nconst VALUED: ValuedOptionSpec[] = [\n { names: [\"-tape\"], key: \"tape\" },\n { names: [\"-p\", \"-printer\"], key: \"printer\" },\n { names: [\"-o\", \"-output\"], key: \"output\" },\n { names: [\"-a\", \"-aspect\"], key: \"aspect\" },\n { names: [\"-ht\", \"-height\"], key: \"height\" },\n { names: [\"-s\", \"-space\"], key: \"space\" },\n { names: [\"-timeout\"], key: \"timeout\" },\n { names: [\"-interval\"], key: \"interval\" },\n { names: [\"-ql\"], key: \"ql\" },\n { names: [\"-ql-media\"], key: \"qlMedia\" },\n];\n\nconst BOOLEAN: BooleanFlagSpec[] = [\n { names: [\"-w\", \"-html\"], key: \"html\" },\n { names: [\"-i\", \"-image\"], key: \"image\" },\n { names: [\"-t\"], key: \"text\" },\n { names: [\"-c\", \"-clip\"], key: \"clip\" },\n { names: [\"-no-wait\", \"-nowait\"], key: \"nowait\" },\n { names: [\"-mdns\"], key: \"mdns\" },\n { names: [\"-help\", \"-h\", \"-?\"], key: \"help\" },\n { names: [\"-version\", \"-v\"], key: \"version\" },\n];\n\nconst SUBCOMMANDS = [\"list\", \"status\", \"config\"];\n\nfunction showHelp(): void {\n console.log(`Brother Label Printer CLI v${VERSION}\n\nUsage:\n brother-print [options] <text> Print text\n brother-print [options] <file> Print html/txt/image file (auto-detected)\n brother-print -clip [options] Print clipboard contents (image or text)\n brother-print -text <v> -qr <v> ... Print ordered text/qr segments\n brother-print list [-mdns] List Brother printers and status\n (-mdns also discovers network printers)\n brother-print status [-p <name>] Show printer status\n brother-print config Show / set defaults\n\nTape size is auto-detected from the printer. Use -tape to override.\nSingle quotes can wrap arguments: '7\"' 'line1\\\\nline2'\n\nOptions:\n -tape <size> Tape size: 6, 9, 12, 18, 24 (mm) [auto-detected]\n -p, -printer <name> Printer queue name\n -o, -output <file> Save PNG to file instead of printing\n -a, -aspect <r> Aspect ratio width:height for HTML (e.g. 3.5:2)\n -ht, -height <size> Text height: 12mm, .5in, or 50% (of tape height)\n -s, -space <size> Space between segments: 12px, 1mm, .2in\n -t, -text Force input as literal text (-text <v> for segments)\n -qr <data> QR code segment\n -w, -html Force input as HTML file path\n -i, -image Force input as image file path\n -c, -clip Read content from clipboard (image preferred, then text)\n -no-wait Fail immediately if printer is offline (default: wait)\n -timeout <secs> Max wait time when printer is offline\n -interval <secs> Polling interval while waiting (default 2)\n -mdns With \"list\": also discover network printers via mDNS\n -ql <host> Print to a Brother QL series printer over TCP/9100\n (bypasses Windows queue; e.g. -ql ql720 or -ql 1.2.3.4)\n -ql-media <id> QL media id (e.g. 271=DK-11201 29×90mm, 259=DK-22205 62mm)\n Required when the printer doesn't respond to status\n -help Show this help\n -version Show version`);\n}\n\n/** Expected user-facing error: prints one line, no log file, no stack. */\nclass UserError extends Error {\n readonly isUserError = true;\n}\n\nasync function main(): Promise<void> {\n const argv = preprocessSingleQuotes(process.argv.slice(2));\n let parsed;\n try {\n parsed = parseArgs(argv, VALUED, BOOLEAN, SUBCOMMANDS);\n } catch (e: any) {\n console.error(`Error: ${e.message}`);\n process.exit(1);\n }\n\n if (parsed.unknown.length > 0) {\n console.error(`Unknown option(s): ${parsed.unknown.join(\", \")}`);\n console.error(`Run \"brother-print -help\" for usage.`);\n process.exit(1);\n }\n\n if (parsed.opts.help) { showHelp(); return; }\n if (parsed.opts.version) { console.log(VERSION); return; }\n\n try {\n if (typeof parsed.opts.printer === \"string\") {\n parsed.opts.printer = await resolvePrinter(parsed.opts.printer);\n /* Auto-route QL series through the QL TCP path. The XPS / CustomMediaSize\n * pipeline is P-touch-only; sending it to a QL queue produces an XPS\n * error. If the user already passed -ql explicitly, leave it alone. */\n if (!parsed.opts.ql && isQLPrinter(parsed.opts.printer)) {\n const host = await resolveQLHost(parsed.opts.printer);\n if (host) parsed.opts.ql = host;\n }\n }\n switch (parsed.command) {\n case \"list\": await cmdList(parsed.opts); return;\n case \"status\": await cmdStatus(parsed.opts); return;\n case \"config\": await cmdConfig(parsed.opts); return;\n }\n await cmdPrint(parsed.opts, parsed.segments, parsed.inputs);\n } catch (e: any) {\n if (e?.isUserError) {\n console.error(`Error: ${e.message}`);\n process.exit(1);\n }\n reportError(e);\n process.exit(1);\n }\n}\n\nfunction isQLPrinter(name: string): boolean {\n return /\\bQL-/i.test(name);\n}\n\n/**\n * Look up the TCP host for a QL printer's installed Windows queue. Pulls the\n * port name via label-core, then asks PowerShell for that port's\n * PrinterHostAddress. Returns undefined if the port isn't a TCP/IP port (e.g.\n * the QL-820NWB's BRWxxxx Bluetooth port has no host address).\n */\nasync function resolveQLHost(printerName: string): Promise<string | undefined> {\n const { listPrinters: coreListPrinters, runScriptOrThrow } =\n await import(\"@bobfrankston/label-core\");\n const all = await coreListPrinters(/brother|^pt-|^ql-/i);\n const info = all.find(p => p.name === printerName);\n if (!info?.portName) return undefined;\n const port = info.portName.replace(/'/g, \"''\");\n const script = `\n$ErrorActionPreference = 'SilentlyContinue'\n$p = Get-PrinterPort -Name '${port}'\nif ($p -and $p.PrinterHostAddress) { Write-Output $p.PrinterHostAddress }\n`;\n try {\n const out = (await runScriptOrThrow(script)).trim();\n return out || undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Resolve a -p value to an installed Windows queue name. Accepts:\n * #N 1-based index into the list shown by `brother-label list`\n * <exact> Full queue name (case-sensitive match shortcut)\n * <substr> Case-insensitive substring against name or port (e.g. \"ql820\",\n * \"172.20.1.165\", \"BRW4CD577B59B6A\"). Errors if ambiguous.\n */\nasync function resolvePrinter(value: string): Promise<string> {\n const { listPrinters: coreListPrinters } = await import(\"@bobfrankston/label-core\");\n const all = await coreListPrinters(/brother|^pt-|^ql-/i);\n const sorted = [...all].sort((a, b) => a.name.localeCompare(b.name));\n\n if (sorted.length === 0) {\n throw new UserError(`No Brother printers installed. Run 'brother-label list' to verify.`);\n }\n\n const idx = value.match(/^#(\\d+)$/);\n if (idx) {\n const n = parseInt(idx[1], 10);\n if (n < 1 || n > sorted.length) {\n throw new UserError(`-p ${value}: only ${sorted.length} printer(s) installed (use 'brother-label list').`);\n }\n return sorted[n - 1].name;\n }\n\n const exact = sorted.find(p => p.name === value);\n if (exact) return exact.name;\n\n const lower = value.toLowerCase();\n const norm = (s: string) => s.toLowerCase().replace(/[^a-z0-9]/g, \"\");\n const target = norm(value);\n const matches = sorted.filter(p => {\n const name = p.name.toLowerCase();\n const port = (p.portName || \"\").toLowerCase();\n if (name.includes(lower) || port.includes(lower)) return true;\n // Normalized fallback: \"ql820\" matches \"Brother QL-820NWB\"\n return norm(p.name).includes(target) || norm(p.portName || \"\").includes(target);\n });\n if (matches.length === 1) return matches[0].name;\n if (matches.length === 0) {\n throw new UserError(`No printer matches '${value}'. Run 'brother-label list' to see available printers.`);\n }\n const names = matches.map(p => p.name).join(\", \");\n throw new UserError(`'${value}' is ambiguous — matches: ${names}`);\n}\n\n/**\n * Print an error with full context: message, cause chain (Node's fetch and\n * other newer APIs put the actual cause in error.cause), and stack on -v.\n */\n/**\n * Report an error: print a one-line summary to stderr and write the full\n * cause-chain (with stacks, codes, paths) to a temp file. Avoids dumping\n * walls of error text into the user's terminal. Set BROTHER_PRINT_DEBUG=1\n * to also include the full chain inline.\n */\nfunction reportError(e: any): void {\n const lines: string[] = [];\n lines.push(`# brother-print error report`);\n lines.push(`# timestamp: ${new Date().toISOString()}`);\n lines.push(`# argv: ${process.argv.join(\" \")}`);\n lines.push(`# cwd: ${process.cwd()}`);\n lines.push(`# pid: ${process.pid}`);\n lines.push(\"\");\n\n let cur = e;\n let depth = 0;\n while (cur) {\n const prefix = depth === 0 ? \"Error\" : \"Caused by\";\n const name = cur.name && cur.name !== \"Error\" ? ` ${cur.name}` : \"\";\n const code = cur.code ? ` [${cur.code}]` : \"\";\n const msg = cur.message ?? String(cur);\n lines.push(`${prefix}${name}${code}: ${msg}`);\n if (cur.path) lines.push(` path: ${cur.path}`);\n if (cur.input) lines.push(` input: ${cur.input}`);\n if (cur.stack) lines.push(String(cur.stack));\n lines.push(\"\");\n cur = cur.cause;\n depth++;\n if (depth > 8) { lines.push(\"... (cause chain truncated at 8 levels)\"); break; }\n }\n\n const dump = lines.join(\"\\n\");\n const tempPath = path.join(os.tmpdir(), `brother-print-error-${Date.now()}-${process.pid}.log`);\n let wroteFile = false;\n try { fs.writeFileSync(tempPath, dump); wroteFile = true; } catch { /* fallback */ }\n\n if (wroteFile) {\n console.error(`Error: ${tempPath}`);\n } else {\n console.error(dump);\n }\n if (process.env.BROTHER_PRINT_DEBUG) {\n console.error(\"--- full report (BROTHER_PRINT_DEBUG enabled) ---\");\n console.error(dump);\n }\n}\n\nasync function cmdList(opts: Record<string, string | boolean>): Promise<void> {\n const printers = await listPrinters();\n /* Brother's listPrinters returns just { name } (legacy shape). To get\n * portName/driverName for the diagnostic line, query label-core's\n * coreListPrinters too. */\n const { listPrinters: coreListPrinters } = await import(\"@bobfrankston/label-core\");\n const all = await coreListPrinters(/brother|^pt-|^ql-/i);\n const byName = new Map(all.map(p => [p.name, p]));\n\n if (printers.length === 0) {\n console.log(\"No installed Brother printers.\");\n } else {\n const sorted = [...printers].sort((a, b) => a.name.localeCompare(b.name));\n console.log(`Brother printers (${sorted.length}):`);\n for (let i = 0; i < sorted.length; i++) {\n const p = sorted[i];\n const info = byName.get(p.name);\n let statusStr = \"?\";\n try {\n const s = await brotherPrinter.getStatus(p.name);\n statusStr = s.online ? \"online\" : `offline (${s.statusText}${s.error ? \", \" + s.error : \"\"})`;\n } catch (e: any) {\n statusStr = `status error: ${e.message}`;\n }\n const port = info ? shortenPort(info.portName) : \"(unknown)\";\n const driver = info?.driverName ?? \"(unknown)\";\n console.log(` #${i + 1} ${p.name}`);\n if (info?.comment) console.log(` description: ${info.comment}`);\n if (info?.location) console.log(` location: ${info.location}`);\n console.log(` port: ${port}`);\n if (info?.portName && info.portName !== port) {\n console.log(` full port: ${info.portName}`);\n }\n console.log(` driver: ${driver}`);\n if (info?.shareName && info.shareName !== p.name) {\n console.log(` share name: ${info.shareName}`);\n }\n console.log(` status: ${statusStr}`);\n }\n }\n\n if (opts.mdns) {\n const { discoverPdlPrinters, isBrother, modelFromTxt, matchesInstalledPort } =\n await import(\"./mdns.js\");\n console.log(\"\");\n console.log(\"Browsing mDNS for _pdl-datastream._tcp (3s)...\");\n const found = (await discoverPdlPrinters()).filter(isBrother);\n if (found.length === 0) {\n console.log(\"No Brother printers responded via mDNS.\");\n return;\n }\n const installedPorts = all.map(p => p.portName);\n const networkOnly = found.filter(m => !installedPorts.some(pn => matchesInstalledPort(m, pn)));\n const matched = found.filter(m => installedPorts.some(pn => matchesInstalledPort(m, pn)));\n\n console.log(`mDNS responders (${found.length}; ${matched.length} matched, ${networkOnly.length} network-only):`);\n let n = 1;\n for (const m of [...matched, ...networkOnly]) {\n const isNetOnly = networkOnly.includes(m);\n const tag = isNetOnly ? \" [network only]\" : \" [installed]\";\n const model = modelFromTxt(m) ?? \"(unknown model)\";\n console.log(` #${n++} ${m.instanceName}${tag}`);\n console.log(` model: ${model}`);\n console.log(` hostname: ${m.hostname.replace(/\\.$/, \"\")}`);\n if (m.addresses.length > 0) {\n console.log(` address: ${m.addresses.join(\", \")}`);\n }\n console.log(` port: ${m.port}`);\n }\n }\n}\n\n/**\n * Pull a short identifier out of a Windows printer port: \"... on hostname\"\n * → hostname; raw IP/hostname kept as-is; USB ports kept as-is.\n */\nfunction shortenPort(portName: string): string {\n if (!portName) return \"(unknown)\";\n const onMatch = portName.match(/\\bon\\s+(\\S+)\\s*$/i);\n if (onMatch) return onMatch[1];\n const hostMatch = portName.match(/\\b(\\d{1,3}(?:\\.\\d{1,3}){3}|[\\w-]+\\.local|[\\w-]+\\.[\\w.-]+)\\b/);\n if (hostMatch) return hostMatch[1];\n return portName;\n}\n\n/**\n * Show details for a single resolved printer: name, port, driver, online\n * status, and the loaded media. For QL printers (auto-detected via the QL\n * TCP path) the media is read from the Windows driver's print ticket; for\n * PT printers it's read by detectTapeSize the same way.\n */\nasync function cmdPrinterInfo(printerName: string, qlHost: string | undefined): Promise<void> {\n const { listPrinters: coreListPrinters } = await import(\"@bobfrankston/label-core\");\n const all = await coreListPrinters(/brother|^pt-|^ql-/i);\n const info = all.find(p => p.name === printerName);\n\n console.log(`Printer: ${printerName}`);\n if (info?.portName) {\n console.log(`Port: ${info.portName}`);\n }\n if (info?.driverName) {\n console.log(`Driver: ${info.driverName}`);\n }\n try {\n const s = await brotherPrinter.getStatus(printerName);\n const statusStr = s.online ? \"online\" : `offline (${s.statusText}${s.error ? \", \" + s.error : \"\"})`;\n console.log(`Status: ${statusStr}`);\n } catch (e: any) {\n console.log(`Status: error: ${e.message}`);\n }\n\n if (qlHost) {\n console.log(`QL host: ${qlHost} (TCP/9100)`);\n const { detectQLMediaFromWindows } = await import(\"./ql.js\");\n const media = await detectQLMediaFromWindows(printerName);\n if (media) {\n console.log(`Media: ${media.name} (id ${media.id}, ${media.widthMm}mm${media.heightMm ? `×${media.heightMm}mm` : \", continuous\"})`);\n console.log(` printArea ${media.printAreaDots} dots${media.dieCutMaskedAreaDots ? `, dieCut ${media.dieCutMaskedAreaDots} dots` : \"\"}`);\n } else {\n console.log(`Media: (not detected from Windows driver — pass -ql-media <id>)`);\n }\n } else {\n const tape = await detectTapeSize(printerName);\n console.log(`Loaded tape: ${tape ? tape + \"mm\" : \"(not detected)\"}`);\n }\n}\n\nasync function cmdStatus(opts: Record<string, string | boolean>): Promise<void> {\n const name = (opts.printer as string) || undefined;\n const s = await brotherPrinter.getStatus(name);\n console.log(`Printer: ${s.name}`);\n console.log(`Online: ${s.online ? \"yes\" : \"no\"}`);\n console.log(`Status: ${s.statusText}`);\n if (s.error) console.log(`Error: ${s.error}`);\n console.log(`Queue: ${s.queueLength} job(s)`);\n if (s.workOffline) console.log(`Work offline flag: true`);\n}\n\nasync function cmdConfig(opts: Record<string, string | boolean>): Promise<void> {\n const hasUpdate = !!(opts.tape || opts.printer);\n if (!hasUpdate) {\n const cfg = getConfig();\n console.log(`Configuration:`);\n console.log(` File: ${getConfigPath()}`);\n console.log(` Default tape: ${cfg.defaultTape ? cfg.defaultTape + \"mm\" : \"(not set)\"}`);\n console.log(` Default printer: ${cfg.defaultPrinter ?? \"(not set)\"}`);\n console.log(\"\\nValid tape sizes: 6, 9, 12, 18, 24\");\n return;\n }\n if (opts.tape) {\n const t = parseTape(opts.tape as string);\n setConfig({ defaultTape: t });\n console.log(`Default tape set to: ${t}mm`);\n }\n if (opts.printer) {\n setConfig({ defaultPrinter: opts.printer as string });\n console.log(`Default printer set to: ${opts.printer}`);\n }\n}\n\nasync function resolveTape(opts: Record<string, string | boolean>): Promise<TapeSize> {\n const explicit = opts.tape ? parseTape(opts.tape as string) : null;\n const detected = await detectTapeSize(opts.printer as string | undefined);\n if (explicit) {\n if (detected && detected !== explicit) {\n console.warn(`Warning: -tape ${explicit}mm specified but printer reports ${detected}mm tape loaded`);\n }\n return explicit;\n }\n if (detected) return detected;\n return getConfig().defaultTape ?? 24;\n}\n\nasync function cmdPrint(\n opts: Record<string, string | boolean>,\n segments: Segment[],\n inputs: string[]\n): Promise<void> {\n const tape = await resolveTape(opts);\n\n // If there are 2+ positional inputs, treat each as a text segment.\n // (A single positional input is handled below via classifyInput so it can\n // auto-detect file paths.)\n if (inputs.length > 1 || (inputs.length >= 1 && segments.length > 0)) {\n for (const inp of inputs) {\n segments.push({ type: \"text\", value: inp });\n }\n inputs.length = 0;\n }\n\n const baseOpts = {\n printer: opts.printer as string | undefined,\n wait: !opts.nowait,\n waitTimeoutMs: opts.timeout ? Math.round(parseFloat(opts.timeout as string) * 1000) : undefined,\n waitIntervalMs: opts.interval ? Math.round(parseFloat(opts.interval as string) * 1000) : undefined,\n onWaiting: makeWaitCallback(),\n log: (msg: string) => console.log(msg),\n };\n\n const qlHost = typeof opts.ql === \"string\" ? opts.ql : undefined;\n const qlMediaId = typeof opts.qlMedia === \"string\" ? parseInt(opts.qlMedia, 10) : undefined;\n\n // Multi-segment mode: explicit -text/-qr (>=1 segment), with positional joined in\n if (segments.length > 1 || (segments.length === 1 && inputs.length === 0)) {\n const segOpts: SegmentOptions = {\n ...baseOpts,\n tape,\n textHeight: opts.height as string | undefined,\n space: opts.space as string | undefined,\n };\n if (opts.output) {\n const buffer = await renderSegments(segments, tape, opts.height as string | undefined, opts.space as string | undefined);\n fs.writeFileSync(opts.output as string, buffer);\n console.log(`Saved to ${opts.output}`);\n } else if (qlHost) {\n const buffer = await renderSegments(segments, tape, opts.height as string | undefined, opts.space as string | undefined);\n const { printPngToQL } = await import(\"./ql.js\");\n await printPngToQL(qlHost, buffer, qlMediaId, opts.printer as string | undefined);\n console.log(`Printed ${segments.length} segment(s) to QL @ ${qlHost}`);\n } else {\n await printSegments(segments, segOpts);\n console.log(`Printed ${segments.length} segment(s) on ${tape}mm tape`);\n }\n return;\n }\n\n // Single content op\n const printOpts: PrintOptions = {\n ...baseOpts,\n tape,\n aspect: opts.aspect as string | undefined,\n textHeight: opts.height as string | undefined,\n };\n\n if (opts.clip) {\n printOpts.clip = true;\n } else if (inputs.length === 1) {\n Object.assign(printOpts, classifyInput(inputs[0], opts));\n } else if (inputs.length === 0 && segments.length === 0) {\n if (opts.printer) {\n await cmdPrinterInfo(opts.printer as string, qlHost);\n return;\n }\n showHelp();\n return;\n }\n\n if (opts.output) {\n const buffer = await render(printOpts);\n fs.writeFileSync(opts.output as string, buffer);\n console.log(`Saved to ${opts.output}`);\n return;\n }\n if (qlHost) {\n const buffer = await render(printOpts);\n const { printPngToQL } = await import(\"./ql.js\");\n await printPngToQL(qlHost, buffer, qlMediaId);\n console.log(`Printed to QL @ ${qlHost}`);\n return;\n }\n await print(printOpts);\n console.log(`Printed on ${tape}mm tape`);\n}\n\nfunction classifyInput(input: string, opts: Record<string, string | boolean>): Partial<CorePrintOptions> {\n if (opts.text) return { text: input };\n if (opts.html) return { htmlPath: path.resolve(input) };\n if (opts.image) return { imagePath: path.resolve(input) };\n\n const lower = input.toLowerCase();\n if (lower.endsWith(\".html\") || lower.endsWith(\".htm\")) return { htmlPath: path.resolve(input) };\n if (lower.endsWith(\".txt\")) return { textFile: path.resolve(input) };\n if (/\\.(png|jpg|jpeg|bmp|gif)$/i.test(lower)) return { imagePath: path.resolve(input) };\n if (fs.existsSync(input)) {\n const ext = path.extname(lower);\n if (ext === \".html\" || ext === \".htm\") return { htmlPath: path.resolve(input) };\n return { textFile: path.resolve(input) };\n }\n return { text: input };\n}\n\nfunction makeWaitCallback() {\n let lastReport = 0;\n return (status: PrinterStatus, elapsedMs: number, alternatives: PrinterStatus[]) => {\n const secs = Math.floor(elapsedMs / 1000);\n if (secs - lastReport < 5) return;\n lastReport = secs;\n const altStr = alternatives.length > 0\n ? ` (online alternatives: ${alternatives.map(a => a.name).join(\", \")})`\n : \"\";\n console.log(`[brother-print] still waiting for ${status.name} (${status.statusText}${status.error ? \", \" + status.error : \"\"}); ${secs}s elapsed${altStr}`);\n };\n}\n\nmain();\n"]}
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["cli.ts"],"names":[],"mappings":"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","sourcesContent":["#!/usr/bin/env node\n/**\n * Brother Label Printer CLI\n * Thin wrapper around api.ts using shared CLI primitives from label-core.\n */\n\nimport * as fs from \"fs\";\nimport * as os from \"os\";\nimport * as path from \"path\";\nimport * as dns from \"dns/promises\";\nimport {\n preprocessSingleQuotes,\n parseArgs,\n} from \"@bobfrankston/label-core\";\nimport type {\n PrintOptions as CorePrintOptions,\n PrinterStatus,\n Segment,\n ValuedOptionSpec,\n BooleanFlagSpec,\n} from \"@bobfrankston/label-core\";\nimport {\n print,\n render,\n renderSegments,\n printSegments,\n getConfig,\n setConfig,\n getConfigPath,\n listPrinters,\n detectTapeSize,\n brotherPrinter,\n} from \"./api.js\";\nimport type { TapeSize, PrintOptions, SegmentOptions } from \"./api.js\";\n\nconst VERSION = \"1.1.0\";\nconst VALID_TAPES: TapeSize[] = [6, 9, 12, 18, 24];\n\nfunction parseTape(value: string): TapeSize {\n const num = parseInt(value.replace(\"mm\", \"\"), 10) as TapeSize;\n if (!VALID_TAPES.includes(num)) {\n throw new Error(`Invalid tape size: ${value}. Valid: ${VALID_TAPES.join(\", \")}`);\n }\n return num;\n}\n\nconst VALUED: ValuedOptionSpec[] = [\n { names: [\"-tape\"], key: \"tape\" },\n { names: [\"-p\", \"-printer\"], key: \"printer\" },\n { names: [\"-o\", \"-output\"], key: \"output\" },\n { names: [\"-a\", \"-aspect\"], key: \"aspect\" },\n { names: [\"-ht\", \"-height\"], key: \"height\" },\n { names: [\"-s\", \"-space\"], key: \"space\" },\n { names: [\"-timeout\"], key: \"timeout\" },\n { names: [\"-interval\"], key: \"interval\" },\n { names: [\"-ql\"], key: \"ql\" },\n { names: [\"-ql-media\"], key: \"qlMedia\" },\n];\n\nconst BOOLEAN: BooleanFlagSpec[] = [\n { names: [\"-w\", \"-html\"], key: \"html\" },\n { names: [\"-i\", \"-image\"], key: \"image\" },\n { names: [\"-t\"], key: \"text\" },\n { names: [\"-c\", \"-clip\"], key: \"clip\" },\n { names: [\"-no-wait\", \"-nowait\"], key: \"nowait\" },\n { names: [\"-mdns\"], key: \"mdns\" },\n { names: [\"-vb\", \"-verbose\"], key: \"verbose\" },\n { names: [\"-help\", \"-h\", \"-?\"], key: \"help\" },\n { names: [\"-version\", \"-v\"], key: \"version\" },\n];\n\nconst SUBCOMMANDS = [\"list\", \"status\", \"config\"];\n\nfunction showHelp(): void {\n console.log(`Brother Label Printer CLI v${VERSION}\n\nUsage:\n brother-print [options] <text> Print text\n brother-print [options] <file> Print html/txt/image file (auto-detected)\n brother-print -clip [options] Print clipboard contents (image or text)\n brother-print -text <v> -qr <v> ... Print ordered text/qr segments\n brother-print list [-mdns] List Brother printers and status\n (-mdns also discovers network printers)\n brother-print status [-p <name>] Show printer status\n brother-print config Show / set defaults\n\nTape size is auto-detected from the printer. Use -tape to override.\nSingle quotes can wrap arguments: '7\"' 'line1\\\\nline2'\n\nOptions:\n -tape <size> Tape size: 6, 9, 12, 18, 24 (mm) [auto-detected]\n -p, -printer <name> Printer queue name\n -o, -output <file> Save PNG to file instead of printing\n -a, -aspect <r> Aspect ratio width:height for HTML (e.g. 3.5:2)\n -ht, -height <size> Text height: 12mm, .5in, or 50% (of tape height)\n -s, -space <size> Space between segments: 12px, 1mm, .2in\n -t, -text Force input as literal text (-text <v> for segments)\n -qr <data> QR code segment\n -w, -html Force input as HTML file path\n -i, -image Force input as image file path\n -c, -clip Read content from clipboard (image preferred, then text)\n -no-wait Fail immediately if printer is offline (default: wait)\n -timeout <secs> Max wait time when printer is offline\n -interval <secs> Polling interval while waiting (default 2)\n -mdns With \"list\": also discover network printers via mDNS\n -ql <host> Print to a Brother QL series printer over TCP/9100\n (bypasses Windows queue; e.g. -ql ql720 or -ql 1.2.3.4)\n -ql-media <id> QL media id (e.g. 271=DK-11201 29×90mm, 259=DK-22205 62mm)\n Required when the printer doesn't respond to status\n -vb, -verbose Print extra QL diagnostic messages (status, media source)\n -help Show this help\n -version Show version`);\n}\n\n/** Expected user-facing error: prints one line, no log file, no stack. */\nclass UserError extends Error {\n readonly isUserError = true;\n}\n\nasync function main(): Promise<void> {\n const argv = preprocessSingleQuotes(process.argv.slice(2));\n let parsed;\n try {\n parsed = parseArgs(argv, VALUED, BOOLEAN, SUBCOMMANDS);\n } catch (e: any) {\n console.error(`Error: ${e.message}`);\n process.exit(1);\n }\n\n if (parsed.unknown.length > 0) {\n console.error(`Unknown option(s): ${parsed.unknown.join(\", \")}`);\n console.error(`Run \"brother-print -help\" for usage.`);\n process.exit(1);\n }\n\n if (parsed.opts.help) { showHelp(); return; }\n if (parsed.opts.version) { console.log(VERSION); return; }\n\n try {\n if (typeof parsed.opts.printer === \"string\") {\n parsed.opts.printer = await resolvePrinter(parsed.opts.printer);\n /* Auto-route QL series through the QL TCP path. The XPS / CustomMediaSize\n * pipeline is P-touch-only; sending it to a QL queue produces an XPS\n * error. If the user already passed -ql explicitly, leave it alone. */\n if (!parsed.opts.ql && isQLPrinter(parsed.opts.printer)) {\n const host = await resolveQLHost(parsed.opts.printer);\n if (host) parsed.opts.ql = host;\n }\n }\n switch (parsed.command) {\n case \"list\": await cmdList(parsed.opts); return;\n case \"status\": await cmdStatus(parsed.opts); return;\n case \"config\": await cmdConfig(parsed.opts); return;\n }\n await cmdPrint(parsed.opts, parsed.segments, parsed.inputs);\n } catch (e: any) {\n if (e?.isUserError) {\n console.error(`Error: ${e.message}`);\n process.exit(1);\n }\n reportError(e);\n process.exit(1);\n }\n}\n\nfunction isQLPrinter(name: string): boolean {\n return /\\bQL-/i.test(name);\n}\n\n/**\n * Look up the TCP host for a QL printer's installed Windows queue. Pulls the\n * port name via label-core, then asks PowerShell for that port's\n * PrinterHostAddress. Returns undefined if the port isn't a TCP/IP port (e.g.\n * the QL-820NWB's BRWxxxx Bluetooth port has no host address).\n */\nasync function resolveQLHost(printerName: string): Promise<string | undefined> {\n const { listPrinters: coreListPrinters, runScriptOrThrow } =\n await import(\"@bobfrankston/label-core\");\n const all = await coreListPrinters(/brother|^pt-|^ql-/i);\n const info = all.find(p => p.name === printerName);\n if (!info?.portName) return undefined;\n const port = info.portName.replace(/'/g, \"''\");\n const script = `\n$ErrorActionPreference = 'SilentlyContinue'\n$p = Get-PrinterPort -Name '${port}'\nif ($p -and $p.PrinterHostAddress) { Write-Output $p.PrinterHostAddress }\n`;\n try {\n const out = (await runScriptOrThrow(script)).trim();\n if (!out) return undefined;\n return await resolvableHost(out);\n } catch {\n return undefined;\n }\n}\n\n/**\n * Brother's installer often writes a bare NetBIOS-style hostname (BRWxxxx)\n * as the port's PrinterHostAddress, which won't resolve in environments where\n * the DNS suffix isn't auto-appended. Resolution tiers, in order:\n * 1. as-is via the OS resolver\n * 2. <host>.<suffix> for every DNS suffix the OS knows about (global search\n * list, per-connection suffix, computer domain) — covers the case where\n * the user pings as FQDN manually because Windows isn't auto-appending\n * 3. <host>.local via the OS resolver (Windows 10 1803+ native mDNS)\n * 4. multicast mDNS query directly (bypasses OS resolver — works even when\n * Windows .local resolution is disabled, common in AD/domain environments)\n */\nasync function resolvableHost(host: string): Promise<string> {\n if (/^\\d{1,3}(\\.\\d{1,3}){3}$/.test(host)) return host;\n try {\n await dns.lookup(host);\n return host;\n } catch {}\n if (!host.includes(\".\")) {\n const suffixes = await getDnsSuffixes();\n const { default_fqdn } = await import(\"@bobfrankston/miscinfo\");\n if (default_fqdn && !suffixes.includes(default_fqdn.toLowerCase())) {\n suffixes.push(default_fqdn.toLowerCase());\n }\n for (const suffix of suffixes) {\n const candidate = `${host}.${suffix}`;\n try {\n await dns.lookup(candidate);\n return candidate;\n } catch {}\n }\n }\n const mdnsName = host.includes(\".\") ? host : host + \".local\";\n try {\n await dns.lookup(mdnsName);\n return mdnsName;\n } catch {}\n try {\n const { discoverPdlPrinters } = await import(\"./mdns.js\");\n const printers = await discoverPdlPrinters(2500);\n const target = host.toLowerCase();\n const hit = printers.find(p => {\n const h = p.hostname.toLowerCase().replace(/\\.$/, \"\");\n return h === target || h === target + \".local\" || h.startsWith(target + \".\");\n });\n if (hit && hit.addresses.length > 0) return hit.addresses[0];\n } catch {}\n return host;\n}\n\nlet dnsSuffixCache: string[] | undefined;\n/**\n * Read every DNS suffix the system knows about: the global SuffixSearchList,\n * each adapter's ConnectionSpecificSuffix, and the computer's AD/workgroup\n * domain. Cached per-process. Returns lowercased, de-duplicated, in priority\n * order (search list first since that's what the user explicitly configured).\n */\nasync function getDnsSuffixes(): Promise<string[]> {\n if (dnsSuffixCache) return dnsSuffixCache;\n const { runScriptOrThrow } = await import(\"@bobfrankston/label-core\");\n const script = `\n$ErrorActionPreference = 'SilentlyContinue'\n$out = @()\n$out += (Get-DnsClientGlobalSetting).SuffixSearchList\n$out += Get-DnsClient | Select-Object -ExpandProperty ConnectionSpecificSuffix\n$out += (Get-CimInstance Win32_ComputerSystem).Domain\n$out | Where-Object { $_ -and $_ -ne 'WORKGROUP' } | ForEach-Object { $_.ToLower() } | Select-Object -Unique\n`;\n try {\n const out = await runScriptOrThrow(script);\n dnsSuffixCache = out.split(/\\r?\\n/).map(s => s.trim()).filter(Boolean);\n } catch {\n dnsSuffixCache = [];\n }\n return dnsSuffixCache;\n}\n\n/**\n * Resolve a -p value to an installed Windows queue name. Accepts:\n * #N 1-based index into the list shown by `brother-label list`\n * <exact> Full queue name (case-sensitive match shortcut)\n * <substr> Case-insensitive substring against name or port (e.g. \"ql820\",\n * \"172.20.1.165\", \"BRW4CD577B59B6A\"). Errors if ambiguous.\n */\nasync function resolvePrinter(value: string): Promise<string> {\n const { listPrinters: coreListPrinters } = await import(\"@bobfrankston/label-core\");\n const all = await coreListPrinters(/brother|^pt-|^ql-/i);\n const sorted = [...all].sort((a, b) => a.name.localeCompare(b.name));\n\n if (sorted.length === 0) {\n throw new UserError(`No Brother printers installed. Run 'brother-label list' to verify.`);\n }\n\n const idx = value.match(/^#(\\d+)$/);\n if (idx) {\n const n = parseInt(idx[1], 10);\n if (n < 1 || n > sorted.length) {\n throw new UserError(`-p ${value}: only ${sorted.length} printer(s) installed (use 'brother-label list').`);\n }\n return sorted[n - 1].name;\n }\n\n const exact = sorted.find(p => p.name === value);\n if (exact) return exact.name;\n\n const lower = value.toLowerCase();\n const norm = (s: string) => s.toLowerCase().replace(/[^a-z0-9]/g, \"\");\n const target = norm(value);\n const matches = sorted.filter(p => {\n const name = p.name.toLowerCase();\n const port = (p.portName || \"\").toLowerCase();\n if (name.includes(lower) || port.includes(lower)) return true;\n // Normalized fallback: \"ql820\" matches \"Brother QL-820NWB\"\n return norm(p.name).includes(target) || norm(p.portName || \"\").includes(target);\n });\n if (matches.length === 1) return matches[0].name;\n if (matches.length === 0) {\n throw new UserError(`No printer matches '${value}'. Run 'brother-label list' to see available printers.`);\n }\n const names = matches.map(p => p.name).join(\", \");\n throw new UserError(`'${value}' is ambiguous — matches: ${names}`);\n}\n\n/**\n * Print an error with full context: message, cause chain (Node's fetch and\n * other newer APIs put the actual cause in error.cause), and stack on -v.\n */\n/**\n * Report an error: print a one-line summary to stderr and write the full\n * cause-chain (with stacks, codes, paths) to a temp file. Avoids dumping\n * walls of error text into the user's terminal. Set BROTHER_PRINT_DEBUG=1\n * to also include the full chain inline.\n */\nfunction reportError(e: any): void {\n const lines: string[] = [];\n lines.push(`# brother-print error report`);\n lines.push(`# timestamp: ${new Date().toISOString()}`);\n lines.push(`# argv: ${process.argv.join(\" \")}`);\n lines.push(`# cwd: ${process.cwd()}`);\n lines.push(`# pid: ${process.pid}`);\n lines.push(\"\");\n\n let cur = e;\n let depth = 0;\n while (cur) {\n const prefix = depth === 0 ? \"Error\" : \"Caused by\";\n const name = cur.name && cur.name !== \"Error\" ? ` ${cur.name}` : \"\";\n const code = cur.code ? ` [${cur.code}]` : \"\";\n const msg = cur.message ?? String(cur);\n lines.push(`${prefix}${name}${code}: ${msg}`);\n if (cur.path) lines.push(` path: ${cur.path}`);\n if (cur.input) lines.push(` input: ${cur.input}`);\n if (cur.stack) lines.push(String(cur.stack));\n lines.push(\"\");\n cur = cur.cause;\n depth++;\n if (depth > 8) { lines.push(\"... (cause chain truncated at 8 levels)\"); break; }\n }\n\n const dump = lines.join(\"\\n\");\n const tempPath = path.join(os.tmpdir(), `brother-print-error-${Date.now()}-${process.pid}.log`);\n let wroteFile = false;\n try { fs.writeFileSync(tempPath, dump); wroteFile = true; } catch { /* fallback */ }\n\n if (wroteFile) {\n console.error(`Error: ${tempPath}`);\n } else {\n console.error(dump);\n }\n if (process.env.BROTHER_PRINT_DEBUG) {\n console.error(\"--- full report (BROTHER_PRINT_DEBUG enabled) ---\");\n console.error(dump);\n }\n}\n\nasync function cmdList(opts: Record<string, string | boolean>): Promise<void> {\n const printers = await listPrinters();\n /* Brother's listPrinters returns just { name } (legacy shape). To get\n * portName/driverName for the diagnostic line, query label-core's\n * coreListPrinters too. */\n const { listPrinters: coreListPrinters } = await import(\"@bobfrankston/label-core\");\n const all = await coreListPrinters(/brother|^pt-|^ql-/i);\n const byName = new Map(all.map(p => [p.name, p]));\n\n if (printers.length === 0) {\n console.log(\"No installed Brother printers.\");\n } else {\n const sorted = [...printers].sort((a, b) => a.name.localeCompare(b.name));\n console.log(`Brother printers (${sorted.length}):`);\n for (let i = 0; i < sorted.length; i++) {\n const p = sorted[i];\n const info = byName.get(p.name);\n let statusStr = \"?\";\n try {\n const s = await brotherPrinter.getStatus(p.name);\n statusStr = s.online ? \"online\" : `offline (${s.statusText}${s.error ? \", \" + s.error : \"\"})`;\n } catch (e: any) {\n statusStr = `status error: ${e.message}`;\n }\n const port = info ? shortenPort(info.portName) : \"(unknown)\";\n const driver = info?.driverName ?? \"(unknown)\";\n console.log(` #${i + 1} ${p.name}`);\n if (info?.comment) console.log(` description: ${info.comment}`);\n if (info?.location) console.log(` location: ${info.location}`);\n console.log(` port: ${port}`);\n if (info?.portName && info.portName !== port) {\n console.log(` full port: ${info.portName}`);\n }\n console.log(` driver: ${driver}`);\n if (info?.shareName && info.shareName !== p.name) {\n console.log(` share name: ${info.shareName}`);\n }\n console.log(` status: ${statusStr}`);\n }\n }\n\n if (opts.mdns) {\n const { discoverPdlPrinters, isBrother, modelFromTxt, matchesInstalledPort } =\n await import(\"./mdns.js\");\n console.log(\"\");\n console.log(\"Browsing mDNS for _pdl-datastream._tcp (3s)...\");\n const found = (await discoverPdlPrinters()).filter(isBrother);\n if (found.length === 0) {\n console.log(\"No Brother printers responded via mDNS.\");\n return;\n }\n const installedPorts = all.map(p => p.portName);\n const networkOnly = found.filter(m => !installedPorts.some(pn => matchesInstalledPort(m, pn)));\n const matched = found.filter(m => installedPorts.some(pn => matchesInstalledPort(m, pn)));\n\n console.log(`mDNS responders (${found.length}; ${matched.length} matched, ${networkOnly.length} network-only):`);\n let n = 1;\n for (const m of [...matched, ...networkOnly]) {\n const isNetOnly = networkOnly.includes(m);\n const tag = isNetOnly ? \" [network only]\" : \" [installed]\";\n const model = modelFromTxt(m) ?? \"(unknown model)\";\n console.log(` #${n++} ${m.instanceName}${tag}`);\n console.log(` model: ${model}`);\n console.log(` hostname: ${m.hostname.replace(/\\.$/, \"\")}`);\n if (m.addresses.length > 0) {\n console.log(` address: ${m.addresses.join(\", \")}`);\n }\n console.log(` port: ${m.port}`);\n }\n }\n}\n\n/**\n * Pull a short identifier out of a Windows printer port: \"... on hostname\"\n * → hostname; raw IP/hostname kept as-is; USB ports kept as-is.\n */\nfunction shortenPort(portName: string): string {\n if (!portName) return \"(unknown)\";\n const onMatch = portName.match(/\\bon\\s+(\\S+)\\s*$/i);\n if (onMatch) return onMatch[1];\n const hostMatch = portName.match(/\\b(\\d{1,3}(?:\\.\\d{1,3}){3}|[\\w-]+\\.local|[\\w-]+\\.[\\w.-]+)\\b/);\n if (hostMatch) return hostMatch[1];\n return portName;\n}\n\n/**\n * Show details for a single resolved printer: name, port, driver, online\n * status, and the loaded media. For QL printers (auto-detected via the QL\n * TCP path) the media is read from the Windows driver's print ticket; for\n * PT printers it's read by detectTapeSize the same way.\n */\nasync function cmdPrinterInfo(printerName: string, qlHost: string | undefined): Promise<void> {\n const { listPrinters: coreListPrinters } = await import(\"@bobfrankston/label-core\");\n const all = await coreListPrinters(/brother|^pt-|^ql-/i);\n const info = all.find(p => p.name === printerName);\n\n console.log(`Printer: ${printerName}`);\n if (info?.portName) {\n console.log(`Port: ${info.portName}`);\n }\n if (info?.driverName) {\n console.log(`Driver: ${info.driverName}`);\n }\n try {\n const s = await brotherPrinter.getStatus(printerName);\n const statusStr = s.online ? \"online\" : `offline (${s.statusText}${s.error ? \", \" + s.error : \"\"})`;\n console.log(`Status: ${statusStr}`);\n } catch (e: any) {\n console.log(`Status: error: ${e.message}`);\n }\n\n if (qlHost) {\n console.log(`QL host: ${qlHost} (TCP/9100)`);\n const { detectQLMediaFromWindows } = await import(\"./ql.js\");\n const media = await detectQLMediaFromWindows(printerName);\n if (media) {\n console.log(`Media: ${media.name} (id ${media.id}, ${media.widthMm}mm${media.heightMm ? `×${media.heightMm}mm` : \", continuous\"})`);\n console.log(` printArea ${media.printAreaDots} dots${media.dieCutMaskedAreaDots ? `, dieCut ${media.dieCutMaskedAreaDots} dots` : \"\"}`);\n } else {\n console.log(`Media: (not detected from Windows driver — pass -ql-media <id>)`);\n }\n } else {\n const tape = await detectTapeSize(printerName);\n console.log(`Loaded tape: ${tape ? tape + \"mm\" : \"(not detected)\"}`);\n }\n}\n\nasync function cmdStatus(opts: Record<string, string | boolean>): Promise<void> {\n const name = (opts.printer as string) || undefined;\n const s = await brotherPrinter.getStatus(name);\n console.log(`Printer: ${s.name}`);\n console.log(`Online: ${s.online ? \"yes\" : \"no\"}`);\n console.log(`Status: ${s.statusText}`);\n if (s.error) console.log(`Error: ${s.error}`);\n console.log(`Queue: ${s.queueLength} job(s)`);\n if (s.workOffline) console.log(`Work offline flag: true`);\n}\n\nasync function cmdConfig(opts: Record<string, string | boolean>): Promise<void> {\n const hasUpdate = !!(opts.tape || opts.printer);\n if (!hasUpdate) {\n const cfg = getConfig();\n console.log(`Configuration:`);\n console.log(` File: ${getConfigPath()}`);\n console.log(` Default tape: ${cfg.defaultTape ? cfg.defaultTape + \"mm\" : \"(not set)\"}`);\n console.log(` Default printer: ${cfg.defaultPrinter ?? \"(not set)\"}`);\n console.log(\"\\nValid tape sizes: 6, 9, 12, 18, 24\");\n return;\n }\n if (opts.tape) {\n const t = parseTape(opts.tape as string);\n setConfig({ defaultTape: t });\n console.log(`Default tape set to: ${t}mm`);\n }\n if (opts.printer) {\n setConfig({ defaultPrinter: opts.printer as string });\n console.log(`Default printer set to: ${opts.printer}`);\n }\n}\n\nasync function resolveTape(opts: Record<string, string | boolean>): Promise<TapeSize> {\n const explicit = opts.tape ? parseTape(opts.tape as string) : null;\n const detected = await detectTapeSize(opts.printer as string | undefined);\n if (explicit) {\n if (detected && detected !== explicit) {\n console.warn(`Warning: -tape ${explicit}mm specified but printer reports ${detected}mm tape loaded`);\n }\n return explicit;\n }\n if (detected) return detected;\n return getConfig().defaultTape ?? 24;\n}\n\nasync function cmdPrint(\n opts: Record<string, string | boolean>,\n segments: Segment[],\n inputs: string[]\n): Promise<void> {\n const tape = await resolveTape(opts);\n\n // If there are 2+ positional inputs, treat each as a text segment.\n // (A single positional input is handled below via classifyInput so it can\n // auto-detect file paths.)\n if (inputs.length > 1 || (inputs.length >= 1 && segments.length > 0)) {\n for (const inp of inputs) {\n segments.push({ type: \"text\", value: inp });\n }\n inputs.length = 0;\n }\n\n const baseOpts = {\n printer: opts.printer as string | undefined,\n wait: !opts.nowait,\n waitTimeoutMs: opts.timeout ? Math.round(parseFloat(opts.timeout as string) * 1000) : undefined,\n waitIntervalMs: opts.interval ? Math.round(parseFloat(opts.interval as string) * 1000) : undefined,\n onWaiting: makeWaitCallback(),\n log: (msg: string) => console.log(msg),\n };\n\n const qlHost = typeof opts.ql === \"string\" ? opts.ql : undefined;\n const qlMediaId = typeof opts.qlMedia === \"string\" ? parseInt(opts.qlMedia, 10) : undefined;\n\n // Multi-segment mode: explicit -text/-qr (>=1 segment), with positional joined in\n if (segments.length > 1 || (segments.length === 1 && inputs.length === 0)) {\n const segOpts: SegmentOptions = {\n ...baseOpts,\n tape,\n textHeight: opts.height as string | undefined,\n space: opts.space as string | undefined,\n };\n if (opts.output) {\n const buffer = await renderSegments(segments, tape, opts.height as string | undefined, opts.space as string | undefined);\n fs.writeFileSync(opts.output as string, buffer);\n console.log(`Saved to ${opts.output}`);\n } else if (qlHost) {\n const buffer = await renderSegments(segments, tape, opts.height as string | undefined, opts.space as string | undefined);\n const { printPngToQL } = await import(\"./ql.js\");\n await printPngToQL(qlHost, buffer, qlMediaId, opts.printer as string | undefined, !!opts.verbose);\n console.log(`Printed ${segments.length} segment(s) to QL @ ${qlHost}`);\n } else {\n await printSegments(segments, segOpts);\n console.log(`Printed ${segments.length} segment(s) on ${tape}mm tape`);\n }\n return;\n }\n\n // Single content op\n const printOpts: PrintOptions = {\n ...baseOpts,\n tape,\n aspect: opts.aspect as string | undefined,\n textHeight: opts.height as string | undefined,\n };\n\n if (opts.clip) {\n printOpts.clip = true;\n } else if (inputs.length === 1) {\n Object.assign(printOpts, classifyInput(inputs[0], opts));\n } else if (inputs.length === 0 && segments.length === 0) {\n if (opts.printer) {\n await cmdPrinterInfo(opts.printer as string, qlHost);\n return;\n }\n showHelp();\n return;\n }\n\n if (opts.output) {\n const buffer = await render(printOpts);\n fs.writeFileSync(opts.output as string, buffer);\n console.log(`Saved to ${opts.output}`);\n return;\n }\n if (qlHost) {\n const buffer = await render(printOpts);\n const { printPngToQL } = await import(\"./ql.js\");\n await printPngToQL(qlHost, buffer, qlMediaId, opts.printer as string | undefined, !!opts.verbose);\n console.log(`Printed to QL @ ${qlHost}`);\n return;\n }\n await print(printOpts);\n console.log(`Printed on ${tape}mm tape`);\n}\n\nfunction classifyInput(input: string, opts: Record<string, string | boolean>): Partial<CorePrintOptions> {\n if (opts.text) return { text: input };\n if (opts.html) return { htmlPath: path.resolve(input) };\n if (opts.image) return { imagePath: path.resolve(input) };\n\n const lower = input.toLowerCase();\n if (lower.endsWith(\".html\") || lower.endsWith(\".htm\")) return { htmlPath: path.resolve(input) };\n if (lower.endsWith(\".txt\")) return { textFile: path.resolve(input) };\n if (/\\.(png|jpg|jpeg|bmp|gif)$/i.test(lower)) return { imagePath: path.resolve(input) };\n if (fs.existsSync(input)) {\n const ext = path.extname(lower);\n if (ext === \".html\" || ext === \".htm\") return { htmlPath: path.resolve(input) };\n return { textFile: path.resolve(input) };\n }\n return { text: input };\n}\n\nfunction makeWaitCallback() {\n let lastReport = 0;\n return (status: PrinterStatus, elapsedMs: number, alternatives: PrinterStatus[]) => {\n const secs = Math.floor(elapsedMs / 1000);\n if (secs - lastReport < 5) return;\n lastReport = secs;\n const altStr = alternatives.length > 0\n ? ` (online alternatives: ${alternatives.map(a => a.name).join(\", \")})`\n : \"\";\n console.log(`[brother-print] still waiting for ${status.name} (${status.statusText}${status.error ? \", \" + status.error : \"\"}); ${secs}s elapsed${altStr}`);\n };\n}\n\nmain();\n"]}
|
package/cli.ts
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import * as fs from "fs";
|
|
8
8
|
import * as os from "os";
|
|
9
9
|
import * as path from "path";
|
|
10
|
+
import * as dns from "dns/promises";
|
|
10
11
|
import {
|
|
11
12
|
preprocessSingleQuotes,
|
|
12
13
|
parseArgs,
|
|
@@ -63,6 +64,7 @@ const BOOLEAN: BooleanFlagSpec[] = [
|
|
|
63
64
|
{ names: ["-c", "-clip"], key: "clip" },
|
|
64
65
|
{ names: ["-no-wait", "-nowait"], key: "nowait" },
|
|
65
66
|
{ names: ["-mdns"], key: "mdns" },
|
|
67
|
+
{ names: ["-vb", "-verbose"], key: "verbose" },
|
|
66
68
|
{ names: ["-help", "-h", "-?"], key: "help" },
|
|
67
69
|
{ names: ["-version", "-v"], key: "version" },
|
|
68
70
|
];
|
|
@@ -105,6 +107,7 @@ Options:
|
|
|
105
107
|
(bypasses Windows queue; e.g. -ql ql720 or -ql 1.2.3.4)
|
|
106
108
|
-ql-media <id> QL media id (e.g. 271=DK-11201 29×90mm, 259=DK-22205 62mm)
|
|
107
109
|
Required when the printer doesn't respond to status
|
|
110
|
+
-vb, -verbose Print extra QL diagnostic messages (status, media source)
|
|
108
111
|
-help Show this help
|
|
109
112
|
-version Show version`);
|
|
110
113
|
}
|
|
@@ -184,12 +187,90 @@ if ($p -and $p.PrinterHostAddress) { Write-Output $p.PrinterHostAddress }
|
|
|
184
187
|
`;
|
|
185
188
|
try {
|
|
186
189
|
const out = (await runScriptOrThrow(script)).trim();
|
|
187
|
-
|
|
190
|
+
if (!out) return undefined;
|
|
191
|
+
return await resolvableHost(out);
|
|
188
192
|
} catch {
|
|
189
193
|
return undefined;
|
|
190
194
|
}
|
|
191
195
|
}
|
|
192
196
|
|
|
197
|
+
/**
|
|
198
|
+
* Brother's installer often writes a bare NetBIOS-style hostname (BRWxxxx)
|
|
199
|
+
* as the port's PrinterHostAddress, which won't resolve in environments where
|
|
200
|
+
* the DNS suffix isn't auto-appended. Resolution tiers, in order:
|
|
201
|
+
* 1. as-is via the OS resolver
|
|
202
|
+
* 2. <host>.<suffix> for every DNS suffix the OS knows about (global search
|
|
203
|
+
* list, per-connection suffix, computer domain) — covers the case where
|
|
204
|
+
* the user pings as FQDN manually because Windows isn't auto-appending
|
|
205
|
+
* 3. <host>.local via the OS resolver (Windows 10 1803+ native mDNS)
|
|
206
|
+
* 4. multicast mDNS query directly (bypasses OS resolver — works even when
|
|
207
|
+
* Windows .local resolution is disabled, common in AD/domain environments)
|
|
208
|
+
*/
|
|
209
|
+
async function resolvableHost(host: string): Promise<string> {
|
|
210
|
+
if (/^\d{1,3}(\.\d{1,3}){3}$/.test(host)) return host;
|
|
211
|
+
try {
|
|
212
|
+
await dns.lookup(host);
|
|
213
|
+
return host;
|
|
214
|
+
} catch {}
|
|
215
|
+
if (!host.includes(".")) {
|
|
216
|
+
const suffixes = await getDnsSuffixes();
|
|
217
|
+
const { default_fqdn } = await import("@bobfrankston/miscinfo");
|
|
218
|
+
if (default_fqdn && !suffixes.includes(default_fqdn.toLowerCase())) {
|
|
219
|
+
suffixes.push(default_fqdn.toLowerCase());
|
|
220
|
+
}
|
|
221
|
+
for (const suffix of suffixes) {
|
|
222
|
+
const candidate = `${host}.${suffix}`;
|
|
223
|
+
try {
|
|
224
|
+
await dns.lookup(candidate);
|
|
225
|
+
return candidate;
|
|
226
|
+
} catch {}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
const mdnsName = host.includes(".") ? host : host + ".local";
|
|
230
|
+
try {
|
|
231
|
+
await dns.lookup(mdnsName);
|
|
232
|
+
return mdnsName;
|
|
233
|
+
} catch {}
|
|
234
|
+
try {
|
|
235
|
+
const { discoverPdlPrinters } = await import("./mdns.js");
|
|
236
|
+
const printers = await discoverPdlPrinters(2500);
|
|
237
|
+
const target = host.toLowerCase();
|
|
238
|
+
const hit = printers.find(p => {
|
|
239
|
+
const h = p.hostname.toLowerCase().replace(/\.$/, "");
|
|
240
|
+
return h === target || h === target + ".local" || h.startsWith(target + ".");
|
|
241
|
+
});
|
|
242
|
+
if (hit && hit.addresses.length > 0) return hit.addresses[0];
|
|
243
|
+
} catch {}
|
|
244
|
+
return host;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
let dnsSuffixCache: string[] | undefined;
|
|
248
|
+
/**
|
|
249
|
+
* Read every DNS suffix the system knows about: the global SuffixSearchList,
|
|
250
|
+
* each adapter's ConnectionSpecificSuffix, and the computer's AD/workgroup
|
|
251
|
+
* domain. Cached per-process. Returns lowercased, de-duplicated, in priority
|
|
252
|
+
* order (search list first since that's what the user explicitly configured).
|
|
253
|
+
*/
|
|
254
|
+
async function getDnsSuffixes(): Promise<string[]> {
|
|
255
|
+
if (dnsSuffixCache) return dnsSuffixCache;
|
|
256
|
+
const { runScriptOrThrow } = await import("@bobfrankston/label-core");
|
|
257
|
+
const script = `
|
|
258
|
+
$ErrorActionPreference = 'SilentlyContinue'
|
|
259
|
+
$out = @()
|
|
260
|
+
$out += (Get-DnsClientGlobalSetting).SuffixSearchList
|
|
261
|
+
$out += Get-DnsClient | Select-Object -ExpandProperty ConnectionSpecificSuffix
|
|
262
|
+
$out += (Get-CimInstance Win32_ComputerSystem).Domain
|
|
263
|
+
$out | Where-Object { $_ -and $_ -ne 'WORKGROUP' } | ForEach-Object { $_.ToLower() } | Select-Object -Unique
|
|
264
|
+
`;
|
|
265
|
+
try {
|
|
266
|
+
const out = await runScriptOrThrow(script);
|
|
267
|
+
dnsSuffixCache = out.split(/\r?\n/).map(s => s.trim()).filter(Boolean);
|
|
268
|
+
} catch {
|
|
269
|
+
dnsSuffixCache = [];
|
|
270
|
+
}
|
|
271
|
+
return dnsSuffixCache;
|
|
272
|
+
}
|
|
273
|
+
|
|
193
274
|
/**
|
|
194
275
|
* Resolve a -p value to an installed Windows queue name. Accepts:
|
|
195
276
|
* #N 1-based index into the list shown by `brother-label list`
|
|
@@ -505,7 +586,7 @@ async function cmdPrint(
|
|
|
505
586
|
} else if (qlHost) {
|
|
506
587
|
const buffer = await renderSegments(segments, tape, opts.height as string | undefined, opts.space as string | undefined);
|
|
507
588
|
const { printPngToQL } = await import("./ql.js");
|
|
508
|
-
await printPngToQL(qlHost, buffer, qlMediaId, opts.printer as string | undefined);
|
|
589
|
+
await printPngToQL(qlHost, buffer, qlMediaId, opts.printer as string | undefined, !!opts.verbose);
|
|
509
590
|
console.log(`Printed ${segments.length} segment(s) to QL @ ${qlHost}`);
|
|
510
591
|
} else {
|
|
511
592
|
await printSegments(segments, segOpts);
|
|
@@ -544,7 +625,7 @@ async function cmdPrint(
|
|
|
544
625
|
if (qlHost) {
|
|
545
626
|
const buffer = await render(printOpts);
|
|
546
627
|
const { printPngToQL } = await import("./ql.js");
|
|
547
|
-
await printPngToQL(qlHost, buffer, qlMediaId);
|
|
628
|
+
await printPngToQL(qlHost, buffer, qlMediaId, opts.printer as string | undefined, !!opts.verbose);
|
|
548
629
|
console.log(`Printed to QL @ ${qlHost}`);
|
|
549
630
|
return;
|
|
550
631
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bobfrankston/brother-label",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.10",
|
|
4
4
|
"description": "API and CLI for printing labels on Brother P-touch printers",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
@@ -46,6 +46,7 @@
|
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
48
|
"@bobfrankston/label-core": "^0.1.11",
|
|
49
|
+
"@bobfrankston/miscinfo": "^1.0.10",
|
|
49
50
|
"@napi-rs/canvas": "^0.1.100",
|
|
50
51
|
"@thermal-label/brother-ql-node": "^0.3.0",
|
|
51
52
|
"multicast-dns": "^7.2.5"
|
|
@@ -59,6 +60,7 @@
|
|
|
59
60
|
},
|
|
60
61
|
".dependencies": {
|
|
61
62
|
"@bobfrankston/label-core": "file:../../../utils/label-core",
|
|
63
|
+
"@bobfrankston/miscinfo": "^1.0.10",
|
|
62
64
|
"@napi-rs/canvas": "^0.1.100",
|
|
63
65
|
"@thermal-label/brother-ql-node": "^0.3.0",
|
|
64
66
|
"multicast-dns": "^7.2.5"
|
|
@@ -66,6 +68,7 @@
|
|
|
66
68
|
".transformedSnapshot": {
|
|
67
69
|
"dependencies": {
|
|
68
70
|
"@bobfrankston/label-core": "^0.1.11",
|
|
71
|
+
"@bobfrankston/miscinfo": "^1.0.10",
|
|
69
72
|
"@napi-rs/canvas": "^0.1.100",
|
|
70
73
|
"@thermal-label/brother-ql-node": "^0.3.0",
|
|
71
74
|
"multicast-dns": "^7.2.5"
|
package/ql.d.ts
CHANGED
|
@@ -27,5 +27,5 @@ import type { BrotherQLMedia } from "@thermal-label/brother-ql-core";
|
|
|
27
27
|
* Returns undefined if PowerShell, the queue, or the dimension lookup fails.
|
|
28
28
|
*/
|
|
29
29
|
export declare function detectQLMediaFromWindows(printerName: string): Promise<BrotherQLMedia | undefined>;
|
|
30
|
-
export declare function printPngToQL(host: string, pngBuffer: Buffer, mediaId?: number, windowsPrinterName?: string): Promise<void>;
|
|
30
|
+
export declare function printPngToQL(host: string, pngBuffer: Buffer, mediaId?: number, windowsPrinterName?: string, verbose?: boolean): Promise<void>;
|
|
31
31
|
//# sourceMappingURL=ql.d.ts.map
|
package/ql.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ql.d.ts","sourceRoot":"","sources":["ql.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAIH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAYrE;;;;;;;;;;;GAWG;AACH,wBAAsB,wBAAwB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC,CAgCvG;AAiED,wBAAsB,YAAY,CAC9B,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,MAAM,EAChB,kBAAkB,CAAC,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"ql.d.ts","sourceRoot":"","sources":["ql.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAIH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAYrE;;;;;;;;;;;GAWG;AACH,wBAAsB,wBAAwB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC,CAgCvG;AAiED,wBAAsB,YAAY,CAC9B,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,MAAM,EAChB,kBAAkB,CAAC,EAAE,MAAM,EAC3B,OAAO,GAAE,OAAe,GACzB,OAAO,CAAC,IAAI,CAAC,CA8Ff"}
|
package/ql.js
CHANGED
|
@@ -127,7 +127,9 @@ function fitToMedia(src, /** @napi-rs/canvas Image */ media) {
|
|
|
127
127
|
const id = ctx.getImageData(0, 0, headDots, feedDots);
|
|
128
128
|
return { width: headDots, height: feedDots, data: new Uint8Array(id.data) };
|
|
129
129
|
}
|
|
130
|
-
export async function printPngToQL(host, pngBuffer, mediaId, windowsPrinterName) {
|
|
130
|
+
export async function printPngToQL(host, pngBuffer, mediaId, windowsPrinterName, verbose = false) {
|
|
131
|
+
const vlog = (msg) => { if (verbose)
|
|
132
|
+
console.log(msg); };
|
|
131
133
|
const img = await loadImage(pngBuffer);
|
|
132
134
|
const printer = await discovery.openPrinter({ host });
|
|
133
135
|
try {
|
|
@@ -158,12 +160,12 @@ export async function printPngToQL(host, pngBuffer, mediaId, windowsPrinterName)
|
|
|
158
160
|
media = explicit ?? status.s.detectedMedia;
|
|
159
161
|
}
|
|
160
162
|
else {
|
|
161
|
-
|
|
163
|
+
vlog(`[ql] ${host}: no status response (printer may not support it over TCP)`);
|
|
162
164
|
media = explicit;
|
|
163
165
|
if (!media && windowsPrinterName) {
|
|
164
166
|
const winMedia = await detectQLMediaFromWindows(windowsPrinterName);
|
|
165
167
|
if (winMedia) {
|
|
166
|
-
|
|
168
|
+
vlog(`[ql] detected from Windows driver: ${winMedia.name}`);
|
|
167
169
|
media = winMedia;
|
|
168
170
|
}
|
|
169
171
|
}
|
|
@@ -173,7 +175,7 @@ export async function printPngToQL(host, pngBuffer, mediaId, windowsPrinterName)
|
|
|
173
175
|
`Pass -ql-media <id> (e.g. 271 = DK-11201 29×90mm address label, ` +
|
|
174
176
|
`259 = DK-22205 62mm continuous). See MEDIA registry for full list.`);
|
|
175
177
|
}
|
|
176
|
-
|
|
178
|
+
vlog(`[ql] ${host} → ${media.name} (${media.widthMm}mm${media.heightMm ? `×${media.heightMm}mm` : ", continuous"})`);
|
|
177
179
|
/* Stretch source into the printer's target area, independently per
|
|
178
180
|
* axis. PT-pipeline output is short-and-wide (designed for narrow
|
|
179
181
|
* tape, content drives length); QL die-cut media is wide-and-short.
|
package/ql.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ql.js","sourceRoot":"","sources":["ql.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Brother QL series support via @thermal-label/brother-ql-node.\n *\n * QL printers (QL-720NW, QL-820NWB, QL-810W, etc.) use DK die-cut /\n * continuous labels — a different media namespace from P-touch TZe tape,\n * so the XPS PrintTicket / CustomMediaSize* path in api.ts doesn't apply.\n *\n * This module is a thin bridge: take a rendered PNG buffer, decode to\n * RGBA via @napi-rs/canvas, open a TCP/9100 connection to the QL printer,\n * and let the thermal-label adapter handle raster encoding, rotation, and\n * media auto-detection from the printer's status response.\n *\n * Network printing only — USB QL support exists in the upstream library\n * but is not wired here.\n */\n\nimport { discovery } from \"@thermal-label/brother-ql-node\";\nimport { MEDIA, findMediaByDimensions } from \"@thermal-label/brother-ql-core\";\nimport type { BrotherQLMedia } from \"@thermal-label/brother-ql-core\";\nimport { createCanvas, loadImage } from \"@napi-rs/canvas\";\nimport { runScriptOrThrow } from \"@bobfrankston/label-core\";\n\n/** Mark an error as user-actionable so cli.ts prints it inline instead of\n * dumping to a log file. The cli's catch block duck-types on `isUserError`. */\nfunction userError(msg: string): Error {\n const e = new Error(msg);\n (e as any).isUserError = true;\n return e;\n}\n\n/**\n * Ask the Windows print spooler what media is loaded for this QL queue, by\n * reading the queue's DefaultPrintTicket XML and matching its MediaSizeWidth /\n * MediaSizeHeight (microns) against the thermal-label MEDIA registry.\n *\n * This is the workaround for QL models (e.g. QL-720NW) that ignore the\n * TCP/9100 status request — the Windows driver cached a media size when it\n * was last configured (P-touch Editor, driver UI, or print-preview), so it's\n * the best signal available without USB.\n *\n * Returns undefined if PowerShell, the queue, or the dimension lookup fails.\n */\nexport async function detectQLMediaFromWindows(printerName: string): Promise<BrotherQLMedia | undefined> {\n const escaped = printerName.replace(/'/g, \"''\");\n const script = `\n$ErrorActionPreference = 'SilentlyContinue'\nAdd-Type -AssemblyName System.Printing | Out-Null\ntry {\n $server = New-Object System.Printing.LocalPrintServer\n $queue = $server.GetPrintQueue('${escaped}')\n $ticket = $queue.DefaultPrintTicket\n $stream = $ticket.GetXmlStream()\n $reader = New-Object System.IO.StreamReader($stream)\n Write-Output $reader.ReadToEnd()\n $reader.Close()\n} catch {}\n`;\n let xml: string;\n try {\n xml = await runScriptOrThrow(script);\n } catch {\n return undefined;\n }\n const w = xml.match(/MediaSizeWidth[^>]*>\\s*<psf:Value[^>]*>(\\d+)/i);\n const h = xml.match(/MediaSizeHeight[^>]*>\\s*<psf:Value[^>]*>(\\d+)/i);\n if (!w || !h) return undefined;\n const widthMm = Math.round(parseInt(w[1], 10) / 1000);\n const heightMm = Math.round(parseInt(h[1], 10) / 1000);\n /* PrintTicket dimensions: (widthMm, heightMm) where width is across the\n * tape and height is along the feed. findMediaByDimensions matches the\n * registry's widthMm/heightMm directly. Two-color flag false — Windows\n * doesn't tell us which palette is loaded; we'd need the printer status\n * for that. */\n return findMediaByDimensions(widthMm, heightMm, false) as BrotherQLMedia | undefined;\n}\n\n/**\n * Print a PNG buffer to a QL printer over TCP/9100.\n *\n * @param host printer hostname or IP (e.g. \"ql720\", \"172.20.1.165\")\n * @param pngBuffer PNG image data\n * @param mediaId optional firmware media id (see MEDIA registry); when\n * omitted the driver uses the media reported by the\n * printer's last getStatus() response\n */\n/**\n * Fit a source image into the printer's exact raster dimensions, in the\n * printer's natural orientation (head pins across = canvas width; feed\n * direction = canvas height — i.e. portrait). Preserves source aspect ratio\n * and rotates 90° CW when the source is landscape so the long axis aligns\n * with the feed direction.\n *\n * Uses `printAreaDots` × `dieCutMaskedAreaDots` from the BrotherQLMedia\n * descriptor — these are the printer's real dot counts, not mm-derived\n * estimates (e.g. DK-11201 = 306 × 991, not 343 × 1063 from naïve 300 DPI).\n * Sending a row count that disagrees with the die-cut length causes the\n * print to spill across label boundaries.\n *\n * White-fills the canvas before drawing so transparent source pixels become\n * white (otherwise dither converts alpha=0 to black).\n */\nfunction fitToMedia(\n src: any, /** @napi-rs/canvas Image */\n media: BrotherQLMedia,\n): { width: number; height: number; data: Uint8Array } {\n const headDots = media.printAreaDots;\n const feedDots = media.heightMm\n ? (media.dieCutMaskedAreaDots ?? Math.round(headDots * media.heightMm / media.widthMm))\n : Math.round(headDots * (Math.max(src.width, src.height) / Math.min(src.width, src.height)));\n\n /* Source long-axis maps to feed; short-axis maps to head. */\n const srcLong = Math.max(src.width, src.height);\n const srcShort = Math.min(src.width, src.height);\n const scale = Math.min(headDots / srcShort, feedDots / srcLong);\n const drawShort = Math.round(srcShort * scale); /** across head */\n const drawLong = Math.round(srcLong * scale); /** along feed */\n const offX = Math.floor((headDots - drawShort) / 2);\n const offY = Math.floor((feedDots - drawLong) / 2);\n\n const canvas = createCanvas(headDots, feedDots);\n const ctx = canvas.getContext(\"2d\");\n ctx.fillStyle = \"white\";\n ctx.fillRect(0, 0, headDots, feedDots);\n\n if (src.width >= src.height) {\n /* Landscape source → rotate 90° CW so its long axis runs down the feed. */\n ctx.save();\n ctx.translate(offX + drawShort, offY);\n ctx.rotate(Math.PI / 2);\n ctx.drawImage(src, 0, 0, drawLong, drawShort);\n ctx.restore();\n } else {\n ctx.drawImage(src, offX, offY, drawShort, drawLong);\n }\n\n const id = ctx.getImageData(0, 0, headDots, feedDots);\n return { width: headDots, height: feedDots, data: new Uint8Array(id.data) };\n}\n\nexport async function printPngToQL(\n host: string,\n pngBuffer: Buffer,\n mediaId?: number,\n windowsPrinterName?: string,\n): Promise<void> {\n const img = await loadImage(pngBuffer);\n\n const printer = await discovery.openPrinter({ host });\n try {\n /* getStatus() polls a 32-byte response with no timeout in the lib;\n * older QL models (e.g. QL-720NW) silently ignore the request over\n * TCP/9100, hanging forever. Race against a wall-clock timeout so\n * we can fall back to explicit media when status isn't available. */\n let explicit: BrotherQLMedia | undefined;\n if (mediaId !== undefined) {\n explicit = MEDIA[mediaId];\n if (!explicit) {\n const ids = Object.keys(MEDIA).join(\", \");\n throw userError(\n `Unknown -ql-media ${mediaId}. The id is the FIRMWARE id, ` +\n `not the DK product code (DK-11201 → 271, DK-22205 → 259). ` +\n `Known ids: ${ids}`\n );\n }\n }\n const status = await Promise.race([\n printer.getStatus().then(s => ({ ok: true as const, s })),\n new Promise<{ ok: false }>(r => setTimeout(() => r({ ok: false }), 2000)),\n ]);\n\n let media: BrotherQLMedia | undefined;\n if (status.ok) {\n if (status.s.errors.length > 0) {\n const msgs = status.s.errors.map(e => e.message).join(\"; \");\n throw userError(`QL @ ${host} not ready: ${msgs}`);\n }\n media = explicit ?? (status.s.detectedMedia as BrotherQLMedia | undefined);\n } else {\n console.log(`[ql] ${host}: no status response (printer may not support it over TCP)`);\n media = explicit;\n if (!media && windowsPrinterName) {\n const winMedia = await detectQLMediaFromWindows(windowsPrinterName);\n if (winMedia) {\n console.log(`[ql] detected from Windows driver: ${winMedia.name}`);\n media = winMedia;\n }\n }\n }\n\n if (!media) {\n throw userError(\n `QL @ ${host}: media not detected and none specified. ` +\n `Pass -ql-media <id> (e.g. 271 = DK-11201 29×90mm address label, ` +\n `259 = DK-22205 62mm continuous). See MEDIA registry for full list.`\n );\n }\n console.log(`[ql] ${host} → ${media.name} (${media.widthMm}mm${media.heightMm ? `×${media.heightMm}mm` : \", continuous\"})`);\n /* Stretch source into the printer's target area, independently per\n * axis. PT-pipeline output is short-and-wide (designed for narrow\n * tape, content drives length); QL die-cut media is wide-and-short.\n * Proportional scaling shrinks the height to keep aspect, leaving\n * blank stripes above/below; independent stretch fills the label.\n * Source orientation is preserved (lib handles rotation).\n *\n * Long axis: dieCutMaskedAreaDots minus a safety pad to absorb the\n * residual feed-direction overrun observed empirically (~10% with\n * default marginDots=35). For continuous media there's no upper\n * bound so we keep proportional scaling there. */\n const isLandscape = img.width >= img.height;\n const srcShort = Math.min(img.width, img.height);\n const targetShort = media.printAreaDots;\n const targetLong = media.dieCutMaskedAreaDots\n ? Math.round(media.dieCutMaskedAreaDots * 0.92)\n : null;\n let w: number;\n let h: number;\n if (targetLong) {\n w = isLandscape ? targetLong : targetShort;\n h = isLandscape ? targetShort : targetLong;\n } else {\n const factor = targetShort / srcShort;\n w = Math.max(1, Math.round(img.width * factor));\n h = Math.max(1, Math.round(img.height * factor));\n }\n const canvas = createCanvas(w, h);\n const ctx = canvas.getContext(\"2d\");\n ctx.fillStyle = \"white\";\n ctx.fillRect(0, 0, w, h);\n ctx.drawImage(img, 0, 0, w, h);\n const id = ctx.getImageData(0, 0, w, h);\n await printer.print(\n { width: w, height: h, data: new Uint8Array(id.data) },\n media,\n );\n } finally {\n await printer.close();\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"ql.js","sourceRoot":"","sources":["ql.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Brother QL series support via @thermal-label/brother-ql-node.\n *\n * QL printers (QL-720NW, QL-820NWB, QL-810W, etc.) use DK die-cut /\n * continuous labels — a different media namespace from P-touch TZe tape,\n * so the XPS PrintTicket / CustomMediaSize* path in api.ts doesn't apply.\n *\n * This module is a thin bridge: take a rendered PNG buffer, decode to\n * RGBA via @napi-rs/canvas, open a TCP/9100 connection to the QL printer,\n * and let the thermal-label adapter handle raster encoding, rotation, and\n * media auto-detection from the printer's status response.\n *\n * Network printing only — USB QL support exists in the upstream library\n * but is not wired here.\n */\n\nimport { discovery } from \"@thermal-label/brother-ql-node\";\nimport { MEDIA, findMediaByDimensions } from \"@thermal-label/brother-ql-core\";\nimport type { BrotherQLMedia } from \"@thermal-label/brother-ql-core\";\nimport { createCanvas, loadImage } from \"@napi-rs/canvas\";\nimport { runScriptOrThrow } from \"@bobfrankston/label-core\";\n\n/** Mark an error as user-actionable so cli.ts prints it inline instead of\n * dumping to a log file. The cli's catch block duck-types on `isUserError`. */\nfunction userError(msg: string): Error {\n const e = new Error(msg);\n (e as any).isUserError = true;\n return e;\n}\n\n/**\n * Ask the Windows print spooler what media is loaded for this QL queue, by\n * reading the queue's DefaultPrintTicket XML and matching its MediaSizeWidth /\n * MediaSizeHeight (microns) against the thermal-label MEDIA registry.\n *\n * This is the workaround for QL models (e.g. QL-720NW) that ignore the\n * TCP/9100 status request — the Windows driver cached a media size when it\n * was last configured (P-touch Editor, driver UI, or print-preview), so it's\n * the best signal available without USB.\n *\n * Returns undefined if PowerShell, the queue, or the dimension lookup fails.\n */\nexport async function detectQLMediaFromWindows(printerName: string): Promise<BrotherQLMedia | undefined> {\n const escaped = printerName.replace(/'/g, \"''\");\n const script = `\n$ErrorActionPreference = 'SilentlyContinue'\nAdd-Type -AssemblyName System.Printing | Out-Null\ntry {\n $server = New-Object System.Printing.LocalPrintServer\n $queue = $server.GetPrintQueue('${escaped}')\n $ticket = $queue.DefaultPrintTicket\n $stream = $ticket.GetXmlStream()\n $reader = New-Object System.IO.StreamReader($stream)\n Write-Output $reader.ReadToEnd()\n $reader.Close()\n} catch {}\n`;\n let xml: string;\n try {\n xml = await runScriptOrThrow(script);\n } catch {\n return undefined;\n }\n const w = xml.match(/MediaSizeWidth[^>]*>\\s*<psf:Value[^>]*>(\\d+)/i);\n const h = xml.match(/MediaSizeHeight[^>]*>\\s*<psf:Value[^>]*>(\\d+)/i);\n if (!w || !h) return undefined;\n const widthMm = Math.round(parseInt(w[1], 10) / 1000);\n const heightMm = Math.round(parseInt(h[1], 10) / 1000);\n /* PrintTicket dimensions: (widthMm, heightMm) where width is across the\n * tape and height is along the feed. findMediaByDimensions matches the\n * registry's widthMm/heightMm directly. Two-color flag false — Windows\n * doesn't tell us which palette is loaded; we'd need the printer status\n * for that. */\n return findMediaByDimensions(widthMm, heightMm, false) as BrotherQLMedia | undefined;\n}\n\n/**\n * Print a PNG buffer to a QL printer over TCP/9100.\n *\n * @param host printer hostname or IP (e.g. \"ql720\", \"172.20.1.165\")\n * @param pngBuffer PNG image data\n * @param mediaId optional firmware media id (see MEDIA registry); when\n * omitted the driver uses the media reported by the\n * printer's last getStatus() response\n */\n/**\n * Fit a source image into the printer's exact raster dimensions, in the\n * printer's natural orientation (head pins across = canvas width; feed\n * direction = canvas height — i.e. portrait). Preserves source aspect ratio\n * and rotates 90° CW when the source is landscape so the long axis aligns\n * with the feed direction.\n *\n * Uses `printAreaDots` × `dieCutMaskedAreaDots` from the BrotherQLMedia\n * descriptor — these are the printer's real dot counts, not mm-derived\n * estimates (e.g. DK-11201 = 306 × 991, not 343 × 1063 from naïve 300 DPI).\n * Sending a row count that disagrees with the die-cut length causes the\n * print to spill across label boundaries.\n *\n * White-fills the canvas before drawing so transparent source pixels become\n * white (otherwise dither converts alpha=0 to black).\n */\nfunction fitToMedia(\n src: any, /** @napi-rs/canvas Image */\n media: BrotherQLMedia,\n): { width: number; height: number; data: Uint8Array } {\n const headDots = media.printAreaDots;\n const feedDots = media.heightMm\n ? (media.dieCutMaskedAreaDots ?? Math.round(headDots * media.heightMm / media.widthMm))\n : Math.round(headDots * (Math.max(src.width, src.height) / Math.min(src.width, src.height)));\n\n /* Source long-axis maps to feed; short-axis maps to head. */\n const srcLong = Math.max(src.width, src.height);\n const srcShort = Math.min(src.width, src.height);\n const scale = Math.min(headDots / srcShort, feedDots / srcLong);\n const drawShort = Math.round(srcShort * scale); /** across head */\n const drawLong = Math.round(srcLong * scale); /** along feed */\n const offX = Math.floor((headDots - drawShort) / 2);\n const offY = Math.floor((feedDots - drawLong) / 2);\n\n const canvas = createCanvas(headDots, feedDots);\n const ctx = canvas.getContext(\"2d\");\n ctx.fillStyle = \"white\";\n ctx.fillRect(0, 0, headDots, feedDots);\n\n if (src.width >= src.height) {\n /* Landscape source → rotate 90° CW so its long axis runs down the feed. */\n ctx.save();\n ctx.translate(offX + drawShort, offY);\n ctx.rotate(Math.PI / 2);\n ctx.drawImage(src, 0, 0, drawLong, drawShort);\n ctx.restore();\n } else {\n ctx.drawImage(src, offX, offY, drawShort, drawLong);\n }\n\n const id = ctx.getImageData(0, 0, headDots, feedDots);\n return { width: headDots, height: feedDots, data: new Uint8Array(id.data) };\n}\n\nexport async function printPngToQL(\n host: string,\n pngBuffer: Buffer,\n mediaId?: number,\n windowsPrinterName?: string,\n verbose: boolean = false,\n): Promise<void> {\n const vlog = (msg: string): void => { if (verbose) console.log(msg); };\n const img = await loadImage(pngBuffer);\n\n const printer = await discovery.openPrinter({ host });\n try {\n /* getStatus() polls a 32-byte response with no timeout in the lib;\n * older QL models (e.g. QL-720NW) silently ignore the request over\n * TCP/9100, hanging forever. Race against a wall-clock timeout so\n * we can fall back to explicit media when status isn't available. */\n let explicit: BrotherQLMedia | undefined;\n if (mediaId !== undefined) {\n explicit = MEDIA[mediaId];\n if (!explicit) {\n const ids = Object.keys(MEDIA).join(\", \");\n throw userError(\n `Unknown -ql-media ${mediaId}. The id is the FIRMWARE id, ` +\n `not the DK product code (DK-11201 → 271, DK-22205 → 259). ` +\n `Known ids: ${ids}`\n );\n }\n }\n const status = await Promise.race([\n printer.getStatus().then(s => ({ ok: true as const, s })),\n new Promise<{ ok: false }>(r => setTimeout(() => r({ ok: false }), 2000)),\n ]);\n\n let media: BrotherQLMedia | undefined;\n if (status.ok) {\n if (status.s.errors.length > 0) {\n const msgs = status.s.errors.map(e => e.message).join(\"; \");\n throw userError(`QL @ ${host} not ready: ${msgs}`);\n }\n media = explicit ?? (status.s.detectedMedia as BrotherQLMedia | undefined);\n } else {\n vlog(`[ql] ${host}: no status response (printer may not support it over TCP)`);\n media = explicit;\n if (!media && windowsPrinterName) {\n const winMedia = await detectQLMediaFromWindows(windowsPrinterName);\n if (winMedia) {\n vlog(`[ql] detected from Windows driver: ${winMedia.name}`);\n media = winMedia;\n }\n }\n }\n\n if (!media) {\n throw userError(\n `QL @ ${host}: media not detected and none specified. ` +\n `Pass -ql-media <id> (e.g. 271 = DK-11201 29×90mm address label, ` +\n `259 = DK-22205 62mm continuous). See MEDIA registry for full list.`\n );\n }\n vlog(`[ql] ${host} → ${media.name} (${media.widthMm}mm${media.heightMm ? `×${media.heightMm}mm` : \", continuous\"})`);\n /* Stretch source into the printer's target area, independently per\n * axis. PT-pipeline output is short-and-wide (designed for narrow\n * tape, content drives length); QL die-cut media is wide-and-short.\n * Proportional scaling shrinks the height to keep aspect, leaving\n * blank stripes above/below; independent stretch fills the label.\n * Source orientation is preserved (lib handles rotation).\n *\n * Long axis: dieCutMaskedAreaDots minus a safety pad to absorb the\n * residual feed-direction overrun observed empirically (~10% with\n * default marginDots=35). For continuous media there's no upper\n * bound so we keep proportional scaling there. */\n const isLandscape = img.width >= img.height;\n const srcShort = Math.min(img.width, img.height);\n const targetShort = media.printAreaDots;\n const targetLong = media.dieCutMaskedAreaDots\n ? Math.round(media.dieCutMaskedAreaDots * 0.92)\n : null;\n let w: number;\n let h: number;\n if (targetLong) {\n w = isLandscape ? targetLong : targetShort;\n h = isLandscape ? targetShort : targetLong;\n } else {\n const factor = targetShort / srcShort;\n w = Math.max(1, Math.round(img.width * factor));\n h = Math.max(1, Math.round(img.height * factor));\n }\n const canvas = createCanvas(w, h);\n const ctx = canvas.getContext(\"2d\");\n ctx.fillStyle = \"white\";\n ctx.fillRect(0, 0, w, h);\n ctx.drawImage(img, 0, 0, w, h);\n const id = ctx.getImageData(0, 0, w, h);\n await printer.print(\n { width: w, height: h, data: new Uint8Array(id.data) },\n media,\n );\n } finally {\n await printer.close();\n }\n}\n"]}
|
package/ql.ts
CHANGED
|
@@ -142,7 +142,9 @@ export async function printPngToQL(
|
|
|
142
142
|
pngBuffer: Buffer,
|
|
143
143
|
mediaId?: number,
|
|
144
144
|
windowsPrinterName?: string,
|
|
145
|
+
verbose: boolean = false,
|
|
145
146
|
): Promise<void> {
|
|
147
|
+
const vlog = (msg: string): void => { if (verbose) console.log(msg); };
|
|
146
148
|
const img = await loadImage(pngBuffer);
|
|
147
149
|
|
|
148
150
|
const printer = await discovery.openPrinter({ host });
|
|
@@ -176,12 +178,12 @@ export async function printPngToQL(
|
|
|
176
178
|
}
|
|
177
179
|
media = explicit ?? (status.s.detectedMedia as BrotherQLMedia | undefined);
|
|
178
180
|
} else {
|
|
179
|
-
|
|
181
|
+
vlog(`[ql] ${host}: no status response (printer may not support it over TCP)`);
|
|
180
182
|
media = explicit;
|
|
181
183
|
if (!media && windowsPrinterName) {
|
|
182
184
|
const winMedia = await detectQLMediaFromWindows(windowsPrinterName);
|
|
183
185
|
if (winMedia) {
|
|
184
|
-
|
|
186
|
+
vlog(`[ql] detected from Windows driver: ${winMedia.name}`);
|
|
185
187
|
media = winMedia;
|
|
186
188
|
}
|
|
187
189
|
}
|
|
@@ -194,7 +196,7 @@ export async function printPngToQL(
|
|
|
194
196
|
`259 = DK-22205 62mm continuous). See MEDIA registry for full list.`
|
|
195
197
|
);
|
|
196
198
|
}
|
|
197
|
-
|
|
199
|
+
vlog(`[ql] ${host} → ${media.name} (${media.widthMm}mm${media.heightMm ? `×${media.heightMm}mm` : ", continuous"})`);
|
|
198
200
|
/* Stretch source into the printer's target area, independently per
|
|
199
201
|
* axis. PT-pipeline output is short-and-wide (designed for narrow
|
|
200
202
|
* tape, content drives length); QL die-cut media is wide-and-short.
|