barcode
Version:
Generate 1D and 2D barcodes
152 lines (129 loc) • 5.79 kB
JavaScript
/*
* barcode generator - code128
*/
var gm = require('gm');
const BARMAP = [
[], [2,2,2,1,2,2], [2,2,2,2,2,1], [1,2,1,2,2,3], [1,2,1,3,2,2],
[], [1,2,2,2,1,3], [1,2,2,3,1,2], [1,3,2,2,1,2], [2,2,1,2,1,3],
[], [2,3,1,2,1,2], [1,1,2,2,3,2], [1,2,2,1,3,2], [1,2,2,2,3,1],
[], [1,2,3,1,2,2], [1,2,3,2,2,1], [2,2,3,2,1,1], [2,2,1,1,3,2],
[], [2,1,3,2,1,2], [2,2,3,1,1,2], [3,1,2,1,3,1], [3,1,1,2,2,2],
[], [3,2,1,2,2,1], [3,1,2,2,1,2], [3,2,2,1,1,2], [3,2,2,2,1,1],
[], [2,1,2,3,2,1], [2,3,2,1,2,1], [1,1,1,3,2,3], [1,3,1,1,2,3],
[], [1,1,2,3,1,3], [1,3,2,1,1,3], [1,3,2,3,1,1], [2,1,1,3,1,3],
[], [2,3,1,3,1,1], [1,1,2,1,3,3], [1,1,2,3,3,1], [1,3,2,1,3,1],
[], [1,1,3,3,2,1], [1,3,3,1,2,1], [3,1,3,1,2,1], [2,1,1,3,3,1],
[], [2,1,3,1,1,3], [2,1,3,3,1,1], [2,1,3,1,3,1], [3,1,1,1,2,3],
[], [3,3,1,1,2,1], [3,1,2,1,1,3], [3,1,2,3,1,1], [3,3,2,1,1,1],
[], [2,2,1,4,1,1], [4,3,1,1,1,1], [1,1,1,2,2,4], [1,1,1,4,2,2],
[], [1,2,1,4,2,1], [1,4,1,1,2,2], [1,4,1,2,2,1], [1,1,2,2,1,4],
[], [1,2,2,1,1,4], [1,2,2,4,1,1], [1,4,2,1,1,2], [1,4,2,2,1,1],
[], [2,2,1,1,1,4], [4,1,3,1,1,1], [2,4,1,1,1,2], [1,3,4,1,1,1],
[], [1,2,1,1,4,2], [1,2,1,2,4,1], [1,1,4,2,1,2], [1,2,4,1,1,2],
[], [4,1,1,2,1,2], [4,2,1,1,1,2], [4,2,1,2,1,1], [2,1,2,1,4,1],
[], [4,1,2,1,2,1], [1,1,1,1,4,3], [1,1,1,3,4,1], [1,3,1,1,4,1],
[], [1,1,4,3,1,1], [4,1,1,1,1,3], [4,1,1,3,1,1], [1,1,3,1,4,1],
[], [3,1,1,1,4,1], [4,1,1,1,3,1], [2,1,1,4,1,2], [2,1,1,2,1,4],
[], [2,3,3,1,1,1,2], [2,1,1,1,3,3]
];
const CODEMAPS = {
'A': [
' ', '!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.',
'/', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=',
'>', '?', '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[',
'\\', ']', '^', '_', 'NUL', 'SOH', 'STX', 'ETX', 'EOT', 'ENQ', 'ACK', 'BEL',
'BS', 'HT', 'LF', 'VT', 'FF', 'CR', 'SO', 'SI', 'DLE', 'DC1', 'DC2', 'DC3',
'DC4', 'NAK', 'SYN', 'ETB', 'CAN', 'EM', 'SUB', 'ESC', 'FS', 'GS', 'RS', 'US',
'FNC3', 'FNC3', 'SHIFTB', 'CODEC', 'CODEB', 'FNC4', 'FNC1'
],
'B': [
' ', '!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.',
'/', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=',
'>', '?', '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[',
'\\', ']', '^', '_', '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y',
'z', '{', '|', '}', '~', 'DEL', 'FNC3', 'FNC2', 'SHIFTA', 'CODEC', 'FNC4',
'CODEA', 'FNC1'
],
'C': [
'00', '01', '02', '03', '04', '05', '06', '07', '08', '09',
'10', '11', '12', '13', '14', '15', '16', '17', '18', '19',
'20', '21', '22', '23', '24', '25', '26', '27', '28', '29',
'30', '31', '32', '33', '34', '35', '36', '37', '38', '39',
'40', '41', '42', '43', '44', '45', '46', '47', '48', '49',
'50', '51', '52', '53', '54', '55', '56', '57', '58', '59',
'60', '61', '62', '63', '64', '65', '66', '67', '68', '69',
'70', '71', '72', '73', '74', '75', '76', '77', '78', '79',
'80', '81', '82', '83', '84', '85', '86', '87', '88', '89',
'90', '91', '92', '93', '94', '95', '96', '97', '98', '99',
'CODEB', 'CODEA', 'FNC1',
]
};
const STARTA = BARMAP[103];
const STARTB = BARMAP[104];
const STARTC = BARMAP[105];
const STOP = BARMAP[106];
const START = {
'A': STARTA,
'B': STARTB,
'C': STARTC
};
function getVal(mode, ch) {
return i = mode.toUpperCase() in CODEMAPS &&
CODEMAPS[mode.toUpperCase()].indexOf(ch) ||
0;
}
module.exports.createCode = function (options, callback) {
if (!options.data) {
return callback(new Error('No data given'), {});
}
if (!options.w || !options.h) {
return callback(new Error('Width and Height must be non-zero'), {});
}
// @todo refactor ------------------------------------------------------------
// we want to detect and optimize the best mode switching for the shortest
// code and the best space utilization.
var mode = options.mode || 'B';
// START + CHECK + END + DATA
var baseWidth = 11 + 11 + 13 + options.data.length * 11;
var barWidth = options.w / baseWidth >> 0;
if (!barWidth) {
return callback(new Error('Width will result in a degenerate barcode'), {});
}
// encode
var encoded = [ START[mode] ];
var checkSum = (mode == 'A') ? 103 : (mode == 'B' ? 104 : 105);
for(var i in options.data) {
i = parseInt(i); // shoudn't i already be a number? It's not.
var val = getVal(mode, options.data[i] );
encoded.push( BARMAP[val] );
checkSum += (i + 1) * val;
}
// add on check digit
encoded.push(BARMAP[ checkSum % 103 ]);
encoded.push(STOP);
// @todo end -----------------------------------------------------------------
var img = gm(options.w, options.h, options.bgcolor),
count = 0,
pos = 0,
w;
img.stroke(options.barcolor, 1);
pos = (options.w - baseWidth * barWidth) / 2 >> 0;
for (var i in encoded) {
for (var k in encoded[i]) {
w = encoded[i][k] * barWidth;
// only draw even indecies
if ( ! (k % 2) ) {
for (var m = 0; m < w; m++) {
img.drawLine(pos + m, 0, pos + m, options.h);
}
}
pos += w;
}
}
img.stream(options.type, function (err, stdout, stderr) {
callback(err, stdout);
});
};