diff --git a/docs/documents/niimbot_print_tasks.md b/docs/documents/niimbot_print_tasks.md index 0c42729..48c6007 100644 --- a/docs/documents/niimbot_print_tasks.md +++ b/docs/documents/niimbot_print_tasks.md @@ -57,10 +57,10 @@ PrintStart [1(u8)] Page: ``` -PrintClear [1(u8)] PageStart [1(u8)] SetPageSize [rows(u16), cols(u16)] PrintEmptyRow | PrintBitmapRow | PrintBitmapRowIndexed +PrinterCheckLine [line(u16), 1(u8)] - after every 200 lines (data example: 00c701, 018f01, 025701, 031f01, ...) PageEnd [1(u8)] ``` diff --git a/src/client/abstract_client.ts b/src/client/abstract_client.ts index a622076..bdab153 100644 --- a/src/client/abstract_client.ts +++ b/src/client/abstract_client.ts @@ -90,7 +90,7 @@ export abstract class NiimbotAbstractClient extends EventEmitter<ClientEventMap> await this.sendPacket(packet, true); if (packet.oneWay) { - return new NiimbotPacket(ResponseCommandId.Invalid, []); // or undefined is better? + return new NiimbotPacket(ResponseCommandId.In_Invalid, []); // or undefined is better? } return this.waitForPacket(packet.validResponseIds, true, timeoutMs); diff --git a/src/image_encoder.ts b/src/image_encoder.ts index 8ed641a..5738203 100644 --- a/src/image_encoder.ts +++ b/src/image_encoder.ts @@ -2,7 +2,7 @@ import { Utils } from "."; /** @category Image encoder */ export type ImageRow = { - dataType: "void" | "pixels"; + dataType: "void" | "pixels" | "check"; rowNumber: number; repeat: number; blackPixelsCount: number; @@ -83,14 +83,31 @@ export class ImageEncoder { } else { rowsData.push(newPart); } + + const sendRowCheck = row % 200 === 199; + + if (sendRowCheck) { + rowsData.push({ + dataType: "check", + rowNumber: row, + repeat: 0, + rowData: undefined, + blackPixelsCount: 0, + }); + } } } - + return { cols, rows, rowsData }; } /** printDirection = "left" rotates image to 90 degrees clockwise */ - public static isPixelNonWhite(iData: ImageData, x: number, y: number, printDirection: PrintDirection = "left"): boolean { + public static isPixelNonWhite( + iData: ImageData, + x: number, + y: number, + printDirection: PrintDirection = "left" + ): boolean { let idx = y * iData.width + x; if (printDirection === "left") { diff --git a/src/packets/commands.ts b/src/packets/commands.ts index 586d746..5bb016a 100644 --- a/src/packets/commands.ts +++ b/src/packets/commands.ts @@ -55,7 +55,7 @@ export enum RequestCommandId { * @category Packets **/ export enum ResponseCommandId { - Invalid = -1, + In_Invalid = -1, In_NotSupported = 0x00, In_Connect = 0xc2, In_CalibrateHeight = 0x69, diff --git a/src/packets/packet_generator.ts b/src/packets/packet_generator.ts index e6451e9..a66aac2 100644 --- a/src/packets/packet_generator.ts +++ b/src/packets/packet_generator.ts @@ -10,9 +10,20 @@ import { commandsMap, NiimbotCrc32Packet, } from "."; -import { EncodedImage, ImageEncoder, ImageRow } from "../image_encoder"; +import { EncodedImage, ImageEncoder } from "../image_encoder"; import { Utils } from "../utils"; +export interface ImagePacketsGenerateOptions { + /** Mode for "black pixel count" section of bitmap packet. */ + countsMode?: "auto" | "split" | "total"; + /** Disable PrintBitmapRowIndexed packet. */ + noIndexPacket?: boolean; + /** Send PrinterCheckLine every 200 line. */ + enableCheckLine?: boolean; + /** Printer head resolution. Used for "black pixel count" section calculation. */ + printheadPixels?: number; +} + /** * A helper class that generates various types of packets. * @category Packets @@ -178,8 +189,14 @@ export class PacketGenerator { return this.mapped(TX.PrintEmptyRow, [...Utils.u16ToBytes(pos), repeats]); } - public static printBitmapRow(pos: number, repeats: number, data: Uint8Array, printheadPixels?: number): NiimbotPacket { - const counts = Utils.countPixelsForBitmapPacket(data, printheadPixels ?? 0); + public static printBitmapRow( + pos: number, + repeats: number, + data: Uint8Array, + printheadPixels: number, + countsMode: "auto" | "split" | "total" = "auto" + ): NiimbotPacket { + const counts = Utils.countPixelsForBitmapPacket(data, printheadPixels, countsMode); return this.mapped(TX.PrintBitmapRow, [...Utils.u16ToBytes(pos), ...counts.parts, repeats, ...data]); } @@ -189,9 +206,10 @@ export class PacketGenerator { pos: number, repeats: number, data: Uint8Array, - printheadPixels?: number + printheadPixels: number, + countsMode: "auto" | "split" | "total" = "auto" ): NiimbotPacket { - const counts = Utils.countPixelsForBitmapPacket(data, printheadPixels ?? 0); + const counts = Utils.countPixelsForBitmapPacket(data, printheadPixels ?? 0, countsMode); const indexes: Uint8Array = ImageEncoder.indexPixels(data); if (counts.total > 6) { @@ -209,18 +227,50 @@ export class PacketGenerator { return this.mapped(TX.WriteRFID, data); } - public static writeImageData(image: EncodedImage, printheadPixels?: number): NiimbotPacket[] { - return image.rowsData.map((p: ImageRow) => { - if (p.dataType === "pixels") { - if (p.blackPixelsCount > 6) { - return this.printBitmapRow(p.rowNumber, p.repeat, p.rowData!, printheadPixels); + public static checkLine(line: number): NiimbotPacket { + return this.mapped(TX.PrinterCheckLine, [...Utils.u16ToBytes(line), 0x01]); + } + + public static writeImageData(image: EncodedImage, options?: ImagePacketsGenerateOptions): NiimbotPacket[] { + let out: NiimbotPacket[] = []; + + for (const d of image.rowsData) { + if (d.dataType === "pixels") { + if (d.blackPixelsCount <= 6 && !options?.noIndexPacket) { + out.push( + this.printBitmapRowIndexed( + d.rowNumber, + d.repeat, + d.rowData!, + options?.printheadPixels ?? 0, + options?.countsMode ?? "auto" + ) + ); } else { - return this.printBitmapRowIndexed(p.rowNumber, p.repeat, p.rowData!, printheadPixels); + out.push( + this.printBitmapRow( + d.rowNumber, + d.repeat, + d.rowData!, + options?.printheadPixels ?? 0, + options?.countsMode ?? "auto" + ) + ); } - } else { - return this.printEmptySpace(p.rowNumber, p.repeat); + continue; } - }); + + if (d.dataType === "check" && options?.enableCheckLine) { + out.push(this.checkLine(d.rowNumber)); + continue; + } + + if (d.dataType === "void") { + out.push(this.printEmptySpace(d.rowNumber, d.repeat)); + } + } + + return out; } public static printTestPage(): NiimbotPacket { @@ -232,11 +282,11 @@ export class PacketGenerator { } public static startFirmwareUpgrade(version: string): NiimbotPacket { - if(!/^\d+\.\d+$/.test(version)) { + if (!/^\d+\.\d+$/.test(version)) { throw new Error("Invalid version format (x.x expected)"); } - const [a, b] = version.split(".").map(p => parseInt(p)); + const [a, b] = version.split(".").map((p) => parseInt(p)); return this.mapped(TX.StartFirmwareUpgrade, [a, b]); } diff --git a/src/print_tasks/B1PrintTask.ts b/src/print_tasks/B1PrintTask.ts index 626b14b..dd8996e 100644 --- a/src/print_tasks/B1PrintTask.ts +++ b/src/print_tasks/B1PrintTask.ts @@ -17,20 +17,22 @@ export class B1PrintTask extends AbstractPrintTask { override printPage(image: EncodedImage, quantity?: number): Promise<void> { this.checkAddPage(quantity ?? 1); - return this.abstraction.sendAll([ - PacketGenerator.pageStart(), - PacketGenerator.setPageSizeV3(image.rows, image.cols, quantity ?? 1), - ...PacketGenerator.writeImageData(image, this.printheadPixels()), - PacketGenerator.pageEnd(), - ], this.printOptions.pageTimeoutMs); + return this.abstraction.sendAll( + [ + PacketGenerator.pageStart(), + PacketGenerator.setPageSizeV3(image.rows, image.cols, quantity ?? 1), + ...PacketGenerator.writeImageData(image, { printheadPixels: this.printheadPixels() }), + PacketGenerator.pageEnd(), + ], + this.printOptions.pageTimeoutMs + ); } override waitForFinished(): Promise<void> { this.abstraction.setPacketTimeout(this.printOptions.statusTimeoutMs); - return this.abstraction.waitUntilPrintFinishedByStatusPoll( - this.printOptions.totalPages, - this.printOptions.statusPollIntervalMs - ).finally(() => this.abstraction.setDefaultPacketTimeout()); + return this.abstraction + .waitUntilPrintFinishedByStatusPoll(this.printOptions.totalPages, this.printOptions.statusPollIntervalMs) + .finally(() => this.abstraction.setDefaultPacketTimeout()); } } diff --git a/src/print_tasks/B21V1PrintTask.ts b/src/print_tasks/B21V1PrintTask.ts index 65b4153..330a6c8 100644 --- a/src/print_tasks/B21V1PrintTask.ts +++ b/src/print_tasks/B21V1PrintTask.ts @@ -18,22 +18,28 @@ export class B21V1PrintTask extends AbstractPrintTask { this.checkAddPage(quantity ?? 1); for (let i = 0; i < (quantity ?? 1); i++) { - await this.abstraction.sendAll([ - PacketGenerator.printClear(), - PacketGenerator.pageStart(), - PacketGenerator.setPageSizeV2(image.rows, image.cols), - ...PacketGenerator.writeImageData(image, this.printheadPixels()), - PacketGenerator.pageEnd(), - ], this.printOptions.pageTimeoutMs); + await this.abstraction.sendAll( + [ + // PacketGenerator.printClear(), + PacketGenerator.pageStart(), + PacketGenerator.setPageSizeV2(image.rows, image.cols), + ...PacketGenerator.writeImageData(image, { + countsMode: "total", + enableCheckLine: true, + printheadPixels: this.printheadPixels(), + }), + PacketGenerator.pageEnd(), + ], + this.printOptions.pageTimeoutMs + ); } } override waitForFinished(): Promise<void> { this.abstraction.setPacketTimeout(this.printOptions.statusTimeoutMs); - return this.abstraction.waitUntilPrintFinishedByPrintEndPoll( - this.printOptions.totalPages, - this.printOptions.statusPollIntervalMs - ).finally(() => this.abstraction.setDefaultPacketTimeout()); + return this.abstraction + .waitUntilPrintFinishedByPrintEndPoll(this.printOptions.totalPages, this.printOptions.statusPollIntervalMs) + .finally(() => this.abstraction.setDefaultPacketTimeout()); } } diff --git a/src/print_tasks/D110PrintTask.ts b/src/print_tasks/D110PrintTask.ts index 69614a9..f738814 100644 --- a/src/print_tasks/D110PrintTask.ts +++ b/src/print_tasks/D110PrintTask.ts @@ -17,22 +17,24 @@ export class D110PrintTask extends AbstractPrintTask { override printPage(image: EncodedImage, quantity?: number): Promise<void> { this.checkAddPage(quantity ?? 1); - return this.abstraction.sendAll([ - PacketGenerator.printClear(), - PacketGenerator.pageStart(), - PacketGenerator.setPageSizeV2(image.rows, image.cols), - PacketGenerator.setPrintQuantity(quantity ?? 1), - ...PacketGenerator.writeImageData(image, this.printheadPixels()), - PacketGenerator.pageEnd(), - ], this.printOptions.pageTimeoutMs); + return this.abstraction.sendAll( + [ + PacketGenerator.printClear(), + PacketGenerator.pageStart(), + PacketGenerator.setPageSizeV2(image.rows, image.cols), + PacketGenerator.setPrintQuantity(quantity ?? 1), + ...PacketGenerator.writeImageData(image, { printheadPixels: this.printheadPixels() }), + PacketGenerator.pageEnd(), + ], + this.printOptions.pageTimeoutMs + ); } override waitForFinished(): Promise<void> { this.abstraction.setPacketTimeout(this.printOptions.statusTimeoutMs); - return this.abstraction.waitUntilPrintFinishedByStatusPoll( - this.printOptions.totalPages, - this.printOptions.statusPollIntervalMs - ).finally(() => this.abstraction.setDefaultPacketTimeout()); + return this.abstraction + .waitUntilPrintFinishedByStatusPoll(this.printOptions.totalPages, this.printOptions.statusPollIntervalMs) + .finally(() => this.abstraction.setDefaultPacketTimeout()); } } diff --git a/src/print_tasks/OldD11PrintTask.ts b/src/print_tasks/OldD11PrintTask.ts index 7a21e83..6325c36 100644 --- a/src/print_tasks/OldD11PrintTask.ts +++ b/src/print_tasks/OldD11PrintTask.ts @@ -17,14 +17,17 @@ export class OldD11PrintTask extends AbstractPrintTask { override printPage(image: EncodedImage, quantity: number): Promise<void> { this.checkAddPage(quantity ?? 1); - return this.abstraction.sendAll([ - PacketGenerator.printClear(), - PacketGenerator.pageStart(), - PacketGenerator.setPageSizeV1(image.rows), - PacketGenerator.setPrintQuantity(quantity ?? 1), - ...PacketGenerator.writeImageData(image, this.printheadPixels()), - PacketGenerator.pageEnd(), - ], this.printOptions.pageTimeoutMs); + return this.abstraction.sendAll( + [ + PacketGenerator.printClear(), + PacketGenerator.pageStart(), + PacketGenerator.setPageSizeV1(image.rows), + PacketGenerator.setPrintQuantity(quantity ?? 1), + ...PacketGenerator.writeImageData(image, { printheadPixels: this.printheadPixels() }), + PacketGenerator.pageEnd(), + ], + this.printOptions.pageTimeoutMs + ); } override waitForFinished(): Promise<void> { diff --git a/src/print_tasks/V5PrintTask.ts b/src/print_tasks/V5PrintTask.ts index 8affe0c..badf406 100644 --- a/src/print_tasks/V5PrintTask.ts +++ b/src/print_tasks/V5PrintTask.ts @@ -1,5 +1,5 @@ import { EncodedImage } from "../image_encoder"; -import { PacketGenerator } from "../packets"; +import { PacketGenerator } from "../packets"; import { AbstractPrintTask } from "./AbstractPrintTask"; /** @@ -10,27 +10,29 @@ export class V5PrintTask extends AbstractPrintTask { return this.abstraction.sendAll([ PacketGenerator.setDensity(this.printOptions.density), PacketGenerator.setLabelType(this.printOptions.labelType), - PacketGenerator.printStartV5(this.printOptions.totalPages, 0, 0) + PacketGenerator.printStartV5(this.printOptions.totalPages, 0, 0), ]); } override printPage(image: EncodedImage, quantity?: number): Promise<void> { this.checkAddPage(quantity ?? 1); - return this.abstraction.sendAll([ - PacketGenerator.pageStart(), - PacketGenerator.setPageSizeV4(image.rows, image.cols, quantity ?? 1, 0, false), - ...PacketGenerator.writeImageData(image, this.printheadPixels()), - PacketGenerator.pageEnd(), - ], this.printOptions.pageTimeoutMs); + return this.abstraction.sendAll( + [ + PacketGenerator.pageStart(), + PacketGenerator.setPageSizeV4(image.rows, image.cols, quantity ?? 1, 0, false), + ...PacketGenerator.writeImageData(image, { printheadPixels: this.printheadPixels() }), + PacketGenerator.pageEnd(), + ], + this.printOptions.pageTimeoutMs + ); } override waitForFinished(): Promise<void> { this.abstraction.setPacketTimeout(this.printOptions.statusTimeoutMs); - return this.abstraction.waitUntilPrintFinishedByStatusPoll( - this.printOptions.totalPages ?? 1, - this.printOptions.statusPollIntervalMs - ).finally(() => this.abstraction.setDefaultPacketTimeout()); + return this.abstraction + .waitUntilPrintFinishedByStatusPoll(this.printOptions.totalPages ?? 1, this.printOptions.statusPollIntervalMs) + .finally(() => this.abstraction.setDefaultPacketTimeout()); } }