Reputation: 195
I am currently struggling with our Ionic3 app. I can't seem to find out how to print an image using BT printer and Bluetooth Serial plugin. Printing text is just fine.
We are testing RPP02N-BU printer using this documentation (found by my ex-colleague) Driver Commands Documentation, but I can not get the
SELECT BIT IMAGE MODE
to work.
We first resize uploaded image to not exceed 300px and we convert it to Black and White. When printing, we iterate the image and create binary strings, which we then convert to bytes. Those bytes follow the SELECT BIT IMAGE MODE commands.
Our code (in TypeScript) is as follows:
/**
* Image to printer command
* @param image
*/
public static getImagePrintData(image: HTMLImageElement): Buffer {
// Initialize list of commands
let command: number[] = [ 0x1b, 0x2a, 33, 255, 3 ];
// Get image bytes
let bytes = this.getImageBytes(image);
// Add bytes to command
bytes.forEach((byte) => command.push(byte));
// Return command
return new Buffer(command);
}
/**
* Get image bytes
* @param image
*/
private static getImageBytes(image: HTMLImageElement): number[] {
// Init result
let result = [];
// Create image padding
let padding = this.getZeroBytePadding(image.width);
// Get image context
let ctx = this.getImageContext(image);
// Iterate image pixels
for (let y = 0; y < image.height; y++) {
// Init row string
let row = "";
// Go through row of pixels
for (let x = 0; x < image.width; x++) {
// Get pixel
let pixel = ctx.getImageData(x, y, 1, 1).data;
// Get rgb value
let rgbValue = pixel[0] + pixel[1] + pixel[2];
// Add 0 or 1 based on value
row = row + (rgbValue > 0 ? 0 : 1);
}
// Add padding
row = row + padding;
// Now we need to split the row into byte chunks
let byteChunks = row.match(/.{1,8}/g);
// Now add those byte chunks to result
byteChunks.forEach((chunk) => result.push(parseInt(chunk, 2)));
}
// Return result
return result;
}
/**
* Get zero byte padding
* @param value
*/
private static getZeroBytePadding(value: number): string {
// Init padding
let padding = "";
// Get difference
let diff = value % 8;
// Create padding
for (let i = 0; i < (8 - diff); i++) {
padding = padding + "0";
}
// Return result
return padding;
}
/**
* Get image context
* @param image
*/
private static getImageContext(image: HTMLImageElement): CanvasRenderingContext2D {
// Prepare canvas to draw image into
var canvas = document.createElement('canvas');
canvas.width = image.width;
canvas.height = image.height;
// Context
var ctx = canvas.getContext('2d');
ctx.drawImage(image, 0, 0);
return ctx;
}
Then the command is sent to BT printer:
// Initialize image
let image = new Image();
// Set data
image.src = options.logo.content;
// Get image print data
let imagePrintData = PrinterUtils.getImagePrintData(image);
// Push image to print queue
printQueue.push(this.bluetoothUtility.write(imagePrintData));
The resulting image looks like this:
And the original is:
Upvotes: 2
Views: 5692
Reputation: 195
UPDATE 9.6.2023
For anyone still struggling, we recently found this library: https://github.com/NielsLeenheer/EscPosEncoder
It does most of the work and also offers QR code and Barcode encoding. Hope that helps!
So I finally succeeded. I used parts of this code: https://github.com/song940/node-escpos and managed to print logo, even centered.
Following code is in TypeScript
I created ESCPOSImage class:
/**
* RGB interface
*/
interface IRGB {
r: number;
g: number;
b: number;
a: number;
}
/**
* ESCPOS image
*/
export class ESCPOSImage {
// Data
private data: number[] = [];
// Width
private width: number = 0;
// Height
private height: number = 0;
/**
* Constructor
* @param image
*/
constructor(image: HTMLImageElement) {
// Set width
this.width = image.width;
// Set height
this.height = image.height;
// Get image context
let ctx = this.getContext(image);
// Get data
this.data = this.getBitData(ctx);
}
/**
* Get bit data
* @param ctx
*/
private getBitData(ctx: CanvasRenderingContext2D): number[] {
// Init result
let result: number[] = [];
// Iterate rows
for (let y = 0; y < this.height; y++) {
// Iterate columns
for (let x = 0; x < this.width; x++) {
// Get pixel
let pixel = ctx.getImageData(x, y, 1, 1).data;
// Get rgb
let rgb = this.getRGB(pixel);
// Get rgb value
let value = rgb.r + rgb.g + rgb.b;
// Add bit to result
result.push(value > 0 ? 0 : 1);
}
}
// Return result
return result;
}
/**
* Get image context
* @param image
*/
private getContext(image: HTMLImageElement): CanvasRenderingContext2D {
// Create canvas
var canvas = document.createElement('canvas');
canvas.width = image.width;
canvas.height = image.height;
// Set context
var context = canvas.getContext('2d');
context.drawImage(image, 0, 0);
// Return context
return context;
}
/**
* Get RGB
* @param pixel
*/
private getRGB(pixel: any): IRGB {
// Return RGB
return {
r: pixel[0],
g: pixel[1],
b: pixel[2],
a: pixel[3]
}
}
/**
* To raster
*/
public toRaster() {
// Init result
let result = [];
// Get width and height
let width = this.width;
let height = this.height;
// N block lines
let n = Math.ceil(width / 8);
// Iterate
for (let y = 0; y < height; y++) {
for (let x = 0; x < n; x++) {
for (let b = 0; b < 8; b++) {
let i = x * 8 + b;
if (result[y * n + x] === undefined) {
result[y * n + x] = 0;
}
let c = x * 8 + b;
if (c < width) {
if (this.data[y * width + i]) {
result[y * n + x] += (0x80 >> (b & 0x7));
}
}
}
}
}
// Return result
return {
data: result,
width: n,
height: height
};
}
}
The class implements toRaster method, that is later used in ESCPOSPrinter class:
/**
* ESCPOS printer
*/
export class ESCPOSPrinter {
// Buffer
private buffer: Buffer;
/**
* Constructor
* @param buffer
*/
constructor(buffer: Buffer) {
// Init buffer
this.buffer = buffer;
}
/**
* Write buffer
* @param buffer
*/
private write(buffer: Buffer) {
this.buffer = Buffer.concat([this.buffer, buffer]);
}
/**
* Print raster
* @param image
* @param mode
*/
public raster(image: ESCPOSImage, mode: string = 'normal') {
// Get header
let header = COMMANDS.S_RASTER_N;
// Get raster
let raster = image.toRaster();
// Set alignment
this.align('center');
// Write header
this.write(new Buffer(header));
this.write(new Buffer([raster.width, 0]));
this.write(new Buffer([raster.height, 0]));
// Write data
this.write(new Buffer(raster.data));
}
/**
* Print line
*/
public printLn() {
this.write(new Buffer([COMMANDS.CTL_CR, COMMANDS.CTL_LF]));
}
/**
* Align
* @param alignment ['left', 'center', 'right']
*/
public align(alignment: string = 'left') {
// Create alignment dictionary
const aligments = {
['left']: COMMANDS.TXT_ALIGN_LT,
['center']: COMMANDS.TXT_ALIGN_CT,
['right']: COMMANDS.TXT_ALIGN_RT
}
// Write alignment
this.write(new Buffer(aligments[alignment]));
}
/**
* Get buffer
*/
public getBuffer(): Buffer {
return this.buffer;
}
}
And finally, all together used:
// Create image
let image = new Image();
// Set data
image.src = options.logo.content;
// Create ESCPOS image
let escposImage = new ESCPOSImage(image);
// Initialize ESCPOS printer
let escposPrinter = new ESCPOSPrinter(new Buffer([]));
// Print image
escposPrinter.raster(escposImage);
// And also print new line
escposPrinter.printLn();
// Assign data to print queue
printData = Buffer.concat([escposPrinter.getBuffer(), printData]);
The data is then divided into packets and sent using write method of Bluetooth Serial plugin.
If you wonder about the commands:
TXT_ALIGN_LT: [0x1b, 0x61, 0x00], // Left justification
TXT_ALIGN_CT: [0x1b, 0x61, 0x01], // Centering
TXT_ALIGN_RT: [0x1b, 0x61, 0x02], // Right justification
S_RASTER_N: [0x1d, 0x76, 0x30, 0x00], // Set raster image normal size
CTL_LF: [0x0a], // Print and line feed
CTL_CR: [0x0d], // Carriage return
Hope this helps someone. Cheers
Upvotes: 6
Reputation: 462
I was able to print to the escpos compatible printer using an external library.
Follow these Guides:
Upvotes: 0
Reputation: 462
Hi Filip Matys
I try to print your StarWars image with your class and having mixed results. From my understanding you try to reuse the node-escpos did you find a way to have access to all packags/printer/index.js fonctions?
Like:
printer
.font('a')
.align('ct')
.style('bu')
.size(1, 1)
.text('The quick brown fox jumps over the lazy dog')
.text('敏捷的棕色狐狸跳过懒狗')
.barcode('1234567', 'EAN8')
.table(["One", "Two", "Three"])
.tableCustom([
{ text:"Left", align:"LEFT", width:0.33 },
{ text:"Center", align:"CENTER", width:0.33},
{ text:"Right", align:"RIGHT", width:0.33 }
])
.qrimage('https://github.com/song940/node-escpos', function(err){
this.cut();
this.close();
});
Upvotes: 0