Skip to content

Commit

Permalink
For Xprinter brand printer
Browse files Browse the repository at this point in the history
- Add printer model "xprinter"
- Add util function to print CODE128 barcode on Xprinter
- Add util function to optimize CODE128 barcode length
  • Loading branch information
Xelio Cheong committed Dec 5, 2023
1 parent 46b687f commit ce67387
Show file tree
Hide file tree
Showing 3 changed files with 140 additions and 4 deletions.
10 changes: 8 additions & 2 deletions packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export interface PrinterOptions {
width?: number | undefined
}

export type PrinterModel = null | "qsprinter";
export type PrinterModel = null | "qsprinter" | "xprinter";

/**
* 'dhdw', 'dwh' and 'dhw' are treated as 'dwdh'
Expand Down Expand Up @@ -642,7 +642,13 @@ export class Printer<AdapterCloseArgs extends []> extends EventEmitter {
if (type === "CODE128" || type === "CODE93")
codeLength = utils.codeLength(convertCode);

this.buffer.write(`${codeLength + convertCode + (options.includeParity ? parityBit : "")}\x00`); // Allow to skip the parity byte
if ((this._model === "xprinter") && type === "CODE128") {
const code128Data = utils.genCode128forXprinter(convertCode);
this.buffer.write(code128Data);
} else {
this.buffer.write(`${codeLength + convertCode + (options.includeParity ? parityBit : "")}\x00`); // Allow to skip the parity byte
}

if (this._model === "qsprinter")
this.buffer.write(_.MODEL.QSPRINTER.BARCODE_MODE.OFF);

Expand Down
54 changes: 53 additions & 1 deletion packages/core/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,56 @@ export function intLowHighHex(input: number, length: number = 1): string {
input = Math.floor(input / 256);
}
return ret;
}
}

const SPLIT_REGEX = /(^(?:\d\d){2,})|((?<=\D)(?:(?:\d\d){3,})(?=\d?\D))|((?:\d\d){2,}$)/;
// /(--beginning--)|--------(------middle--)----------|(-----end-----)/
/**
* Helper function for barcode type CODE128, split string into blocks to optimize barcode length
* Refer to following Wikipedia page
* https://en.wikipedia.org/wiki/Code_128#Barcode_length_optimization_by_Code_128_Type-C
* @param {[string]} input
* @returns {[string[]]} blocks of string for optimized CODE128 barcode length
*/
export function splitForCode128(str: string): string[] {
// No need to match REGEX for short string
if(str.length <= 4)
return [str];

// Find 4+ consecutive and even number of digits at the beginning or end
// Find 6+ consecutive and even number of digits at the middle
//
// "12345ABC2345678BCD3456789CDE98765" =>
// ["1234","5ABC","234567","8BCD","345678","9CDE9","8765"]
return str.split(SPLIT_REGEX).filter(s => s !== "" && s !== undefined);
}

const USE_CODEC_REGEX = /^((?:\d\d){1,})$/;
/**
* Generate control code to print CODE128 on Xprinter brand printer
* Barcode length is optimized
* Documentation (Chinese only):
* https://www.xprinter.net/companyfile/1/
* @param {[string]} input
* @returns {[string]} control code for printing
*/
export function genCode128forXprinter(barcode:string): string {
const toCodeC = (s:string) =>
"{C" + s.match(/\d{2}/g) // split every 2 digit
?.map(num => String.fromCharCode(Number(num)))
?.join('');

const toCodeB = (s:string) => "{B" + s.replace('{','{{');

const blocks = splitForCode128(barcode)
const dataPart = blocks.map(block => USE_CODEC_REGEX.test(block) ? toCodeC(block) : toCodeB(block))
.join('');
const dataLength = dataPart.length;

if (dataLength > 255)
throw new Error("Barcode data is too long");

return String.fromCharCode(dataLength) + dataPart;
}


80 changes: 79 additions & 1 deletion packages/core/test/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,85 @@
import { describe, expect, it } from 'vitest'
import { splitForCode128, genCode128forXprinter } from '../src/utils';

describe('should', () => {
it('exported', () => {
expect(1).toEqual(1)
})
});

it('Split string to optimized blocks for CODE128 barcode', () => {
const inputs: string[] = [];
const expected: string[][] = [];

inputs.push("12");
expected.push(["12"]);

inputs.push("123");
expected.push(["123"]);

inputs.push("1234");
expected.push(["1234"]);

inputs.push("12345");
expected.push(["1234", '5']);

inputs.push("123456789");
expected.push(["12345678","9"]);

inputs.push("123A4567C");
expected.push(["123A4567C"]);

inputs.push("1234A4567C");
expected.push(["1234","A4567C"]);

inputs.push("AAA1234");
expected.push(["AAA","1234"]);

inputs.push("AAA12345");
expected.push(["AAA1","2345"]);

inputs.push("4321AAA1234");
expected.push(["4321","AAA","1234"]);

inputs.push("AAA123456BBB");
expected.push(["AAA","123456","BBB"]);

inputs.push("12345ABC2345678BCD3456789CDE98765");
expected.push(["1234","5ABC","234567","8BCD","345678","9CDE9","8765"]);

inputs.forEach((input,index) => {
expect(splitForCode128(input)).toEqual(expected[index]);
});
});

it('generate CODE128 barcode printing control code for Xprinter', () => {
const inputs: string[] = [];
const expected: string[] = [];

inputs.push("AB");
expected.push("\x04{BAB");

inputs.push("12");
expected.push("\x03{C\x0c");

inputs.push("123456789");
expected.push("\x09{C\x0c\x22\x38\x4e{B9");

inputs.push("1234ABC");
expected.push("\x09{C\x0c\x22{BABC");

inputs.push("ABC1234");
expected.push("\x09{BABC{C\x0c\x22");

inputs.push("AAA123456BBB");
expected.push("\x0f{BAAA{C\x0c\x22\x38{BBBB");

inputs.push("12345ABC2345678BCD3456789CDE98765");
expected.push("\x25{C\x0c\x22{B5ABC{C\x17\x2d\x43{B8BCD{C\x22\x38\x4e{B9CDE9{C\x57\x41");

inputs.forEach((input,index) => {
expect(genCode128forXprinter(input)).toEqual(expected[index]);
});

});
})

0 comments on commit ce67387

Please sign in to comment.