From 2722e4ae282b44145a3ef927d6e29588b2e7e735 Mon Sep 17 00:00:00 2001 From: Cellivar <1441553+Cellivar@users.noreply.github.com> Date: Thu, 2 Jan 2025 22:25:27 -0800 Subject: [PATCH 1/4] Remove spurious warnings from parser code --- src/ReceiptLine/Parser.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/ReceiptLine/Parser.ts b/src/ReceiptLine/Parser.ts index 4750ae3..304a8a4 100644 --- a/src/ReceiptLine/Parser.ts +++ b/src/ReceiptLine/Parser.ts @@ -588,7 +588,6 @@ function createLine( const isTextLine = line.every(el => el.text !== undefined); const firstColumn = line[0]; - console.warn('First column', firstColumn); // remove zero width columns let columns = line.filter(el => el.width !== 0); @@ -632,7 +631,6 @@ function createLine( const left = Math.floor(freeWidth * getLeftAlignmentMultiplier(firstColumn.lineAlignment)); const width = printerConfig.charactersPerLine - freeWidth; const right = freeWidth - left; - console.warn(`Line margins are | ${left} [ ${width} ] ${right} |`); // process text if (isTextLine) { From 4ab2d4b04fbcc4ce7bf2cccf3249f074ef8a2b9b Mon Sep 17 00:00:00 2001 From: Cellivar <1441553+Cellivar@users.noreply.github.com> Date: Thu, 2 Jan 2025 22:40:10 -0800 Subject: [PATCH 2/4] Fix end of form status message handling --- src/Documents/DocumentTranspiler.ts | 19 +++++++++++++------ src/Languages/EscPos/CmdSetAutoStatusBack.ts | 3 ++- src/Languages/EscPos/CmdTransmitPrinterId.ts | 5 ++++- .../EscPos/CmdTransmitPrinterStatus.ts | 1 + src/Languages/EscPos/EscPos.ts | 5 ++--- 5 files changed, 22 insertions(+), 11 deletions(-) diff --git a/src/Documents/DocumentTranspiler.ts b/src/Documents/DocumentTranspiler.ts index fca162f..3d570cc 100644 --- a/src/Documents/DocumentTranspiler.ts +++ b/src/Documents/DocumentTranspiler.ts @@ -23,10 +23,6 @@ export function transpileDocument( const forms = splitTransactionsAndForms(doc.commands, commandSet, commandReorderBehavior); - // Wait why do we throw away the form data here? - // ZPL has some advanced form processing concepts that aren't implemented in - // this library yet, and that code was annoying to figure out. It hangs out - // here until the advanced form processing can be implemented later. // TODO: Handle separate forms instead of mooshing them together. const { transactions, effects } = combineForms(forms); @@ -140,10 +136,8 @@ function splitTransactionsAndForms( } // Record the command in the transpile buffer. - if (command.type !== "NoOp") { currentTrans.commands.push(command); command.effectFlags.forEach(f => currentForm.effects.add(f)); - } if (command.effectFlags.has("waitsForResponse")) { // This command expects the printer to provide feedback. @@ -221,6 +215,19 @@ function splitTransactionsAndForms( } } + const lastTransaction = forms.at(-1)?.transactions.at(-1); + if (lastTransaction === undefined || lastTransaction.waitCommands.length === 0) { + // Best practice is to always have the printer return some information when + // it's finished printing. If the last transaction doesn't have anything to + // wait for add our own. + const awaitForms = splitTransactionsAndForms( + [new Cmds.GetStatus()], + commandSet, + reorderBehavior, + ); + awaitForms.forEach(f => forms.push(f)); + } + return forms; } diff --git a/src/Languages/EscPos/CmdSetAutoStatusBack.ts b/src/Languages/EscPos/CmdSetAutoStatusBack.ts index d365054..ccc2d60 100644 --- a/src/Languages/EscPos/CmdSetAutoStatusBack.ts +++ b/src/Languages/EscPos/CmdSetAutoStatusBack.ts @@ -32,7 +32,8 @@ export class CmdSetAutoStatusBack implements Cmds.IPrinterExtendedCommand { export const mappingCmdSetAutoStatusBack: Cmds.IPrinterCommandMapping = { commandType: CmdSetAutoStatusBack.typeE, transpile: handleCmdSetAutoStatusBack, - readMessage: parseCmdSetAutoStatusBack + readMessage: parseCmdSetAutoStatusBack, + formInclusionMode: Cmds.CommandFormInclusionMode.noForm, } export function handleCmdSetAutoStatusBack( diff --git a/src/Languages/EscPos/CmdTransmitPrinterId.ts b/src/Languages/EscPos/CmdTransmitPrinterId.ts index dbfd904..48ff087 100644 --- a/src/Languages/EscPos/CmdTransmitPrinterId.ts +++ b/src/Languages/EscPos/CmdTransmitPrinterId.ts @@ -48,6 +48,7 @@ export const mappingCmdTransmitPrinterId: Cmds.IPrinterCommandMapping[] = [] @@ -91,7 +91,6 @@ export class EscPos extends Cmds.RawCommandSet { // The manual indicates always getting the paper status is a good practice // as it tells you when the printer is done printing. This also ensures we // always have something to await at the end of a document. - expand: () => [new CmdTransmitPrinterStatus('PaperSensorStatus')] }, Barcode: { commandType: 'Barcode', From 4b01bab6c5b7187a96b843a73a40e13987504c24 Mon Sep 17 00:00:00 2001 From: Cellivar <1441553+Cellivar@users.noreply.github.com> Date: Thu, 2 Jan 2025 22:40:38 -0800 Subject: [PATCH 3/4] Add ready to print documents for common operations --- demo/index.html | 22 +- demo/test_index.ts | 19 +- src/Commands/BasicCommands.ts | 6 + src/Documents/DocumentTranspiler.test.ts | 309 +++++++++++++++++++++++ src/Documents/DocumentTranspiler.ts | 4 +- src/ReadyToPrintDocuments.ts | 61 +++++ src/ReceiptPrinter.ts | 10 +- src/index.ts | 2 +- 8 files changed, 393 insertions(+), 40 deletions(-) create mode 100644 src/Documents/DocumentTranspiler.test.ts create mode 100644 src/ReadyToPrintDocuments.ts diff --git a/demo/index.html b/demo/index.html index 9fe2a4c..b8861d5 100644 --- a/demo/index.html +++ b/demo/index.html @@ -91,6 +91,9 @@ // For this demo we're going to make use of the USB printer manager // so it can take care of concerns like the USB connect and disconnect events. + // For this demo we're going to make use of the USB printer manager + // so it can take care of concerns like the USB connect and disconnect events. + // It's a handy-dandy feature included from the web-device-mux library! // We need to tell it what type of device it's managing, and how to filter // USB devices that are receipt printers. @@ -220,9 +223,7 @@

${titleHtml}


Fix the issue, then dismiss this alert to check the status again.

`, // And when the alert is dismissed, check the status again! - () => printer.sendDocument({ - commands: [new WebReceipt.GetStatus()] - }) + () => printer.sendDocument(WebReceipt.ReadyToPrintDocuments.getStatus), ); }); }); @@ -339,30 +340,21 @@

${titleHtml}

e.preventDefault(); const printerIdx = (e.currentTarget as HTMLAnchorElement).dataset.printerIdx as unknown as number; const printer = this.printers[printerIdx]; - const doc = { - commands: [new WebReceipt.TestPrint('rolling')] - }; - await printer.sendDocument(doc); + await printer.sendDocument(WebReceipt.ReadyToPrintDocuments.printTest); }); document.getElementById(`printconfig_${idx}`)! .addEventListener('click', async (e) => { e.preventDefault(); const printerIdx = (e.currentTarget as HTMLAnchorElement).dataset.printerIdx as unknown as number; const printer = this.printers[printerIdx]; - const doc = { - commands: [new WebReceipt.TestPrint('printerStatus')] - }; - await printer.sendDocument(doc); + await printer.sendDocument(WebReceipt.ReadyToPrintDocuments.printConfig); }); document.getElementById(`drawerkick_${idx}`)! .addEventListener('click', async (e) => { e.preventDefault(); const printerIdx = (e.currentTarget as HTMLAnchorElement).dataset.printerIdx as unknown as number; const printer = this.printers[printerIdx]; - const doc = { - commands: [new WebReceipt.PulseCommand()] - }; - await printer.sendDocument(doc); + await printer.sendDocument(WebReceipt.ReadyToPrintDocuments.drawerKick); }); } diff --git a/demo/test_index.ts b/demo/test_index.ts index 3450534..fe038af 100644 --- a/demo/test_index.ts +++ b/demo/test_index.ts @@ -149,9 +149,7 @@ class BasicDocumentPrinterApp {

Fix the issue, then dismiss this alert to check the status again.

`, // And when the alert is dismissed, check the status again! - () => printer.sendDocument({ - commands: [new WebReceipt.GetStatus()] - }) + () => printer.sendDocument(WebReceipt.ReadyToPrintDocuments.getStatus), ); }); }); @@ -268,30 +266,21 @@ class BasicDocumentPrinterApp { e.preventDefault(); const printerIdx = (e.currentTarget as HTMLAnchorElement).dataset.printerIdx as unknown as number; const printer = this.printers[printerIdx]; - const doc = { - commands: [new WebReceipt.TestPrint('rolling')] - }; - await printer.sendDocument(doc); + await printer.sendDocument(WebReceipt.ReadyToPrintDocuments.printTest); }); document.getElementById(`printconfig_${idx}`)! .addEventListener('click', async (e) => { e.preventDefault(); const printerIdx = (e.currentTarget as HTMLAnchorElement).dataset.printerIdx as unknown as number; const printer = this.printers[printerIdx]; - const doc = { - commands: [new WebReceipt.TestPrint('printerStatus')] - }; - await printer.sendDocument(doc); + await printer.sendDocument(WebReceipt.ReadyToPrintDocuments.printConfig); }); document.getElementById(`drawerkick_${idx}`)! .addEventListener('click', async (e) => { e.preventDefault(); const printerIdx = (e.currentTarget as HTMLAnchorElement).dataset.printerIdx as unknown as number; const printer = this.printers[printerIdx]; - const doc = { - commands: [new WebReceipt.PulseCommand()] - }; - await printer.sendDocument(doc); + await printer.sendDocument(WebReceipt.ReadyToPrintDocuments.drawerKick); }); } diff --git a/src/Commands/BasicCommands.ts b/src/Commands/BasicCommands.ts index 6056e3f..b698063 100644 --- a/src/Commands/BasicCommands.ts +++ b/src/Commands/BasicCommands.ts @@ -41,6 +41,12 @@ export class QueryConfiguration extends BasicCommand { constructor() { super(['waitsForResponse']); } } +export class PrintConfiguration extends BasicCommand { + name = 'Print the printer configuration' + type: CommandTypeBasic = 'PrintConfiguration'; + constructor() { super(['feedsPaper']); } +} + export class GetStatus extends BasicCommand { name = 'Get the printer status' type: CommandTypeBasic = 'GetStatus'; diff --git a/src/Documents/DocumentTranspiler.test.ts b/src/Documents/DocumentTranspiler.test.ts new file mode 100644 index 0000000..efd289a --- /dev/null +++ b/src/Documents/DocumentTranspiler.test.ts @@ -0,0 +1,309 @@ +import { expect, describe, it } from 'vitest'; +import {ReadyToPrintDocuments} from '../ReadyToPrintDocuments.js'; +import { getNewTranspileState, PrinterConfig, type TranspiledDocumentState } from '../Commands/index.js'; +import { transpileDocument } from './DocumentTranspiler.js'; +import { EscPos } from '../Languages/index.js'; + +function getFakeState(): TranspiledDocumentState { + return getNewTranspileState(new PrinterConfig()); +} + +describe('DocumentTranspiler', () => { + describe('escpos', () => { + const escpos = new EscPos.EscPos(); + + describe('ReadyToPrint Docs', () => { + it('configDocument', () => { + const state = getFakeState(); + expect(transpileDocument(ReadyToPrintDocuments.getConfig, escpos, state)) + .toMatchInlineSnapshot(` + CompiledDocument { + "effects": Set { + "waitsForResponse", + }, + "language": 1, + "transactions": [ + Transaction { + "awaitedCommands": [ + CmdTransmitPrinterId { + "commandLanguageApplicability": 1, + "effectFlags": Set { + "waitsForResponse", + }, + "name": "Transmit Printer ID", + "subcommand": "TypeID", + "type": "CustomCommand", + "typeExtended": Symbol(CmdTransmitPrinterId), + }, + ], + "commands": Uint8Array [ + 29, + 73, + 2, + ], + }, + Transaction { + "awaitedCommands": [ + CmdTransmitPrinterId { + "commandLanguageApplicability": 1, + "effectFlags": Set { + "waitsForResponse", + }, + "name": "Transmit Printer ID", + "subcommand": "InfoBMakerName", + "type": "CustomCommand", + "typeExtended": Symbol(CmdTransmitPrinterId), + }, + ], + "commands": Uint8Array [ + 29, + 73, + 66, + ], + }, + Transaction { + "awaitedCommands": [ + CmdTransmitPrinterId { + "commandLanguageApplicability": 1, + "effectFlags": Set { + "waitsForResponse", + }, + "name": "Transmit Printer ID", + "subcommand": "InfoBModelName", + "type": "CustomCommand", + "typeExtended": Symbol(CmdTransmitPrinterId), + }, + ], + "commands": Uint8Array [ + 29, + 73, + 67, + ], + }, + Transaction { + "awaitedCommands": [ + CmdTransmitPrinterId { + "commandLanguageApplicability": 1, + "effectFlags": Set { + "waitsForResponse", + }, + "name": "Transmit Printer ID", + "subcommand": "InfoBSerialNo", + "type": "CustomCommand", + "typeExtended": Symbol(CmdTransmitPrinterId), + }, + ], + "commands": Uint8Array [ + 29, + 73, + 68, + ], + }, + Transaction { + "awaitedCommands": [ + CmdTransmitPrinterId { + "commandLanguageApplicability": 1, + "effectFlags": Set { + "waitsForResponse", + }, + "name": "Transmit Printer ID", + "subcommand": "InfoBFirmwareVersion", + "type": "CustomCommand", + "typeExtended": Symbol(CmdTransmitPrinterId), + }, + ], + "commands": Uint8Array [ + 29, + 73, + 65, + ], + }, + ], + } + `); + }); + + it('printerStatus', () => { + const state = getFakeState(); + expect(transpileDocument(ReadyToPrintDocuments.getStatus, escpos, state)) + .toMatchInlineSnapshot(` + CompiledDocument { + "effects": Set { + "waitsForResponse", + }, + "language": 1, + "transactions": [ + Transaction { + "awaitedCommands": [ + CmdTransmitPrinterStatus { + "commandLanguageApplicability": 1, + "effectFlags": Set { + "waitsForResponse", + }, + "name": "Transmit Printer Status", + "subcommand": "PaperSensorStatus", + "type": "CustomCommand", + "typeExtended": Symbol(TransmitPrinterStatus), + }, + ], + "commands": Uint8Array [ + 29, + 114, + 1, + ], + }, + Transaction { + "awaitedCommands": [ + CmdTransmitPrinterStatus { + "commandLanguageApplicability": 1, + "effectFlags": Set { + "waitsForResponse", + }, + "name": "Transmit Printer Status", + "subcommand": "DrawerKickStatus", + "type": "CustomCommand", + "typeExtended": Symbol(TransmitPrinterStatus), + }, + ], + "commands": Uint8Array [ + 29, + 114, + 2, + ], + }, + ], + } + `); + }); + + it('printerStatus', () => { + const state = getFakeState(); + expect(transpileDocument(ReadyToPrintDocuments.feedMedia, escpos, state)) + .toMatchInlineSnapshot(` + CompiledDocument { + "effects": Set { + "feedsPaper", + "waitsForResponse", + }, + "language": 1, + "transactions": [ + Transaction { + "awaitedCommands": [], + "commands": Uint8Array [ + 10, + 10, + 10, + 10, + ], + }, + Transaction { + "awaitedCommands": [ + CmdTransmitPrinterStatus { + "commandLanguageApplicability": 1, + "effectFlags": Set { + "waitsForResponse", + }, + "name": "Transmit Printer Status", + "subcommand": "PaperSensorStatus", + "type": "CustomCommand", + "typeExtended": Symbol(TransmitPrinterStatus), + }, + ], + "commands": Uint8Array [ + 29, + 114, + 1, + ], + }, + Transaction { + "awaitedCommands": [ + CmdTransmitPrinterStatus { + "commandLanguageApplicability": 1, + "effectFlags": Set { + "waitsForResponse", + }, + "name": "Transmit Printer Status", + "subcommand": "DrawerKickStatus", + "type": "CustomCommand", + "typeExtended": Symbol(TransmitPrinterStatus), + }, + ], + "commands": Uint8Array [ + 29, + 114, + 2, + ], + }, + ], + } + `); + }); + + it('printerStatus', () => { + const state = getFakeState(); + expect(transpileDocument(ReadyToPrintDocuments.printConfig, escpos, state)) + .toMatchInlineSnapshot(` + CompiledDocument { + "effects": Set { + "feedsPaper", + "waitsForResponse", + }, + "language": 1, + "transactions": [ + Transaction { + "awaitedCommands": [], + "commands": Uint8Array [ + 29, + 40, + 65, + 2, + 0, + 1, + 2, + ], + }, + Transaction { + "awaitedCommands": [ + CmdTransmitPrinterStatus { + "commandLanguageApplicability": 1, + "effectFlags": Set { + "waitsForResponse", + }, + "name": "Transmit Printer Status", + "subcommand": "PaperSensorStatus", + "type": "CustomCommand", + "typeExtended": Symbol(TransmitPrinterStatus), + }, + ], + "commands": Uint8Array [ + 29, + 114, + 1, + ], + }, + Transaction { + "awaitedCommands": [ + CmdTransmitPrinterStatus { + "commandLanguageApplicability": 1, + "effectFlags": Set { + "waitsForResponse", + }, + "name": "Transmit Printer Status", + "subcommand": "DrawerKickStatus", + "type": "CustomCommand", + "typeExtended": Symbol(TransmitPrinterStatus), + }, + ], + "commands": Uint8Array [ + 29, + 114, + 2, + ], + }, + ], + } + `); + }); + }); + }); +}); diff --git a/src/Documents/DocumentTranspiler.ts b/src/Documents/DocumentTranspiler.ts index 3d570cc..4bae184 100644 --- a/src/Documents/DocumentTranspiler.ts +++ b/src/Documents/DocumentTranspiler.ts @@ -136,8 +136,8 @@ function splitTransactionsAndForms( } // Record the command in the transpile buffer. - currentTrans.commands.push(command); - command.effectFlags.forEach(f => currentForm.effects.add(f)); + currentTrans.commands.push(command); + command.effectFlags.forEach(f => currentForm.effects.add(f)); if (command.effectFlags.has("waitsForResponse")) { // This command expects the printer to provide feedback. diff --git a/src/ReadyToPrintDocuments.ts b/src/ReadyToPrintDocuments.ts new file mode 100644 index 0000000..04228fc --- /dev/null +++ b/src/ReadyToPrintDocuments.ts @@ -0,0 +1,61 @@ +import * as Docs from './Documents/index.js'; +import * as Cmds from './Commands/index.js'; + +export class ReadyToPrintDocuments { + /** A command document to query the printer for configuration. */ + static get getConfig(): Docs.IDocument { + return { + commands: [ + new Cmds.QueryConfiguration(), + ] + }; + } + + /** A command document to query the printer's status. */ + static get getStatus(): Docs.IDocument { + return { + commands: [ + new Cmds.GetStatus(), + ] + }; + } + + /** A command document to make the printer print its configuration. */ + static get printConfig(): Docs.IDocument { + return { + commands: [ + new Cmds.PrintConfiguration(), + ] + }; + } + + /** A command document to make the printer print a test pattern. */ + static get printTest(): Docs.IDocument { + return { + commands: [ + new Cmds.TestPrint(), + ] + }; + } + + /** A command document to feed some paper. */ + static get feedMedia(): Docs.IDocument { + return { + commands: [ + new Cmds.Newline(), + new Cmds.Newline(), + new Cmds.Newline(), + new Cmds.Newline(), + ] + } + } + + /** A command document to open a connected cash drawer. */ + static get drawerKick(): Docs.IDocument { + return { + commands: [ + new Cmds.PulseCommand(), + ] + }; + } +} diff --git a/src/ReceiptPrinter.ts b/src/ReceiptPrinter.ts index 60e6f8d..3bcfa12 100644 --- a/src/ReceiptPrinter.ts +++ b/src/ReceiptPrinter.ts @@ -4,6 +4,7 @@ import * as Cmds from './Commands/index.js'; import * as Docs from './Documents/index.js'; import * as Lang from './Languages/index.js'; import * as Mux from 'web-device-mux'; +import { ReadyToPrintDocuments } from './ReadyToPrintDocuments.js'; export interface ReceiptPrinterEventMap { //disconnectedDevice: CustomEvent; @@ -193,12 +194,7 @@ export class ReceiptPrinter extends do { retryLimit--; try { - await this.sendDocument({ - commands: [ - new Cmds.QueryConfiguration(), - new Cmds.GetStatus(), - ] - }); + await this.sendDocument(ReadyToPrintDocuments.getConfig); return this.printerOptions; } catch (e) { @@ -232,7 +228,7 @@ export class ReceiptPrinter extends /** Send a compiled document to the printer. */ public async sendCompiledDocument(doc: Docs.CompiledDocument): Promise { - if (this._disposed == true) { + if (!this.connected) { throw new Mux.DeviceNotReadyError("Printer is not ready to communicate."); } diff --git a/src/index.ts b/src/index.ts index cf01fea..1c86111 100644 --- a/src/index.ts +++ b/src/index.ts @@ -6,4 +6,4 @@ export * from './Languages/index.js'; export * from './ReceiptLine/index.js'; export * from './ReceiptPrinter.js'; -//export * from './ReadyToPrintDocuments.js'; +export * from './ReadyToPrintDocuments.js'; From cd4ea438d641988799d57e36ebfb5b374191047f Mon Sep 17 00:00:00 2001 From: Cellivar <1441553+Cellivar@users.noreply.github.com> Date: Thu, 2 Jan 2025 22:43:07 -0800 Subject: [PATCH 4/4] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e08e2a7..4d12c2c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "web-receiptline-printer", - "version": "0.3.0-rc1", + "version": "0.3.0-rc2", "description": "A small library for printing ReceiptLine documents to various receipt printers from a browser.", "type": "module", "repository": {