node-thermal-printer
Version:
Print on Epson, Star, Tranca, Daruma, Brother and Custom thermal printers with Node.js
431 lines (379 loc) • 19.5 kB
JavaScript
const PrinterType = require('./printer-type');
class Star extends PrinterType {
constructor () {
super();
this.config = require('./star-config');
}
// ------------------------------ Append ------------------------------
append (appendBuffer) {
if (this.buffer) {
this.buffer = Buffer.concat([this.buffer, appendBuffer]);
} else {
this.buffer = appendBuffer;
}
}
// ------------------------------ QR ------------------------------
printQR (str, settings) {
this.buffer = null;
if (!settings) {
settings = {};
}
const config = {
model: this.config.QRCODE_MODEL1,
correctionLevel: this.config.QRCODE_CORRECTION_M,
cellSize: this.config.QRCODE_CELLSIZE_4,
};
const models = {
1: this.config.QRCODE_MODEL1,
2: this.config.QRCODE_MODEL2,
};
const correctionLevels = {
L: this.config.QRCODE_CORRECTION_L, // Correction level: L - 7%
M: this.config.QRCODE_CORRECTION_M, // Correction level: M - 15%
Q: this.config.QRCODE_CORRECTION_Q, // Correction level: Q - 25%
H: this.config.QRCODE_CORRECTION_H, // Correction level: H - 30%
};
const cellSizes = {
1: this.config.QRCODE_CELLSIZE_1, // Cell size 1
2: this.config.QRCODE_CELLSIZE_2, // Cell size 2
3: this.config.QRCODE_CELLSIZE_3, // Cell size 3
4: this.config.QRCODE_CELLSIZE_4, // Cell size 4
5: this.config.QRCODE_CELLSIZE_5, // Cell size 5
6: this.config.QRCODE_CELLSIZE_6, // Cell size 6
7: this.config.QRCODE_CELLSIZE_7, // Cell size 7
8: this.config.QRCODE_CELLSIZE_8, // Cell size 8
};
if (models[settings.model]) config.model = models[settings.model];
if (correctionLevels[settings.correctionLevel]) config.correctionLevel = correctionLevels[settings.correctionLevel];
if (cellSizes[settings.cellSize]) config.cellSize = cellSizes[settings.cellSize];
// [Name] Set QR code model
// [Code] Hex. 1B 1D 79 53 30 n
// [Defined Area] 1 ≤ n ≤ 2
// [Initial Value] n = 2
// [Function] Sets the model.
// • Parameter details
// n | Set Model
// ---+---------------
// 1 | Model 1
// 2 | Model 2
this.append(config.model);
// [Name] Set QR code mistake correction level
// [Code] Hex. 1B 1D 79 53 31 n
// [Defined Area] 0 ≤ n ≤ 3
// [Initial Value] n = 0
// [Function] Sets the mistake correction level.
// • Parameter details
// n | Correction Level | Mistake Correction Rate (%)
// --+------------------+----------------------------
// 0 | L | 7
// 1 | M | 15
// 2 | Q | 25
// 3 | H | 30
this.append(config.correctionLevel);
// [Name] Set QR code cell size
// [Code] Hex. 1B 1D 79 53 32 n
// [Defined Area] 1 ≤ n ≤ 8
// [Initial Value] n = 3
// [Function] Sets the cell size.
// • Parameter details
// • n: Cell size (Units: Dots)
// • It is recommended that the specification using this command be 3 ≤ n.
// If n = 1 or 2, check by actually using.
this.append(config.cellSize);
// [Name] Set QR code cell size (Auto Setting)
// [Code] Hex. 1B 1D 79 44 31 m nL nH d1 d2 … dk
// [Defined Area]
// m = 0
// 0 ≤ nL ≤ 255,
// 0 ≤ nH ≤ 255
// 1 ≤ nL + nH x 256 ≤ 7089 (k = nL + nH x 256)
// 0 ≤ d ≤ 255
// [Function]
// Automatically expands the data type of the bar code and sets the data.
// • Parameter details
// • nL + nH x 256: Byte count of bar code data
// • dk: Bar code data (Max. 7089 bytes)
// • When using this command, the printer receives data for the number of bytes (k) specified by nL and nH.
// The data automatically expands to be set as the qr code data.
// • Indicates the number bytes of data specified by the nL and nH. Bar code data is cleared at this time.
// • The data storage region of this command is shared with the manual setting command so data is updated
// each time either command is executed.
const s = str.length;
const lsb = parseInt(s % 256);
const msb = parseInt(s / 256);
this.append(Buffer.from([lsb, msb])); // nL, nH
this.append(Buffer.from(str.toString())); // Data
this.append(Buffer.from([0x0a])); // NL (new line)
// [Name] Print QR code
// [Code] 1B 1D 79 50
// [Function] Prints bar code data.
// When receiving this command, if there is unprinted data in the image buffer,
// the printer will print the bar code after printing the unprinted print data.
// A margin of more than 4 cells is required around the QR code. The user should ensure that space.
// Always check printed bar codes in actual use.
this.append(this.config.QRCODE_PRINT);
return this.buffer;
}
// ------------------------------ PDF417 ------------------------------
pdf417 (data, settings) {
this.buffer = null;
if (settings) {
throw new Error('PDF417 settings not yet available for star printers!');
}
// (1) Bar code type setting (<ESC> <GS> “x” “S”)
// (2) Bar code data setting (<ESC> <GS> “x” “D”)
// (3) Bar code printing (<ESC> <GS> “x” “P”)
// (4) Bar code expansion information acquisition (<ESC> <GS> “x” “I”)
// Set PDF417 bar code size
// 1B 1D 78 53 30 n p1 p2
this.append(Buffer.from([0x1b, 0x1d, 0x78, 0x53, 0x30, 0x00, 0x01, 0x02]));
// Set PDF417 ECC (security level)
// 1B 1D 78 53 31 n
this.append(Buffer.from([0x1b, 0x1d, 0x78, 0x53, 0x31, 0x02]));
// Set PDF417 module X direction size
// 1B 1D 78 53 32 n
this.append(Buffer.from([0x1b, 0x1d, 0x78, 0x53, 0x32, 0x02]));
// Set PDF417 module aspect ratio
// 1B 1D 78 53 33 n
this.append(Buffer.from([0x1b, 0x1d, 0x78, 0x53, 0x33, 0x03]));
// Set PDF417 bar code data
// 1B 1D 78 44 nL nH d1 d2 … dk
const s = data.length;
const lsb = parseInt(s % 256);
const msb = parseInt(s / 256);
this.append(Buffer.from([0x1b, 0x1d, 0x78, 0x44]));
this.append(Buffer.from([lsb, msb])); // nL, nH
this.append(Buffer.from(data.toString())); // Data
this.append(Buffer.from([0x0a])); // NL (new line)
// Print PDF417 bar code
// 1B 1D 78 50
this.append(Buffer.from([0x1b, 0x1d, 0x78, 0x50]));
return this.buffer;
}
// ------------------------------ CODE128 ------------------------------
code128 (data, settings) {
this.buffer = null;
this.append(this.config.BARCODE_CODE128);
// Barcode option
// 1 - No text
// 2 - Text on bottom
// 3 - No text inline
// 4 - Text on bottom inline
if (settings) {
if (settings.text == 1) this.append(this.config.BARCODE_CODE128_TEXT_1);
else if (settings.text == 2) this.append(this.config.BARCODE_CODE128_TEXT_2);
else if (settings.text == 3) this.append(this.config.BARCODE_CODE128_TEXT_3);
else if (settings.text == 4) this.append(this.config.BARCODE_CODE128_TEXT_4);
} else {
this.append(this.config.BARCODE_CODE128_TEXT_2);
}
// Barcode width
// 31 - Small
// 32 - Medium
// 33 - Large
if (settings) {
if (settings.width == 'SMALL') this.append(this.config.BARCODE_CODE128_WIDTH_SMALL);
else if (settings.width == 'MEDIUM') this.append(this.config.BARCODE_CODE128_WIDTH_MEDIUM);
else if (settings.width == 'LARGE') this.append(this.config.BARCODE_CODE128_WIDTH_LARGE);
} else {
this.append(this.config.BARCODE_CODE128_WIDTH_LARGE);
}
// Barcode height
if (settings && settings.height) this.append(Buffer.from([settings.height]));
else this.append(Buffer.from([0x50]));
// Barcode data
this.append(Buffer.from(data.toString()));
// Append RS(record separator)
this.append(Buffer.from([0x1e]));
return this.buffer;
}
// ----------------------------------------------------- PRINT IMAGE -----------------------------------------------------
async printImage (image) {
const fs = require('fs');
const { PNG } = require('pngjs');
try {
const data = fs.readFileSync(image);
const png = PNG.sync.read(data);
const buff = this.printImageBuffer(png.width, png.height, png.data);
return buff;
} catch (error) {
throw error;
}
}
printImageBuffer (width, height, data) {
this.buffer = null;
// Get pixel rgba in 2D array
const pixels = [];
for (let i = 0; i < height; i++) {
const line = [];
for (let j = 0; j < width; j++) {
const idx = (width * i + j) << 2;
line.push({
r: data[idx],
g: data[idx + 1],
b: data[idx + 2],
a: data[idx + 3],
});
}
pixels.push(line);
}
this.append(Buffer.from([0x1b, 0x30]));
// v3
for (let i = 0; i < Math.ceil(height / 24); i++) {
let imageBuffer = Buffer.from([]);
for (let y = 0; y < 24; y++) {
for (let j = 0; j < Math.ceil(width / 8); j++) {
let byte = 0x0;
for (let x = 0; x < 8; x++) {
if ((i * 24 + y < pixels.length) && (j * 8 + x < pixels[i * 24 + y].length)) {
const pixel = pixels[i * 24 + y][j * 8 + x];
if (pixel.a > 126) { // checking transparency
const grayscale = parseInt(0.2126 * pixel.r + 0.7152 * pixel.g + 0.0722 * pixel.b);
if (grayscale < 128) { // checking color
const mask = 1 << 7 - x; // setting bitwise mask
byte |= mask; // setting the correct bit to 1
}
}
}
}
imageBuffer = Buffer.concat([imageBuffer, Buffer.from([byte])]);
}
}
this.append(Buffer.from([0x1b, 0x6b, parseInt(imageBuffer.length / 24), 0x00]));
this.append(imageBuffer);
this.append(Buffer.from('\n'));
}
this.append(Buffer.from([0x1b, 0x7a, 0x01]));
return this.buffer;
}
// ------------------------------ BARCODE ------------------------------
printBarcode (data, type, settings) {
this.buffer = null;
if (!settings) {
settings = {};
}
// [Name] ESC b n1 n2 n3 n4 d1...dk RS
// [Code] Hex. 1B 62 n1 n2 n3 n4 d1 ... dk 1E
// [Defined Area]
// n1 (Barcode type): 0≤n1≤8, 48≤n1≤56 (0≤n1≤8)
// n2 (Barcode under-bar): 1≤n2≤4, 49≤n2≤52 (1≤n2≤4)
// n3 (Barcode mode),
// n4 (Barcode height) 1≤n4≤255
// d (Barcode data),
// k (Barcode data count) definitions differ according to the type of barcode.
// RS
// [Function]
// Barcode printing is executed according to the following parameters.
// If n1, n2, n3 and n4 are acquired and detected to be out of the defined area, data up to RS is discarded.
this.append(Buffer.from([0x1b, 0x62]));
// n1 - Barcode type selection
// +-----------------------+
// | n1 | Barcode type |
// |-----------------------|
// | 0, 48 | UPC-E |
// | 1, 49 | UPC-A |
// | 2, 50 | JAN/EAN8 |
// | 3, 51 | JAN/EAN13 |
// | 4, 52 | Code39 |
// | 5, 53 | ITF |
// | 6, 54 | Code128 |
// | 7, 55 | Code93 |
// | 8, 56 | NW-7 |
// +-----------------------+
this.append(Buffer.from([type || 7]));
// n2 - Under-bar character selection and added line feed selection
// +--------------------------------------------------------------------------------------------+
// | n2 | Selection |
// |--------------------------------------------------------------------------------------------|
// | 1, 49 | No added under-bar charactersExecutes line feed after printing a bar code |
// | 2, 50 | Adds under-bar characters Executes line feed after printing a bar code |
// | 3, 51 | No added under-bar charactersDoes not execute line feed after printing a bar code |
// | 4, 52 | Adds under-bar characters Does not execute line feed after printing a bar code |
// +--------------------------------------------------------------------------------------------+
this.append(Buffer.from([settings.characters || 1]));
// n3 - Barcode mode selection
// +-------------------------------------------------------------------------------------------+
// | n3 | Bar code type |
// | +-----------------------------------------------------------------------------------+
// | | UPC-E, UPC-A, JAN/EAN8 | Code39, NW-7 | ITF |
// | | JAN/EAN13, Code128, Code93 | | |
// +-------------------------------------------------------------------------------------------+
// | 1, 49 | Minimum module 2 dots | Narrow: Wide = 2:6 dots | Narrow: Wide = 2:5 dots |
// | 2, 50 | Minimum module 3 dots | Narrow: Wide = 3:9 dots | Narrow: Wide = 4:10 dots |
// | 3, 51 | Minimum module 4 dots | Narrow: Wide = 4:12 dots | Narrow: Wide = 6:15 dots |
// | 4, 52 | - - - | Narrow: Wide = 2:5 dots | Narrow: Wide = 2:4 dots |
// | 5, 53 | - - - | Narrow: Wide = 3:8 dots | Narrow: Wide = 4:8 dots |
// | 6, 54 | - - - | Narrow: Wide = 4:10 dots | Narrow: Wide = 6:12 dots |
// | 7, 55 | - - - | Narrow: Wide = 2:4 dots | Narrow: Wide = 2:6 dots |
// | 8, 56 | - - - | Narrow: Wide = 3:6 dots | Narrow: Wide = 3:9 dots |
// | 9, 57 | - - - | Narrow: Wide = 4:8 dots | Narrow: Wide = 4:12 dots |
// +-------------------------------------------------------------------------------------------+
this.append(Buffer.from([settings.mode || 2]));
// n4 - Barcode height (dot count)
// +-------------------------------------------------------------------------------------------------------+
// | Specification A | Specification B |
// +-------------------------------------------------------------------------------------------------------+
// | When the height of the bar code is more than | Form feed at (Bar code height + underbar characters) |
// | the form feed amount, the form feed amount is | |
// | automatically doubled. | |
// +-------------------------------------------------------------------------------------------------------+
this.append(Buffer.from([settings.height || 150]));
// d - Barcode data
// +----------------------------------------------------------------------------------------------------------------+
// | Bar code type | Defined area of k | Defined area of d |
// +----------------------------------------------------------------------------------------------------------------+
// | UPC-E | 11≤k≤12 | 48≤d≤57 (”0”≤d≤”9”) |
// | UPC-A | 11≤k≤12 | 48≤d≤57 (”0”≤d≤”9”) |
// | JAN/EAN8 | 7≤k≤8 | 48≤d≤57 (”0”≤d≤”9”) |
// | JAN/EAN13 | 12≤k≤13 | 48≤d≤57 (”0”≤d≤”9”) |
// | Code39 | 1≤k | 48≤d≤57 (”0”≤d≤”9”) |
// | | | 65≤d≤90 (”A”≤d≤”Z”) |
// | | | 32, 36, 37, 43, 45, 46, 47 (SP, ”$”, ”%”, ”+”, ”-“, ”.”, ”/”) |
// | ITF | 1≤k | 48≤d≤57 (“0”≤d≤”9”) |
// | | When an odd number: 0 is | |
// | | automatically applied to the | |
// | | top. | |
// | Code128 | 1≤k | 0≤d≤127 |
// | Code93 | 1≤k | 0≤d≤127 |
// | NW-7 | 1≤k | 48≤d≤57 (”0”≤d≤”9”) |
// | | | 65≤d≤68 (”A”≤d≤”D”) |
// | | | 36, 43, 45, 46, 47, 58 (”$”, ”+”, ”-“, ”.”, ”/”, ”:”) |
// | | | 97, 98, 99, 100 (”a”, ”b”, ”c”, ”d”) |
// +----------------------------------------------------------------------------------------------------------------+
this.append(Buffer.from(data));
// k - Barcode data count
// • UPC – E: k = 11 (or 12)
// The 12th check digit is automatically applied, so it is specified and ignored.
// The command is ignored for data that cannot be shortened.
// Automatically converts data to shortened form.
//
// • UPC – A: k = 11 (or 12)
// The 12th check digit is automatically applied, so it is specified and ignored.
//
// • JAN/EAN – 8: k = 7 (or 8)
// The 8th check digit is automatically applied, so it is specified and ignored.
//
// • JAN/EAN -13: k = 12 (or 13)
// The 13th check digit cannot be automatically applied, so it is specified and ignored.
//
// • CODE 39: k is freely set, and maximum value differs according to the mode.
// Start/stop code (“*”) is automatically applied.
//
// • ITF: k is freely set, and maximum value differs according to the mode.
// If data is oddly numbered, a 0 is applied to the top.
//
// • CODE 128: k is freely set, and maximum value differs according to the mode and the print character type.
// The check character is automatically applied.
//
// • CODE 93: k is freely set, and maximum value differs according to the mode and the print character type.
// The check character (“□”) is automatically applied.
//
// • NW7: k is freely set, and maximum value differs according to the mode and the print character type.
// NOTE: Not needed.
// this.append(Buffer.from([data.length]));
// Start/stop codes included in the data (not automatically applied).
this.append(Buffer.from([0x1e]));
return this.buffer;
}
}
module.exports = Star;