diff --git a/packages/devices/src/drivers/printer-escpos.ts b/packages/devices/src/drivers/printer-escpos.ts index 76cfd8c..7a1f000 100644 --- a/packages/devices/src/drivers/printer-escpos.ts +++ b/packages/devices/src/drivers/printer-escpos.ts @@ -119,13 +119,20 @@ function line(text = ""): Buffer { // dependency). The same code is printed as large human-readable digits below, so // the operator can hand-key it if every reader fails. -/** GS k — Code128 1D barcode. Height/width set first, then HRI off, then data. */ -function code128(data: string): Buffer { +/** GS k — Code128 1D barcode. Height/width set first, then HRI off, then data. + * `moduleWidth` (narrow-bar dots, 1–6) trades scan-tolerance for total width: at ~11 + * modules/char a 13-char id fits 80mm (576 printable dots) at width 3, but a ~20-char + * id needs width 2 or it OVERFLOWS the head and the firmware silently aborts the + * barcode (prints nothing). Keep the symbol LEFT-aligned — centering shifts the start + * point right and makes a wide barcode overflow even sooner. */ +function code128(data: string, moduleWidth = 3): Buffer { // Code128 code set B (printable ASCII) — prefix the data with the {B selector. const payload = Buffer.from(`{B${data}`, "ascii"); + const w = Math.max(1, Math.min(6, moduleWidth)); return Buffer.concat([ + ALIGN_LEFT, // a wide barcode must start at the left margin to fit the 80mm head Buffer.from([GS, 0x68, 0x64]), // GS h 100 — barcode height = 100 dots (taller = tolerant of scan angle) - Buffer.from([GS, 0x77, 0x03]), // GS w 3 — module width = 3 (wider bars for the short-range "Simple" QR/barcode engine; 13-digit Code128 ≈ 495/576 dots, fits 80mm with quiet zones) + Buffer.from([GS, 0x77, w]), // GS w n — module (narrow-bar) width in dots Buffer.from([GS, 0x48, 0x00]), // GS H 0 — HRI text off (we print the id ourselves) // GS k 73 n — function B form: 73 = Code128, n = data byte length. Buffer.from([GS, 0x6b, 0x49, payload.length]), @@ -418,9 +425,12 @@ export function renderReceipt(data: ReceiptData): Buffer { ]; if (data.voucher) { - // The same ticket id, scannable at the exit reader, + the grace emphasis. + // The same ticket id, scannable at the exit reader, + the grace emphasis. code128 + // forces ALIGN_LEFT (a wide barcode must hug the margin); restore centering for the + // human-readable id + grace lines that follow. parts.push( code128(data.ticketId), + ALIGN_CENTER, line(), line(data.ticketId), line(), @@ -456,10 +466,14 @@ export function renderWindowChargeNotice(data: WindowChargeNoticeData): Buffer { line(STR.windowTitle), BOLD_OFF, line(), - // The occurrence id, scannable two ways: Code128 for a 1D laser scanner, QR for - // the booth's combo reader. Either pulls the occurrence up in the pay modal. - code128(data.occurrenceId), + // The occurrence id, scannable two ways. Code128 for a 1D laser scanner FIRST — + // module width 2 + left-aligned (code128 forces ALIGN_LEFT) because the ~20-char + // occurrence id is too wide to fit the 80mm head at width 3 (the firmware would + // abort it). Then the QR for the booth's combo reader. Either pulls the occurrence + // up in the pay modal. + code128(data.occurrenceId, 2), line(), + ALIGN_CENTER, qrCode(data.occurrenceId), line(), // The id in text, as the hand-key fallback if neither scans.