Files
TeleSec/assets/static/qrcode/barcode.js
2025-08-27 13:47:23 +02:00

1033 lines
53 KiB
JavaScript

/** 2D barcode symbol creation by javascript
* @author alois zingl
* @version V2.2 June 2021
* @license MIT copyright: open-source software
* @link https://zingl.github.io/
* @description the indention of this library is a short and easy implementation to create the 2D barcodes
* of Data Matrix, QR, Aztec or PDF417 symbols so it could be easily adapted for individual requirements.
* ALL return the smallest possible barcode fitting the data as array matrix
* which could be converted to SVG path, html/css, canvas or GIF image.
* functions:
* datamatrix(text,rect) create Data Matrix barcode
* quickresponse(text,level,ver) create QR and micro QR barcode
* aztec(text,sec,lay) create Aztec, compact Aztec and Aztec runes
* pdf417(text,level,cols,rows,type) create PDF417 barcode
* code128(text) create Code 128 barcode
* toPath(mat) convert array matrix to SVG path
* toGif(mat,scale,trans,pad,rgb) convert array matrix to GIF image
* toHtml(mat,size,blocks) convert array matrix to html/css
* The functions have no dependency between, just copy the ones you need.
* 'Small is beautiful' - Leopold Kohr.
*/
"use strict";
/** Data Matrix symbol creation according ISO/IEC 16022:2006
* @param text to encode
* @param rect optional: flag - true for rectangular barcode
* @return matrix array of DataMatrix symbol ([] if text is too long)
*/
function datamatrix(text, rect) {
var enc = [], cw = 0, ce = 0; // byte stream
function push(val) { // encode bit stream c40/text/x12
cw = 40*cw+val;
if (ce++ == 2) { // full, add code
enc.push(++cw>>8); // 3 chars in 2 bytes
enc.push(cw&255);
ce = cw = 0;
}
}
var cost = [ // compute char cost in 1/12 bytes for mode..
function(c) { return ((c-48)&255) < 10 ? 6 : c < 128 ? 12 : 24 ; }, // ascii
function(c) { return ((c-48)&255) < 10 || ((c-65)&255) < 26 || c == 32 ? 8 : c < 128 ? 16 : 16+cost[1](c&127); }, // c40
function(c) { return ((c-48)&255) < 10 || ((c-97)&255) < 26 || c == 32 ? 8 : c < 128 ? 16 : 16+cost[2](c&127); }, // text
function(c) { return ((c-48)&255) < 10 || ((c-65)&255) < 26 || c == 32 || c == 13 || c == 62 || c == 42 ? 8 : 1e9; }, //x12
function(c) { return c >= 32 && c < 95 ? 9 : 1e9; }, // edifact
function(c) { return 12; } // base256
];
var latch = [0, 24, 24, 24, 21, 25]; // latch+unlatch costs
var count = [0, 12, 12, 12, 12, 25]; // actual costs (start by latch only)
var c, i, p, cm = 0, nm = 0; // current / next mode
var bytes = []; // cost table in 1/12 bytes
bytes[text.length] = count.slice(); // compute byte costs..
for (p = text.length; p-- > 0; ) { // ..by dynamic programming
for (c = 1e9, i = 0; i < count.length; i++ ) {
count[i] += cost[i](text.charCodeAt(p)); // accumulate costs from back
c = Math.min(c,Math.ceil(count[i]/12)*12); // get minimum in full bytes
} // ascii mode: if non digit round up to full byte
if (cost[0](text.charCodeAt(p)) > 6) count[0] = Math.ceil(count[0]/12)*12;
for (i = 0 ; i < count.length; i++) // latch to shorter mode?
if (c+latch[i] < count[i]) count[i] = c+latch[i];
bytes[p] = count.slice(); // record costs
}
for (p = 0; ; cm = nm) { // encode text
c = bytes[p][cm]-latch[cm];
if (p+[0,2,2,2,3,0][cm] >= text.length) nm = 0; // finished, return to ascii
else for (i = cost.length; i-- > 0; ) // check if a mode is shorter
if (Math.ceil((bytes[p+1][i]+cost[i](text.charCodeAt(p)))/12)*12 == c)
nm = i; // change to shorter mode
if (cm != nm && cm > 0) // return to ascii mode
if (cm < 4) enc.push(254); // unlatch c40/text/x12
else if (cm == 4) enc.push(31|cw&255); // unlatch edifact, add last byte
else { // encode base256 in 255 state rand algo
if (ce > 249) enc.push((ce/250+250+(149*(enc.length+1))%255)&255); // high
enc.push((ce%250+(149*(enc.length+1))%255+1)&255); // encode low length
for ( ;ce > 0; ce--) // encode base256 data
enc.push((text.charCodeAt(p-ce)+(149*(enc.length+1))%255+1)&255);
}
if (p >= text.length) break; // encoding finished
if (cm != nm) cw = ce = 0; // reset packing
if (cm != nm && nm > 0) // latch to c40/text/x12/edifact/base256
enc.push([230,239,238,240,231][nm-1]);
if (nm == 0) { // encode ascii
c = text.charCodeAt(p++); i = (c-48)&255;
if (i < 10 && p < text.length && ((text.charCodeAt(p)-48)&255) < 10)
enc.push(i*10+text.charCodeAt(p++)-48+130); // two digits
else {
if (c > 127) enc.push(235); // upper shift
enc.push((c&127)+1); // encode data
}
if (cm == 4 || ce < 0) ce--; // count post edifact chars
} else if (nm < 4) { // encode c40, text, x12
var set = [[31,0,32,119,47,133,57,179,64,173,90,207,95,277,127,386,255,1], // c40
[31,0,32,119,47,133,57,179,64,173,90,258,95,277,122,335,127,386,255,1], // text
[13,55,32,119,42,167,57,179,62,243,90,207,255,3]][nm-1]; // x12
do { // set contains character range dupels: upper value, shift*4+set-1
c = text.charCodeAt(p++);
if (c > 127) { push(1); push(30); c &= 127; } // upper shift
for (i = 0; c > set[i]; i += 2); // select char set
if ((set[i+1]&3) < 3) push(set[i+1]&3); // select set
push(c-(set[i+1]>>2));
} while (ce > 0);
} else if (nm == 4) { // encode edifact
if (ce > 0) enc.push(255&cw+(text.charCodeAt(p++)&63)); // 3rd byte
for (cw = ce = 0; ce < 3; ce++)
cw = 64*(cw+(text.charCodeAt(p++)&63));
enc.push(cw>>16); // 4 chars in 3 bytes
enc.push((cw>>8)&255);
} else { p++; ce++; } // count base256 chars
}
var el = enc.length; // compute symbol size
var h,w, nc = 1,nr = 1, fw,fh; // symbol size, regions, region size
var j = -1, l, r, s, b = 1, k;
if (ce == -1 || (cm && cm < 5)) nm = 1; // c40/text/x12/edifact unlatch removable
if (rect && el-nm < 50) { // rectangular symbol possible
k = [16,7, 28,11, 24,14, 32,18, 32,24, 44,28]; // symbol width, checkwords
do {
w = k[++j]; // width w/o finder pattern
h = 6+(j&12); // height
l = w*h/8; // # of bytes in symbol
} while (l-k[++j] < el-nm); // data + check fit in symbol?
if (w > 25) nc = 2; // column regions
} else { // square symbol
w = h = 6;
i = 2; // size increment
k = [5,7,10,12,14,18,20,24,28,36,42,48,56,68,84,
112,144,192,224,272,336,408,496,620]; // RS checkwords
do {
if (++j == k.length) return []; // message too long for Datamatrix
if (w > 11*i) i = 4+i&12; // advance increment
w = h += i;
l = (w*h)>>3;
} while (l-k[j] < el-nm); // data + check fit in symbol?
if (w > 27) nr = nc = 2*Math.floor(w/54)+2; // regions
if (l > 255) b = 2*(l>>9)+2; // blocks
}
s = k[j]; // RS checkwords
if (l-s+1 == el && nm > 0) { // remove last unlatch to fit in smaller symbol
el--; // replace edifact unlatch by char
if (ce == -1) enc[el-1] ^= 31^(enc[el]-1)&63;
}
fw = w/nc; fh = h/nr; // region size
if (el < l-s) enc[el++] = 129; // first padding
while (el < l-s) // add more padding
enc[el++] = (((149*el)%253)+130)%254;
s /= b; // compute Reed Solomon error detection and correction
var rs = new Array(70), rc = new Array(70); // reed/solomon code
var lg = new Array(256), ex = new Array(255); // log/exp table for multiplication
for (j = 1, i = 0; i < 255; i++) { // compute log/exp table of Galois field
ex[i] = j; lg[j] = i;
j += j; if (j > 255) j ^= 301; // GF polynomial a^8+a^5+a^3+a^2+1 = 100101101b = 301
}
for (rs[s] = 0, i = 1; i <= s; i++) // compute RS generator polynomial
for (j = s-i, rs[j] = 1; j < s; j++)
rs[j] = rs[j+1]^ex[(lg[rs[j]]+i)%255];
for (c = 0; c < b; c++) { // compute RS correction data for each block
for (i = 0; i <= s; i++) rc[i] = 0;
for (i = c; i < el; i += b)
for (j = 0, k = rc[0]^enc[i]; j < s; j++)
rc[j] = rc[j+1]^(k ? ex[(lg[rs[j]]+lg[k])%255] : 0);
for (i = 0; i < s; i++) // add interleaved correction data
enc[el+c+i*b] = rc[i];
}
// layout perimeter finder pattern
var mat = Array(h+2*nr).fill(null).map(function() {return [];});
for (i = 0; i < w+2*nc; i += fw+2) // vertical
for (j = 0; j < h; j++) {
mat[j+(j/fh|0)*2+1][i] = 1;
if ((j&1) == 1) mat[j+(j/fh|0)*2][i+fw+1] = 1;
}
for (i = 0; i < h+2*nr; i += fh+2) // horizontal
for (j = 0; j < w+2*nc; j++) {
mat[i+fh+1][j] = 1;
if ((j&1) == 0) mat[i][j] = 1;
}
// layout data
s = 2; c = 0; r = 4; // step,column,row of data position
for (i = 0; i < l; r -= s, c += s) { // diagonal steps
if (r == h-3 && c == -1) // corner A layout
k = [w,6-h, w,5-h, w,4-h, w,3-h, w-1,3-h, 3,2, 2,2, 1,2];
else if (r == h+1 && c == 1 && (w&7) == 0 && (h&7) == 6) // corner D layout
k = [w-2,-h, w-3,-h, w-4,-h, w-2,-1-h, w-3,-1-h, w-4,-1-h, w-2,-2, -1,-2];
else {
if (r == 0 && c == w-2 && (w&3)) continue; // corner B: omit upper left
if (r < 0 || c >= w || r >= h || c < 0) { // outside
s = -s; r += 2+s/2; c += 2-s/2; // turn around
while (r < 0 || c >= w || r >= h || c < 0) { r -= s; c += s; }
}
if (r == h-2 && c == 0 && (w&3)) // corner B layout
k = [w-1,3-h, w-1,2-h, w-2,2-h, w-3,2-h, w-4,2-h, 0,1, 0,0, 0,-1];
else if (r == h-2 && c == 0 && (w&7) == 4) // corner C layout
k = [w-1,5-h, w-1,4-h, w-1,3-h, w-1,2-h, w-2,2-h, 0,1, 0,0, 0,-1];
else if (r == 1 && c == w-1 && (w&7) == 0 && (h&7) == 6) continue; // omit corner D
else k = [0,0, -1,0, -2,0, 0,-1, -1,-1, -2,-1, -1,-2, -2,-2]; // nominal L-shape layout
}
for (el = enc[i++], j = 0; el > 0; j += 2, el >>= 1) { // layout each bit
if (el&1) {
var x = c+k[j], y = r+k[j+1];
if (x < 0) { x += w; y += 4-((w+4)&7); } // wrap around
if (y < 0) { y += h; x += 4-((h+4)&7); }
mat[y+2*(y/fh|0)+1][x+2*(x/fw|0)+1] = 1; // add region gap
}
}
}
for (i = w; i&3; i--) mat[i][i] = 1; // unfilled corner
return mat; // width and height of symbol
}
/** QR Code 2005 bar code symbol creation according ISO/IEC 18004:2006
* creates QR and micro QR bar code symbol as javascript matrix array.
* @param text to encode, can contain unicode chars (for kanji encoding)
* @param level optional: quality level LMQH
* @param ver optional: minimum version size (-3:M1, -2:M2, .. 1, .. 40), set to -3 for micro QR
* @return matrix array of QR symbol ([] if text is too long)
* needs kanji.js for unicode kanji encoding string
*/
function quickresponse(text, level, ver) { // create QR and micro QR bar code symbol
var mode, size, align, blk, ec;
var i, j, k, c, b, d, w, x, y, n;
var erc=[[2, 5, 6, 8, 7,10,15,20,26,18,20,24,30,18,20,24,26,30,22,24,28,30,28,28,28,28,30,30,26,28,30,30,30,30,30,30,30,30,30,30,30,30,30,30], // error correction words L
[99, 6, 8,10, 10,16,26,18,24,16,18,22,22,26,30,22,22,24,24,28,28,26,26,26,26,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28], // M
[99,99,99,14, 13,22,18,26,18,24,18,22,20,24,28,26,24,20,30,24,28,28,26,30,28,30,30,30,30,28,30,30,30,30,30,30,30,30,30,30,30,30,30,30], // Q
[99,99,99,99, 17,28,22,16,22,28,26,26,24,28,24,28,22,24,24,30,28,28,26,28,30,24,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30], // H
[ 1, 1, 1, 1, 1,1,1,1,1,2,2,2,2,4, 4, 4, 4, 4, 6, 6, 6, 6, 7, 8, 8, 9, 9,10,12,12,12,13,14,15,16,17,18,19,19,20,21,22,24,25], // error correction blocks L
[ 1, 1, 1, 1, 1,1,1,2,2,4,4,4,5,5, 5, 8, 9, 9,10,10,11,13,14,16,17,17,18,20,21,23,25,26,28,29,31,33,35,37,38,40,43,45,47,49], // M
[ 1, 1, 1, 1, 1,1,2,2,4,4,6,6,8,8, 8,10,12,16,12,17,16,18,21,20,23,23,25,27,29,34,34,35,38,40,43,45,48,51,53,56,59,62,65,68], // Q
[ 1, 1, 1, 1, 1,1,2,4,4,4,5,6,8,8,11,11,16,16,18,16,19,21,25,25,25,34,30,32,35,37,40,42,45,48,51,54,57,60,63,66,70,74,77,81] // H
]; // M1,M2,M3,M4,V1, 2, ..
var lev = 3-"HQMLhqml3210".indexOf(level||0)&3; // level "LMQH" to 0,1,2,3
var chars = [ "0123456789", // char table for numeric
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:", // alpha
String.fromCharCode.apply(null, Array.apply(null, {length: 127}).map(Number.call, Number)), // binary, >127 -> use utf-8
typeof kanji === "undefined" || kanji.length != 7973 ? "" : kanji ]; // kanji char index (in kanji.js)
function len(mod,chr) { // get encoding length in 1/6 bits
if (chars[mod].indexOf(chr) >= 0) return [20,33,48,78][mod];
return mod != 2 ? 1e9 : chr.charCodeAt(0) < 2048 ? 96 : 144; // two/three byte utf-8
}
function cib(mod) { // get # of bits of count indicator
return ver < 1 ? ver+((19-2*mod)/3|0) : // micro QR
[[10,12,14],[9,11,13],[8,16,16],[8,10,12]][mod][(ver+7)/17|0]; // QR
}
function push(val,bits) { // add data to bit stream
val <<= 8; eb += bits;
enc[enc.length-1] |= val>>eb;
while (eb > 7) enc[enc.length] = (val>>(eb -= 8))&255;
}
/** compute symbol version size, ver < 1: micro QR */
ver = isNaN(ver) ? 0 : ver-1;
do { // increase version till message fits
if (++ver >= erc[0].length-3) return []; // text too long for QR
if (ver < 2 || ver == 10 || ver == 27) { // recompute stream
var enc = [0], el, eb = 0; // encoding data, length, bits
var head = []; // calculate the bit table using dynamic programming:
for (j = 0; j < 4; j++) // www.nayuki.io/page/optimal-text-segmentation-for-qr-codes
head.push((Math.min(4,ver+3)+cib(j))*6); // segment head sizes
var bits = [[]], cost = head.slice(); // cost table in 1/6 bits
for (i = text.length; i-- > 0; ) { // data analysis
bits.unshift(cost.slice()); // record costs
for (j = 0; j < cost.length; j++) // accumulate costs from back
cost[j] += len(j,text.charAt(i));
b = Math.min.apply(null,cost);
for (j = 0; j < cost.length; j++) // switch to shorter encoding
cost[j] = Math.min(cost[j],((b+5)/6|0)*6+head[j]);
}
n = mode = cost.indexOf(b); // start encoding with mode of fewest bits
for (i = j = 0; j++ < text.length; ) { // calc optimal encoding for each char
[2,3,1,0].forEach(function(k) { // check binary, kanji, alpha, numeric mode
b = bits[j][k]+len(k,text.charAt(j))+5; // switch to shorter encoding
if (b < 1e7 && (mode == k || 6*(b/6|0) == bits[j-1][mode]-head[mode])) n = k;
});
if (mode != n || j == text.length) { // mode changes -> encode previous
if (ver < -1 && ver+3 < mode) push(0,50); // prevent illegal mode
if (ver > 0) push(1<<mode,4); // mode indicator, QR
else push(mode,ver+3); // mode indicator micro QR
b = unescape(encodeURIComponent(text.substring(i,j))); // to utf-8
push(mode == 2 ? b.length : j-i,cib(mode)); // character count indicator
if (mode == 0) { // encode numeric data
for (; i < j-2; i += 3)
push(text.substr(i,3),10); // 3 digits in 10 bits
if (i < j) push(text.substring(i,j),j-i == 1 ? 4 : 7);
} else if (mode == 1) { // encode alphanumeric data
for (; i < j-1; i += 2) // 2 chars in 11 bits
push(chars[1].indexOf(text.charAt(i))*45+chars[1].indexOf(text.charAt(i+1)),11);
if (i < j) push(chars[1].indexOf(text.charAt(i)),6);
} else if (mode == 2) // encode binary (utf-8)
for (i = 0; i < b.length; i++)
push(b.charCodeAt(i),8); // 1 char in 8 bits
else for (; i < j; i++) // encode kanji
push(chars[3].indexOf(text.charAt(i)),13); // 1 char in 13 bits
i = j; mode = n; // next segment
}
}
}
size = ver*(ver < 1 ? 2 : 4)+17; // symbol size
align = ver < 2 ? 0 : (ver/7|0)+2; // # of align patterns
el = (size-1)*(size-1)-(5*align-1)*(5*align-1); // total bits - align - timing
el -= ver < 1 ? 59 : ver < 2 ? 191 : ver < 7 ? 136 : 172; // finder, version, format
i = ver < 1 ? 8-(ver&1)*4 : 8; // M1+M3: last byte only one nibble
c = erc[lev+4][ver+3]*erc[lev][ver+3]; // # of error correction blocks/bytes
} while ((el&-8)-c*8 < enc.length*8+eb-i); // message fits in version
for (;(!level || (level.charCodeAt(0)&-33) == 65) && lev < 3; lev++) { // if level undefined or 'A'
j = erc[lev+5][ver+3]*erc[lev+1][ver+3]; // increase security level
if ((el&-8)-j*8 < enc.length*8+eb-i) break; // if data fits in same size
}
blk = erc[lev+4][ver+3]; // # of error correction blocks
ec = erc[lev][ver+3]; // # of error correction bytes
el = (el>>3)-ec*blk; // remaining data bytes
w = Math.floor(el/blk); // # of words in group 1 (group 2: w+1)
b = blk+w*blk-el; // # of blocks in group 1 (group 2: blk-b)
if ((-3&ver) == -3 && el == enc.length)
enc[w-1] >>= 4; // M1, M3: shift high bits to low nibble
if (el >= enc.length) push(0,ver > 0 ? 4 : ver+6); // terminator
if (eb == 0 || el < enc.length) enc.pop(); // bit padding
for (i = 236; el > enc.length; i ^= 236^17) // byte padding
enc.push((-3&ver) == -3 && enc.length == el-1 ? 0 : i); // M1, M3: last 4 bit zero
/** error correction coding */
var rs = new Array(ec+1); // reed/solomon code
var lg = new Array(256), ex = new Array(255); // log/exp table for multiplication
for (j = 1, i = 0; i < 255; i++) { // compute log/exp table of Galois field prime
ex[i] = j; lg[j] = i;
j += j; if (j > 255) j ^= 285; // GF polynomial a^8+a^4+a^3+a^2+1 = 100011101b = 285
}
for (i = 0, rs[0] = 1; i < ec; i++) // compute RS generator polynomial
for (j = i+1, rs[j] = 0; j > 0; j--)
rs[j] ^= ex[(lg[rs[j-1]]+i)%255];
for (i = 0; i <= ec*blk; i++) enc.push(0); // clr checkwords
for (k = c = 0, eb = el; c < blk; c++, eb += ec) // for each data block
for (i = c < b ? w : w+1; i-- > 0; k++) // compute RS checkwords
for (j = 0, x = enc[eb]^enc[k]; j++ < ec; )
enc[eb+j-1] = enc[eb+j]^(x ? ex[(lg[rs[j]]+lg[x])%255] : 0);
/** layout symbol */
var mat = Array(size).fill(null).map(function() {return [];});
function set(x,y,pat) { // layout fixed pattern: finder & align
for (var i = 0; i < pat.length; i++)
for (var p = pat[i], j = 0; 1<<j <= pat[0]; j++, p >>= 1)
mat[y+i][x+j] = (p&1)|2;
}
c = ver < 1 ? 0 : 6;
for (i = 8; i < size; i++) mat[c][i] = mat[i][c] = i&1^3; // timing pattern
set(0,0,[383,321,349,349,349,321,383,256,511]); // finder upper left +format
if (ver > 0) {
set(0,size-8,[256,383,321,349,349,349,321,383]); // finder lower left
set(size-8,0,[254,130,186,186,186,130,254,0,255]); // finder upper right
c = (ver+1)/(1-align)*4&-2; // alignment pattern spacing
for (x = 0; x < align; x++) // alignment grid
for (y = 0; y < align; y++)
if ((x > 0 && y > 0) || (x != y && x+y != align-1)) // no align at finder
set(x == 0 ? 4 : size-9+c*(align-1-x), // set alignment pattern
y == 0 ? 4 : size-9+c*(align-1-y), [31,17,21,17,31]);
if (ver > 6) // reserve version area
for (i = 0; i < 18; i++)
mat[size+i%3-11][i/3|0] = mat[i/3|0][size+i%3-11] = 2;
}
/** layout codewords */
y = x = size-1; // start lower right
for (i = 0; i < eb; i++) {
c = k = 0; j = w+1; // interleave data
if (i >= el) { c = k = el; j = ec; } // interleave checkwords
else if (i+blk-b >= el) c = k = -b; // interleave group 2 last bytes
else if (i%blk >= b) c = -b; // interleave group 2
else j--; // interleave group 1
c = enc[c+(i-k)%blk*j+((i-k)/blk|0)]; // interleave data
for (j = (-3&ver) == -3 && i == el-1 ? 8 : 128; j > 0; j >>= 1) { // M1,M3: 4 bit
if (c & j) mat[y][x] = 1; // layout bit
k = ver > 0 && x < 6 ? 1 : 0; // skip vertical timing pattern
do if (1 & x-- ^ k) { // advance x,y
if (size-x-k & 2) if (y > 0) y--; else continue; // top turn
else if (y < size-1) y++; else continue; // bottom turn
x += 2; // no turn
}
while (mat[y][x]&2); // skip reserved area
}
}
/** data masking */
var get = [ function(x,y) { return ((x+y|mat[y][x]>>1)^mat[y][x])&1^1; }, // pattern generation conditions
function(x,y) { return ((y|mat[y][x]>>1)^mat[y][x])&1^1; },
function(x,y) { return ((x%3>0|mat[y][x]>>1)^mat[y][x])&1^1; },
function(x,y) { return (((x+y)%3>0|mat[y][x]>>1)^mat[y][x])&1^1; },
function(x,y) { return ((x/3+(y>>1)|mat[y][x]>>1)^mat[y][x])&1^1; },
function(x,y) { return ((((x*y)&1)+x*y%3>0|mat[y][x]>>1)^mat[y][x])&1^1; },
function(x,y) { return ((x*y+x*y%3|mat[y][x]>>1)^mat[y][x])&1^1; },
function(x,y) { return ((x+y+x*y%3|mat[y][x]>>1)^mat[y][x])&1^1; } ];
if (ver < 1) get = [get[1],get[4],get[6],get[7]]; // mask pattern for micro QR
var msk, pen = 30000, p;
for (var m = 0; m < get.length; m++) { // compute penalty of masks
x = y = p = d = 0;
if (ver < 1) { // penalty micro QR
for (i = 1; i < size; i++) {
x -= get[m](i,size-1);
y -= get[m](size-1,i);
}
p = x > y ? 16*x+y : x+16*y;
} else { // penalty QR
[ [[1,1,1,1,1]], [[0,0,0,0,0]], // N1 >4 adjacent
[[1,1],[1,1]], [[0,0],[0,0]], // N2 block 2x2
[[1,0,1,1,1,0,1,0,0,0,0]], [[0,0,0,0,1,0,1,1,1,0,1]], // N3 like finder
[[1]] // N4 darks
].forEach(function(pat,pi) { // look for pattern
for (p += d, d = y = 0; y+pat.length <= size; y++) {
var add = [3,3,40,1, 3,0,40,0]; // N1, N2, N3, N4; horizontal/vertical
for (x = 0; x+pat[0].length <= size; x++) {
i = j = 1;
for (var py = 0; py < pat.length; py++)
for (var px = 0; px < pat[py].length; px++) {
if (get[m](x+px,y+py) != pat[py][px]) i = 0; // horizontal
if (get[m](y+py,x+px) != pat[py][px]) j = 0; // vertical
}
d += add[pi>>1]*i+add[pi>>1|4]*j; // add penalty
add[0] = 3-2*i; add[4] = 3-2*j; // toggle N1: 3-1-1-...
}
}
});
p += Math.floor(Math.abs(10-20*d/(size*size)))*10; // N4: darks
}
if (p < pen) { pen = p; msk = m; } // take mask of lower penalty
}
for (y = 0; y < size; y++) // remove reservation, apply mask
for (x = 0; x < size; x++)
mat[y][x] = get[msk](x,y);
/** format information, code level & mask */
j = ver == -3 ? msk : ver < 1 ? (2*ver+lev+5)*4+msk : ((5-lev)&3)*8+msk;
for (k = j *= 1024, i = 4; i >= 0; i--) // BCH error correction: 5 data, 10 error bits
if (j >= 1024<<i) j ^= 1335<<i; // generator polynomial: x^10+x^8+x^5+x^4+x^2+x+1 = 10100110111b = 1335
k ^= j^(ver < 1 ? 17477 : 21522); // XOR masking
for (j = 0; j < 15; j++, k >>= 1) // layout format information
if (ver < 1)
mat[j < 8 ? j+1 : 8][j < 8 ? 8 : 15-j] = k&1; // micro QR
else
mat[8][j < 8 ? size-j-1 : j == 8 ? 7 : 14-j] = // QR horizontal
mat[j < 6 ? j : j < 8 ? j+1 : size+j-15][8] = k&1; // vertical
/** version information */
for (k = ver*4096, i = 5; i >= 0; i--) // BCH error correction: 6 data, 12 error bits
if (k >= 4096<<i) k ^= 7973<<i; // generator: x^12+x^11+x^10+x^9+x^8+x^5+x^2+1 = 1111100100101b = 7973
if (ver > 6) // layout version information
for (k ^= ver*4096, j = 0; j < 18; j++, k >>= 1)
mat[size+j%3-11][j/3|0] = mat[j/3|0][size+j%3-11] = k&1;
return mat; // QR additionally needs a quiet zone of 4 cells around the symbol!
}
/** Aztec bar code symbol creation according ISO/IEC 24778:2008
* creates Actec and compact Aztec bar code symbol by call back function.
* @param text to encode
* @param sec optional: percentage of checkwords used for security 2%-90% (23%)
* @param lay optional: minimum number of layers (size), 0: Aztec Rune
* @return matrix array of barcode ([] if text too long for Aztec)
*/
function aztec(text, sec, lay) { // make Aztec bar code
var e = 20000, BackTo, numBytes, CharSiz = [5,5,5,5,4];
var LatLen = [[ 0,5,5,10,5,10], [9,0,5,10,5,10], [5,5,0,5,10,10],
[5,10,10,0,10,15], [4,9,9,14,0,14], [0,0,0,0,0,0]];
var ShftLen = [[0,e,e,5,e], [5,0,e,5,e], [e,e,0,5,e], [e,e,e,0,e], [4,e,e,4,0]];
var Latch = [[[], [28], [29], [29,30],[30], [31]], // from upper to ULMPDB
[[30,14],[], [29], [29,30],[30], [31]], // lower
[[29], [28], [], [30], [28,30],[31]], // mixed
[[31], [31,28],[31,29],[], [31,30],[31,31]], // punct
[[14], [14,28],[14,29],[14,29,30],[], [14,31]]]; // digit
var CharMap = [ " ABCDEFGHIJKLMNOPQRSTUVWXYZ", // upper
" abcdefghijklmnopqrstuvwxyz", // lower
String.fromCharCode(0,32,1,2,3,4,5,6,7,8,9,10,11,12,13,
7,28,29,30,31,64,92,94,95,96,124,126,127), // mixed
" \r\r\r\r\r!\"#$%&'()*+,-./:;<=>?[]{}", // punct
" 0123456789,."]; // digit
var enc, el = text.length, b, typ = 0, x,y, ctr, c, i, j, l;
function stream(seq, val, bits) { // add data to bit stream
var eb = seq[0]%b+bits; // first element is length in bits
val <<= b; seq[0] += bits; // b - word size in bits
seq[seq.length-1] |= val>>eb; // add data
while (eb >= b) { // word full?
bits = seq[seq.length-1]>>1;
if (typ == 0 && (bits == 0 || 2*bits+2 == 1<<b)) { // bit stuffing: all 0 or 1
seq[seq.length-1] = 2*bits+(1&bits^1); // insert complementary bit
seq[0]++; eb++;
}
eb -= b;
seq.push((val>>eb)&((1<<b)-1));
}
}
function binary(seq, pos) { // encode numBytes of binary
seq[0] -= numBytes*8+(numBytes > 31 ? 16 : 5); // stream() adjusts len too -> remove
stream(seq, numBytes > 31 ? 0 : numBytes, 5); // len
if (numBytes > 31) stream(seq, numBytes-31, 11); // long len
for (var i = pos-numBytes; i < pos; i++)
stream(seq, text.charCodeAt(i), 8); // bytes
}
/** encode text */
sec = 100/(100-Math.min(Math.max(sec||25,0),90)); // limit percentage of check words to 0-90%
for (j = c = 4; ; c = b) { // compute word size b: 6/8/10/12 bits
j = Math.max(j,(Math.floor(el*sec)+3)*c); // total needed bits, at least 3 check words
b = j <= 240 ? 6 : j <= 1920 ? 8 : j <= 10208 ? 10 : 12; // bit capacity -> word size
if (lay) b = Math.max(b, lay < 3 ? 6 : lay < 9 ? 8 : lay < 23 ? 10 : 12); // parameter
if (c >= b) break; // fits in word size
var Cur = [[0,0],[e],[e],[e],[e],[e]]; // current sequence for [U,L,M,P,D,B]
for (i = 0; i < text.length; i++) { // calculate shortest message sequence
for (var to = 0; to < 6; to++) // check for shorter latch to
for (var frm = 0; frm < 6; frm++) // if latch from
if (Cur[frm][0]+LatLen[frm][to] < Cur[to][0] && (frm < 5 || to == BackTo)) {
Cur[to] = Cur[frm].slice(); // replace by shorter sequence
if (frm < 5) // latch from shorter mode
Latch[frm][to].forEach(function (lat) {stream(Cur[to], lat, lat < 16 ? 4 : 5);});
else
binary(Cur[to], i); // return from binary -> encode
if (to == 5) { BackTo = frm; numBytes = 0; Cur[5][0] += 5; } // begin binary shift
}
var Nxt = [[e],[e],[e],[e],[e],Cur[5]]; // encode char
var twoChar = ["\r\n",". ",", ",": "].indexOf(text.substr(i,2)); // special 2 char sequences
for (to = 0; to < 5; to++) { // to sequence
var idx = twoChar < 0 ? CharMap[to].indexOf(text.substr(i,1),1) : twoChar+2; // index to map
if (idx < 0 || (twoChar >= 0 && to != 3)) continue; // char in set ?
for (frm = 0; frm < 5; frm++) // encode char
if (Cur[frm][0]+ShftLen[frm][to]+CharSiz[to] < Nxt[frm][0]) {
Nxt[frm] = Cur[frm].slice();
if (frm != to) // add shift
stream(Nxt[frm], to == 3 ? 0 : frm < 4 ? 28 : 15, CharSiz[frm]);
stream(Nxt[frm], idx, CharSiz[to]); // add char
}
}
Nxt[5][0] += numBytes++ == 31 ? 19 : 8; // binary exeeds 31 bytes
if (twoChar >= 0) { i++; Nxt[5][0] += numBytes++ == 31 ? 19 : 8; } // 2 char seq: jump over 2nd
Cur = Nxt; // take next sequence
}
binary(Cur[5], text.length); // encode remaining bytes
enc = Cur.reduce(function(a,b) { return a[0] < b[0] ? a : b; }); // get shortest sequence
i = b-enc[0]%b; if (i < b) stream(enc,(1<<i)-1,i); // padding
enc.pop(); // remove 0-byte
el = enc.shift()/b|0; // get encoding length
}
if (el > 1660) return []; // message too long
typ = j > 608 || el > 64 || (lay && lay > 4) ? 14 : 11; // full or compact Aztec finder size
var mod = parseInt(text); // Aztec rune possible?
if (mod < 0 || mod > 255 || mod+"" != text || lay != 0) // Aztec rune 0-255 ?
lay = Math.max(lay||1,Math.min(32,(Math.ceil((Math.sqrt(j+typ*typ)-typ)/4)))); // needed layers
var ec = Math.floor((8*lay*(typ+2*lay))/b)-el; // # of error words
typ >>= 1; ctr = typ+2*lay; ctr += (ctr-1)/15|0; // center position
/** compute Reed Solomon error detection and correction */
function rs(ec,s,p) { // # of checkwords, polynomial bit size, generator polynomial
var rc = new Array(ec+2), i, j, el = enc.length; // reed/solomon code
var lg = new Array(s+1), ex = new Array(s); // log/exp table for multiplication
for (j = 1, i = 0; i < s; i++) { // compute log/exp table of Galois field
ex[i] = j; lg[j] = i;
j += j; if (j > s) j ^= p; // GF polynomial
}
for (rc[ec+1] = i = 0; i <= ec; i++) // compute RS generator polynomial
for (j = ec-i, rc[j] = 1; j++ < ec; )
rc[j] = rc[j+1]^ex[(lg[rc[j]]+i)%s];
for (i = 0; i < el; i++) // compute RS checkwords
for (j = 0, p = enc[el]^enc[i]; j++ < ec; )
enc[el+j-1] = enc[el+j]^(p ? ex[(lg[rc[j]]+lg[p])%s] : 0);
}
/** layout Aztec barcode */
var mat = Array(2*ctr+1).fill(null).map(function() {return [];});
for (y = 1-typ; y < typ; y++) // layout central finder
for (x = 1-typ; x < typ; x++)
mat[ctr+y][ctr+x] = Math.max(Math.abs(x),Math.abs(y))&1^1;
mat[ctr-typ+1][ctr-typ] = mat[ctr-typ][ctr-typ] = 1; // orientation marks
mat[ctr-typ][ctr-typ+1] = mat[ctr+typ-1][ctr+typ] = 1;
mat[ctr-typ+1][ctr+typ] = mat[ctr-typ][ctr+typ] = 1;
function move(dx,dy) { // move one cell
do x += dx; while (typ == 7 && (x&15) == 0); // skip reference grid
do y += dy; while (typ == 7 && (y&15) == 0);
}
if (lay > 0) { // layout the message
rs(ec,(1<<b)-1,[67,301,1033,4201][b/2-3]); // error correction, generator polynomial
x = -typ; y = x-1; // start of layer 1 at top left
j = l = (3*typ+9)/2; // length of inner side
var dx = 1, dy = 0; // direction right
while ((c = enc.pop()) !== undefined) // data in reversed order inside to outside
for (i = b/2; i-- > 0; c >>= 2) {
if (c&1) mat[ctr+y][ctr+x] = 1; // odd bit
move(dy,-dx); // move across
if (c&2) mat[ctr+y][ctr+x] = 1; // even bit
move(dx-dy,dx+dy); // move ahead
if (j-- == 0) { // spiral turn
move(dy,-dx); // move across
j = dx; dx = -dy; dy = j; // rotate clockwise
if (dx < 1) // move to next side
for (j = 2; j--;) move(dx-dy,dx+dy);
else l += 4; // full turn -> next layer
j = l; // start new side
}
}
if (typ == 7) // layout reference grid
for (x = (15-ctr)&-16; x <= ctr; x += 16)
for (y = (1-ctr)&-2; y <= ctr; y += 2)
mat[ctr+y][ctr+x] = mat[ctr+x][ctr+y] = 1;
mod = (lay-1)*(typ*992-4896)+el-1; // 2/5 + 6/11 mode bits
}
/** process modes message compact/full */
b = (typ*3-1)/2; // 7/10 bits per side
for (i = typ-2; i-- > 0; mod >>= 4) enc[i] = mod&15; // mode to 4 bit words
rs((typ+5)/2,15,19); // add 5/6 words error correction
enc.push(0); j = lay ? 0 : 10; // XOR Aztec rune data
for (i = 1; i <= b; i++) stream(enc,j^enc[i],4); // 8/16 words to 4 sides
for (i = 2-typ, j = 1; i < typ-1; i++, j += j) { // layout mode data
if (typ == 7 && i == 0) i++; // skip reference grid
if (enc[b+1]&j) mat[ctr-typ][ctr-i] = 1; // top
if (enc[b+2]&j) mat[ctr-i][ctr+typ] = 1; // right
if (enc[b+3]&j) mat[ctr+typ][ctr+i] = 1; // bottom
if (enc[b+4]&j) mat[ctr+i][ctr-typ] = 1; // left
}
return mat; // matrix Aztec barcode
}
/** PDF417 bar code symbol creation according ISO/IEC 15438:2006
* creates PDF417, CompactPDF417 or MicroPDF417 bar code symbol as a javascript matrix.
* @param text to encode
* @param level optional: security level: 0-7; (null for auto 2-5)
* @param cols optional: # of columns: 1-30 (0 for auto)
* @param rows optional: # of rows: 3-90 (0 for auto)
* for cols / rows > 90 they define an aspect_ratio
* @param type optional: barcode type (f:full, c:compact, m:micro)
* @return matrix array of PDF417 symbol ([] if text is too long)
*/
function pdf417(text, level, cols, rows, type) { // make PDF417 barcode
var sub = [ "ABCDEFGHIJKLMNOPQRSTUVWXYZ ", // alpha compactation chars
"abcdefghijklmnopqrstuvwxyz ", // lower
"0123456789&\r\t,:#-.$/+%*=^\r ", // mixed
";<>@[\\]_`~!\r\t,:\n-.$/\"|*()?{}'" ]; // punctuation
var enc = [], mode = 2; // text>alpha
var pn = 0, pc = 0;
function push(c) { // encoding text compaction
if (pn == 1) enc.push(pc*30+c);
pn = 1-pn; pc = c*pn; // toggle pn
}
/** encode text */
for (var i = 0; i < text.length; ) { // check for numeric
for (var j = 0; j < 45 && i+j < text.length; j++)
if (((text.charCodeAt(i+j)-48)&255) > 9) break;
if (j > 12 || i+j == text.length) {
if (mode > 0) enc.push(902); // numeric compaction
var t = "1"+text.substr(i,j);
i += j; j = enc.length; // to numeric
for ( ; t > 0; t = c) {
var r = t.substr(0, t.length%3), c = "";
for (var k = t.length/3|0; k > 0; k--) {
r = r%900+t.substr(-3*k,3); // string division
c += ("00"+(r/900|0)).substr(-3);
}
enc.splice(j,0,r%900); // push reminder
}
mode = 0; continue;
}
// check for text
for (t = j = 0; j < t+13 && i+j < text.length; j++) {
if (((text.charCodeAt(i+j)-32)&255) > 95) break;
if (((text.charCodeAt(i+j)-48)&255) > 9) t = j+1; // start digits
}
if (j > 4 || i+j == text.length) { // text compaction
if (mode != 2) enc.push(900); // to text
mode = 2;
for (t = j == t+13 ? t : j; t > 0; t--) {
c = text.charAt(i++);
k = sub[mode-2].indexOf(c);
if (k < 0) { // switch sub mode
if (mode > 4) { push(29); mode = 2; } // exit punctuation
for (j = -1; k < 0; k = sub[++j].indexOf(c)); // get sub mode
if (j == 3) // to punctiation ?
if (i < text.length && sub[3].indexOf(text.charAt(i)) >= 0) {
if (mode < 4) push(28); // first to mixed
push(25); mode = 5; // latch to punctuation
} else push(29); // shift to punctuation
else if (mode == 3 && j == 0) // lower to upper
if (i < text.length && sub[1].indexOf(text.charAt(i)) < 0) {
push(28); push(28); mode = 2; // latch to upper
} else push(27); // just one shift from lower to upper
else { push(28-j%2); mode = j+2; } // latch to alpha/lower/mixed
}
push(k); // add char
}
if (pn > 0) push(29); // padding
continue;
}
var b = 1; // byte compaction
for (j = 1; j < b+5 && i+j < text.length; j++) // get first 5 non-bytes
if (((text.charCodeAt(i+j)-32)&255) > 95) b = j+1;
if (i+j == text.length && mode < 2) b = text.length-i; // no 5 non-bytes beford eot
if (mode > 1 && b == 1 && text.length-i > 4) { // just one byte
enc.push(913); // shift byte
enc.push(text.charCodeAt(i++)&255);
continue;
}
b = b < j ? j : b;
if (mode != 1) enc.push(b%6 == 0 ? 924 : 901);
mode = 1; // latch byte compaction
for ( ; b > 5; b -= 6) { // multiple of 6 bytes compaction
for (j = t = 0; j < 6; j++) t = t*256+(text.charCodeAt(i++)&255);
for (c = enc.length; --j > 0; t = Math.floor(t/900)) enc.splice(c,0,t%900);
}
while (b-- > 0) enc.push(text.charCodeAt(i++)&255); // remaining byte compaction
}
/** compute symbol size and barcode type */
var el = enc.length, r = 0;
rows = rows||0; cols = cols||0;
if (type == 'm' && el > 176) type = 'f'; // too long for MicroPDF
if ((rows|cols) == 0) { rows = 99; cols = 33; } // auto_size
if (type != 'm') { // full PDF417
if (el > 924) return []; // message too long
if ((level ^ 0) !== level) // isNotNumeric ?
level = el < 41 ? 2 : el < 161 ? 3 : el < 320 ? 4 : 5; // auto_level
level = level < 0 ? 0 : level > 8 ? 8 : level; // limit level
while (929-el < 2<<level) level--; // limit level to capacity
var ec = 2 << level;
if (rows > 90 && cols > 30) {
cols = Math.ceil(cols*Math.sqrt(el+ec+1)/rows); // aspect_ratio
rows = 0;
}
if (30*rows < el+ec+1) rows = Math.ceil((el+ec+1)/cols); // too less rows ?
rows = rows < 3 ? 3 : rows > 90 ? 90 : rows; // limit rows;
cols = Math.max(cols,Math.ceil((++el+ec)/rows)); // too less columns ?
enc.unshift(rows*cols-ec); // symbol length descriptor
} else { // MicroPDF417 parameter (ISO/IEC 24728:2006)
c = [1,1,1,1,1,1,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4];
r = [11,14,17,20,24,28,8,11,14,17,20,23,26,6,8,10,12,15,20,26,32,38,44,4,6,8,10,12,15,20,26,32,38,44];
b = [7,7,7,8,8,8,8,9,9,10,11,13,15,12,14,16,18,21,26,32,38,44,50,8,12,14,16,18,21,26,32,38,44,50];
for (i = 0; i < c.length-1; i++) { // compute size
if (c[i]*r[i] < el+b[i]) continue; // too small
if (cols == c[i] && (rows == r[i] || rows == 0)) break; // column fits
if (rows == r[i] && cols == 0) break; // row fits
if (cols*rows > 0 && c[i] >= cols && r[i] >= rows) break; // fit next size
if (rows+cols > 99 && r[i]*cols < c[i]*rows) break; // aspect_ratio
}
ec = b[i]; cols = c[i]; rows = r[i]; // get parameter
var rapl = [1,8,36,19,9,25,1,1,8,36,19,9,27,1,7,15,25,37,1,1,21,15,1,47,1,7,15,25,37,1,1,21,15,1][i];
var rapc = [31,25,19,1,7,15,25,37,17,9,29,31,25,1,7,15,25,37,17,9,29][i%21];
var rapr = [9,8,36,19,17,33,1,9,8,36,19,17,35,1,7,15,25,37,33,17,37,47,49,43,1,7,15,25,37,33,17,37,47,49][i];
var clst = [0,1,2,0,2,0,0,0,1,2,0,2,2,0,0,2,0,0,0,0,2,2,0,1,0,0,2,0,0,0,0,2,2,0][i];
var pats = [49,17,81,89,25,57,61,29,93,77,109,101,69,5,13,9,73,105,107,106,74,90,82,83,87,
86,84,20,22,23,19,27,91,75,11,10,26,58,50,51,55,54,52,36,44,46,47,39,38,34,35,33];
var patc = [111,103,39,55,23,19,27,11,9,13,29,25,17,49,57,61,125,121,123,59,58,50,51,35,33,97,
113,115,114,118,54,22,20,52,116,100,102,98,99,67,71,70,78,76,92,88,72,104,40,44,46,110];
}
k = rows*cols-enc.length-ec; // padding
while (k--) enc.push(900); // add padding
/** compute Reed Solomon error detection and correction */
var rs = Array(ec+1);
rs[0] = t = 1; el = enc.length;
for (i = 0; i < ec; i++) { // calculate generator polynomial
enc[el+i] = rs[i+1] = 0;
t = 3*t%929; // (3^i) MOD 929
for (j = i; j >= 0; j--)
rs[j+1] = rs[j]-t*rs[j+1]%929;
rs[0] = -rs[0]*t%929;
}
for (i = 0; i < el; i++) { // calculate RS codewords
t = enc[i]+enc[el];
for (j = 0; j++ < ec; )
enc[el+j-1] = enc[el+j]-t*rs[ec-j]%929;
enc[el+ec-1] = -t*rs[0]%929;
}
while (ec--) enc[el+ec] = (9290-enc[el+ec])%929; // complement RS
/** layout PDF417 barcode */
sub = [[17,,,3,,,11,,4,4,,4,15,,,3,,,11,,4,22,,,3,,,3,,23,,4,,25,191,232,,,11,,,3,,3,,,3,,4,15,,,11,,,3,,,3,,
21,,,3,,4,,23,,4,5,569,,,3,,,11,,,3,,4,5,14,,,3,,,3,,4,5,19,,4,,4,24,639,,,11,,,3,,,3,,4,,18,,,3,,4,,4,
5,84,17,785,,,3,,,3,,4,,4,19,5,5,1005,10,4,5,17,40048,,,11,,,3,,,11,,4,84,,,11,,,3,,4,5,19,,,3,,4,5,22,,
4,185,-39757,,,3,,,11,,,3,,,3,,31,,,58,9,,,58,,,11,9,,,58,,4,32,,,58,9,,,58,,,3,9,8,35,,7,9,,7,,4,38,8,9,
8,388,,,11,,,3,,,3,,4,5,30,,,58,9,,58,9,56,9,58,9,,7,,9,4,14,,,58,9,,7,,9,7,9,5,19,,7,9,8,9,8,24,419,,,
3,,,3,,4,,4,5,40001,,,58,9,,,58,,,3,9,,7,,4,32,,7,9,,7,,4,9,8,22,5,604,,,3,,4,,4,5,31,,,58,9,,7,,9,7,9,8,
9,19,8,5,5,793,5,17,35,4,8,5,5,80039,,,3,,3,,,3,,4,5,14,,,3,,,3,,4,5,19,,4,,4,24,5,60,,,11,,,3,,,3,,4,,
31,,,58,9,,58,9,56,9,58,,9,7,9,8,32,,,58,9,56,3,,9,7,9,8,35,,7,9,8,9,24,8,-39757,,,3,,,3,,4,,4,31,,,58,
9,56,9,58,,,3,9,,7,,40026,40001,,,58,9,,,-79963,9,,,58,,,42,54,9,,7,79990,14,,7,9,,54,9,,7,,42,8,35,8,
9,6,9,60,,,3,,4,,4,5,31,,,58,9,56,3,,9,7,9,8,9,8,79996,,,58,9,,54,9,,9,54,,42,6,9,9,8,32,,7,9,6,9,9,6,9,
38,-79920,423,,4,,4,5,34,,7,9,,7,,4,9,8,44,,7,9,,54,9,,7,,42,6,9,8,35,8,5,612,17,38,4,5,37,8,8,79990,8,
80074,4,,4,,,3,,4,,18,,,3,,4,,4,5,19,,4,5,24,-39541,,,3,,4,,4,,4,31,,,58,9,56,9,58,,,3,9,,7,,40026,14,,
7,9,,7,,4,9,22,8,9,8,60,,,3,,4,,4,5,31,,7,9,,7,,9,7,9,8,9,8,79996,,7,9,56,58,9,,9,54,,42,6,9,9,8,32,,
7,9,6,9,9,6,9,38,8,8,-39753,,4,,4,5,34,,7,,9,7,,4,9,8,44,,7,9,,54,9,,7,,42,6,9,8,119991,,7,9,,54,9,,54,
9,,42,53,,42,6,51,8,9,6,9,6,9,8,392,,4,5,37,,7,,4,9,8,46,,7,9,6,9,9,6,9,8,50,,7,,7,9,9,53,9,9,6,9,8,47,
-79920,462,25,5,40,8,5,48,8,8,5,160025,5,,4,5,5,19,5,5,5,22,5,61,5,,4,,4,5,31,,7,,9,7,,4,,4,9,8,32,,7,,
4,9,8,38,8,-39537,5,5,5,34,8,,9,7,,4,9,8,44,8,9,,54,9,9,6,9,9,6,9,35,8,9,6,9,8,-39568,5,5,37,8,,4,9,8,
46,,7,,7,9,9,6,9,8,50,,7,,7,9,9,53,9,9,6,9,47,8,8,8,-39749,5,40,8,9,8,48,8,9,6,9,8,120061,8,9,6,9,6,],
[10,,3,,,3,,,3,,4,,4,14,,,11,,,3,,,3,,4,,4,14,,,11,,,3,,,3,,4,,4,14,,,11,,,3,,,3,,4,,18,,,3,,4,,4,22,,4,5,
137,,,3,,,3,,4,,4,5,13,,,3,,,3,,4,,4,5,13,,,3,,,3,,4,,4,5,13,,,3,,,3,,4,,4,19,,4,,4,5,22,5,172,,,3,,4,,
4,5,18,,,3,,4,,4,5,18,,,3,,4,,4,5,18,,,3,,4,,4,5,19,,4,5,24,207,,4,,4,5,21,,4,,4,5,21,,4,,4,5,21,,4,,4,
5,22,5,392,,4,5,23,,4,5,23,,4,5,23,,4,5,577,5,25,5,25,5,39713,,,3,,,3,,4,,4,5,13,,,3,,,3,,4,,4,5,13,,,
3,,,3,,4,,4,5,13,,,3,,,3,,4,,4,19,,4,,4,5,22,5,-40004,,2,1,,2,1,,2,1,5,31,,,58,9,56,3,,9,7,9,8,9,8,31,,,
58,9,,7,,9,7,9,8,9,8,31,,,58,9,,7,,9,7,9,8,9,8,31,,,58,9,,7,,9,7,9,8,9,19,,7,9,8,9,8,38,-39969,,2,1,,2,
1,12,1,34,,7,9,,7,,4,9,8,34,,7,9,,7,,4,9,8,34,,7,9,,7,,4,9,8,34,,7,9,,7,,4,9,8,35,8,9,8,176,,2,1,12,1,
37,,7,,4,9,8,37,,7,9,8,9,8,37,,7,,4,9,8,37,,7,9,8,9,8,38,8,211,12,1,40,8,9,8,40,8,9,8,40,8,9,8,40,8,9,
8,396,41,8,41,8,41,8,79708,,,3,,4,,4,5,18,,,3,,4,,4,5,18,,,3,,4,,4,5,18,,,3,,4,,4,5,19,,4,5,24,-39969,,
2,1,,2,1,12,1,34,,7,9,,7,,4,9,8,34,,7,9,,7,,4,9,8,34,,7,9,,7,,4,9,8,34,,7,9,,7,,4,9,8,35,8,9,8,-40000,
2,,1,2,,1,2,5,40046,,2,55,,2,9,55,9,59,1,46,,7,9,6,,42,6,9,8,46,,7,9,6,9,9,6,9,8,46,,7,9,6,9,9,6,9,8,
46,,7,9,6,9,9,6,9,8,47,8,-39965,2,,1,2,5,36,12,55,9,59,1,48,8,9,6,9,8,48,8,9,6,9,8,48,8,9,6,9,8,48,8,9,
6,9,8,180,2,5,39,8,49,8,8,49,8,8,49,8,8,49,8,8,240,120507,,4,,4,5,21,,4,,4,5,21,,4,,4,5,21,,4,,4,5,22,5,
-39784,,2,1,12,1,37,,7,9,8,9,8,37,,7,9,8,9,8,37,,7,9,8,9,8,37,,7,9,8,9,8,38,8,-39965,2,,1,2,5,36,12,55,
9,59,1,48,8,9,6,9,8,48,8,9,6,9,8,48,8,9,6,9,8,48,8,9,6,9,8,-39975,,4,,4,5,33,2,57,2,9,8,80076,8,8,52,8,
8,8,52,8,8,8,52,8,8,8,52,8,8,-39940,,4,5,38,8,29,5,1260,159392,5,5,23,,4,5,23,,4,5,23,,4,5,24,-39749,12,
1,40,8,9,8,40,8,9,8,40,8,9,8,40,8,9,8,-39780,2,5,39,8,49,8,8,49,8,8,49,8,8,49,8,8,-39940,,4,5,38,8,62,,
4,,4,5,35,8,9,8,28,,4,5,38,8,29,5,1260,198493,5,25,5,25,5,-39348,41,8,41,8,41,8,-39720,-38885,5,63,,4,
5,38,8,62,,4,,4,5,35,8,9,8,28,,4,5,38,8,29,5,121800,-38885,5,63,,4,5,38,8,29,5,],
[28,,3,,,3,,,3,,4,16,2,,1,2,,1,2,,1,2,20,,2,1,,2,1,12,1,21,,4,,4,5,21,,4,,4,5,21,,4,,4,166,,,3,,,3,,4,,4,16,
2,,1,2,,1,2,5,20,,2,1,12,1,23,,4,5,23,,4,5,23,,4,5,166,,,3,,4,,4,5,16,2,,1,2,5,121,12,1,25,5,25,5,25,5,
201,,4,,4,5,90,2,5,156,26,26,452,,4,5,24,29,5,40145,,,3,,,3,,4,,4,16,2,,1,2,,1,2,5,20,,2,1,12,1,23,,4,5,
23,,4,5,23,,4,5,-40010,,,11,,,3,,,3,,4,,4,30,,,58,9,,7,,9,7,9,8,9,8,40015,2,,55,9,-39970,9,57,2,9,8,36,
12,55,9,59,1,40,8,9,8,40,8,9,8,40,5,-39979,,,3,,,3,,4,,4,5,30,,7,9,,7,,4,9,8,33,2,57,2,9,8,39,8,41,8,41,
8,41,-39944,,,3,,4,,4,5,32,,7,9,8,9,8,38,8,27,,4,,4,5,35,8,9,8,28,,4,5,38,8,29,5,79030,,,3,,4,,4,5,16,2,,
1,2,5,121,12,1,25,5,25,5,25,5,-39979,,,3,,,3,,4,,4,5,30,,7,9,,7,,4,9,8,33,2,57,2,9,8,39,8,41,8,41,8,41,
-80120,,,11,,,3,,,3,,4,,4,30,,,58,9,,7,,9,7,9,8,9,8,43,,7,9,6,9,9,6,9,8,47,8,8,635,,,3,,,3,,4,,4,5,30,,
7,9,,7,,4,9,8,45,8,9,6,9,8,820,,,3,,4,,4,5,32,,7,9,8,9,8,47,8,8,27,,4,,4,5,35,8,9,8,28,,4,5,38,8,29,5,
117915,,4,,4,5,22,5,156,26,26,26,-39944,,,3,,4,,4,5,32,,7,9,8,9,8,38,8,-39325,,,3,,,3,,4,,4,5,30,,7,9,,7,
9,8,9,8,45,8,9,6,9,8,61,,,11,,,3,,,3,,4,,4,30,,,58,9,,7,,9,7,9,8,9,8,43,,7,9,6,9,9,6,9,8,51,8,8,8,635,,,
3,,,3,,4,,4,5,30,,7,9,,7,,4,9,8,45,8,9,6,9,8,820,,,3,,4,,4,5,32,,7,9,8,9,8,47,8,8,27,,4,,4,5,35,8,9,8,
28,,4,5,38,8,157910,,4,5,24,62,,4,,4,5,35,8,9,8,-39140,,,3,,4,,4,5,32,,7,9,8,9,8,47,8,8,-39325,,,3,,,3,,
4,,4,5,30,,7,9,,7,,4,9,8,45,8,9,6,9,8,61,,,3,,4,,4,5,31,,,58,9,,7,,9,7,9,8,9,8,43,,7,9,6,9,9,6,9,8,51,8,
8,8,639,,4,,4,5,34,,7,9,,7,,4,9,8,45,8,9,6,9,8,824,,4,5,37,,7,9,8,9,8,47,8,8,859,5,40,8,9,8,198055,5,63,,
4,5,38,8,62,,4,,4,5,35,8,9,8,-39140,,,3,,4,,4,5,32,,7,,4,9,8,47,8,8,-39321,,4,,4,5,34,,7,,9,7,9,8,9,8,
45,8,9,6,9,8,-39352,,4,5,37,,7,9,8,9,8,46,,7,9,6,9,9,6,9,8,51,8,8,8,643,5,40,8,9,8,48,8,9,6,9,8,828,41,
8,49,8,8,199165,5,63,,4,5,38,8,62,,4,,4,5,35,8,9,8,-39136,,4,5,37,,7,,4,9,8,47,8,8,-39317,5,40,8,9,8,48,
8,9,6,9,8,828,41,8,49,8,8,161575,5,63,,4,5,38,8,-39101,5,40,8,9,8,-39132,]
]; // indexed offsets of bar/space cluster
ec = [4,6,25,27,31,35,-79955,-39964,-39960,39995,8,23,29,41,45,49,55,66,76,80,86,111,115,146,150,181,216,855,1040,
1075,40005,40036,40040,40050,40071,40075,40081,40106,40110,40116,40141,40176,79986,80000,80031,80035,80066,80070,
80101,80136,120026,120030,120096,-119950,-79959,-39989,-39987,-39985,-39968,-39966,-39572,-39356,-39105,-38920];
sub.forEach(function(b) { // cluster 0/3/6
for (i = 0; i < b.length-1; i++) { // unpack bar/space sequence
t = [17]; b[i+1] = b[i] + (b[i+1]&-64 ? b[i+1] : ec[b[i+1]|0]);
for (j = 8; --j > 0; b[i] = b[i]/6|0) // get width of bar/space
t[0] -= t[j] = b[i]%6+1;
while (j < 8) b[i] = ((++b[i]<<t[j++])-1)<<t[j++]; // make pattern
}});
var mat = Array(rows); // barcode matrix
function set(bar,bits) { // set code bars
while (bits-- > 0) mat[r][k++] = (bar>>bits)&1;
}
t = [(rows-1)/3|0,level*3+(rows-1)%3,cols-1]; // row indicator cluster
for (r = 0; r < rows; r++) { // all rows
mat[r] = []; k = 0;
if (type != 'm') { // full PDF417
set(130728,17); // start
set(sub[r%3][30*(r/3|0)+t[r%3]],17); // left row indicator
for (c = 0; c < cols; c++) // layout message
set(sub[r%3][enc[r*cols+c]],17);
if (type != 'c') // full PDF417 ?
set(sub[r%3][30*(r/3|0)+t[(r+2)%3]],17); // right row indicator
set(260649, type != 'c' ? 18 : 1); // stop
} else { // MicroPDF417
set(pats[(rapl+r)%52]*2+768,10); // left row address pattern
for (c = 0; c < cols; c++) {
if (c > 0 && cols-c == 2) set(patc[(rapc+r)%52]*2+512,10); // center row address pattern
set(sub[(clst+r)%3][enc[r*cols+c]],17); // data message
}
set(pats[(rapr+r)%52]*4+1537,11); // right row address pattern
}
}
return mat; // matrix PDF417 barcode
}
/** Code 128 symbol creation according ISO/IEC 15417:2007
* @param text to encode
* @return array of code128 barcode
*/
function code128(text) {
var t = 3, enc = [], i, j, c, mat = [];
for (i = 0; i < text.length; i++) {
c = text.charCodeAt(i);
if (t != 2) { // alpha mode
for (j = 0; j+i < text.length; j++) // count digits
if (text.charCodeAt(i+j)-48>>>0 > 9) break; // digit ?
if ((j > 1 && i == 0) || (j > 3 && (i+j < text.length || (j&1) == 0))) {
enc.push(i == 0 ? 105 : 99); // Start / Code C
t = 2; // to digit
}
}
if (t == 2) // digit mode
if (c-48>>>0 < 10 && i+1 < text.length && text.charCodeAt(i+1)-48>>>0 < 10)
enc.push(+text.substr(i++,2)); // 2 digits
else t = 3; // exit digit
if (t != 2) { // alpha mode
if (t > 2 || ((c&127) < 32 && t) || ((c&127) > 95 && !t)) { // change ?
for (j = t > 2 ? i : i+1; j < text.length; j++) // A or B needed?
if ((text.charCodeAt(j)-32)&64) break; // < 32 or > 95
j = j == text.length || (text.charCodeAt(j)&96) ? 1 : 0;
enc.push(i == 0 ? 103+j : j != t ? 101-j : 98); // start:code:shift
t = j; // change mode
}
if (c > 127) enc.push(101-t); // FNC4: code chars > 127
enc.push(((c&127)+64)%96);
}
}
if (i == 0) enc.push(103); // empty message
j = enc[0]; // check digit
for (i = 1; i < enc.length; i++) j += i*enc[i];
enc.push(j%103); enc.push(106); // stop
c = [358,310,307,76,70,38,100,98,50,292,290,274,206,110,103,230,118,115,313,302,295,370,314,439,422,406,403,
434,410,409,364,355,283,140,44,35,196,52,49,324,276,273,220,199,55,236,227,59,443,327,279,372,369,375,
428,419,395,436,433,397,445,289,453,152,134,88,67,22,19,200,194,104,97,26,25,265,296,477,266,61,158,94,
79,242,122,121,466,458,457,367,379,475,188,143,47,244,241,468,465,239,247,431,471,322,328,334,285];
for (t = i = 0; i < enc.length; i++, t++) { // code to pattern
mat[t++] = 1;
for (j = 256; j > 0; j >>= 1, t++)
if (c[enc[i]]&j) mat[t] = 1;
}
mat[t++] = mat[t] = 1;
return mat;
}
/** convert a black&white image matrix to SVG/canvas path
* @param mat: 0/1 image matrix array, will be destroyed
* @return string: d attribut of svg path
*/
function toPath(mat) {
var path = "", x, y;
mat.forEach(function (y) { y.unshift(0); }); // add padding around matrix
mat.push([]); mat.unshift([]);
for (;;) { // draw polygons
for (y = 0; y+2 < mat.length; y++) // look for set pixel
if ((x = mat[y+1].indexOf(1)-1) >= 0 || (x = mat[y+1].indexOf(5)-1) >= 0) break;
if (y+2 == mat.length || path.length > 1e7) return path;
var c = mat[y+1][x+1]>>2, p = ""; // from start
for (var x0 = x, y0 = y, d = 1; p.length < 1e6;) { // encircle pixel area
do x += 2*d-1; // move left/right
while ((mat[y][x+d]^mat[y+1][x+d])&mat[y+d][x+d]&1); // follow horizontal edge
d ^= mat[y+d][x+d]&1; // turn up/down
do mat[d ? ++y : y--][x+1] ^= 2; // move and mark edge
while ((mat[y+d][x]^mat[y+d][x+1])&mat[y+d][x+1-d]&1); // follow vertical edge
if (x == x0 && y == y0) break; // returned to start
d ^= 1^mat[y+d][x+1-d]&1; // turn left/right
if (c) p = "V"+y+"H"+x+p; // add points counterclockwise
else p += "H"+x+"V"+y; // add clockwise
}
path += "M"+x+" "+y+p+(c ? "V"+y : "H"+x)+"Z"; // close path
for (d = 0, y = 1; y < mat.length-1; y++) // clear pixel between marked edges
for (x = 1; x < mat[y].length; x++) {
d ^= (mat[y][x]>>1)&1; // invert pixels inside, clr marking
mat[y][x] = 5*d^mat[y][x]&5;
}
}
}
/** convert an image matrix to GIF base64 string <img src=".." />
* @param mat image matrix array of index colors
* @param scale optional (1): single bar or [width,height]
* @param trans optional: transparent color index (undefined)
* @param pad optional: padding arround image in cells (0)
* @param rgb optional: table of rgb color map (black&white)
* @param max optional: maximum dictionary bits (2-12 - but large dictionaries are slow in js)
* @return string "data:image/gif;base64,imagedata"
*/
function toGif(mat, scale, trans, pad, rgb, max) {
var eb = 0, ec = 0, ev = 0, i, c, dic = [], xl = 0; // encoding, pixel dictionary
rgb = rgb||[[255,255,255],[0,0,0]]; // default colors black&white only
if (!(scale instanceof Array)) scale = [scale||1,scale||1]; // default 1x
for (i = mat.length; i--; xl = Math.max(xl,mat[i].length)); // get max width of matrix
pad = pad||0; // padding around barcode
function put(val, bits) { // raster data stream
ev |= (val<<eb)&255;
val >>= 8-eb;
for (eb += bits; eb > 7; eb -= 8) { // output data stream
enc += String.fromCharCode(ev);
if (ec++ == 254) { enc += "\xff"; ec = 0; } // new data block
ev = val&255;
val >>= 8;
}
}
for (var colorBits = 0, colors = 2; colors < rgb.length; colors += colors, colorBits++);
var sx, sy, x = scale[0]*(xl+2*pad), y = scale[1]*(mat.length+2*pad);
var enc = "GIF89a"+String.fromCharCode(x&255,x>>8, y&255,y>>8, 17*colorBits+128,0,0); // global descriptor
for (i = 0; i < colors; i++) { // global color map
c = rgb[i]||[i,i,i]; // default color
enc += String.fromCharCode(c[0],c[1],c[2]);
dic[i] = String.fromCharCode(i); // init dictionary
}
if (trans !== undefined)
enc += String.fromCharCode(33,249,4,1,0,0,trans,0); // extension block transparent color
if (colorBits++ == 0) { colorBits++; colors = 4; dic.push(""); dic.push(""); } // black&white image
enc += ",\0\0\0\0"+enc.substr(6,4)+String.fromCharCode(0,colorBits,255); // local descriptor, raster data block
dic.push(""); dic.push(""); // add clear, reset code
var bits = colorBits+1, code, seq = "", p;
put(colors,bits); // clear code
for (y = -pad; y < pad+mat.length; y++) // LZW compression
for (sy = 0; sy < scale[1]; sy++)
for (x = -pad; x < pad+xl; x++)
for (sx = 0; sx < scale[0]; sx++) {
c = x < 0 || y < 0 || x >= xl || y >= mat.length ? 0 : mat[y][x]&(colors-1); // get pixel
seq += p = String.fromCharCode(c);
i = dic.indexOf(seq); // sequenze in dictionary?
if (i < 0) { // no
put(code,bits); // output code
dic.push(seq); // add to dictionary
seq = p; code = c; // new sequence
if (dic.length-1 == 1<<bits) bits++; // increase bits
if (bits == (max||5) && dic.length == 1<<bits) { // max 12 bits
put(colors,bits); // clear code
bits = colorBits+1; // reset
dic.splice(colors+2,dic.length-colors-2); // reset dictionary
}
} else code = i; // append code
}
put(code,bits); // output remaining
put(colors+1,bits+((24-eb-bits)&7)); // end of image
enc = enc.substr(0,enc.length-ec-1)+String.fromCharCode(ec)+enc.substr(enc.length-ec); // length final raster data block
return 'data:image/gif;base64,'+btoa(enc+"\0;"); // <img src=".." /> DOM gif image
}
/** convert a black&white image matrix to html/css
* @param mat 0/1 image matrix array
* @param size optional (3): single bar in pixel, or as [width,height]
* @param blocks optional (7): # of bar/space classes
* @return html/css of 2D barcode
*/
function toHtml(mat, size, blocks) {
if (!Array.isArray(size)) size = [size||3,size||3];
var s = "barcode"+size[0]+size[1], b; // style class
var html = "<style> ."+s+" div {float:left; margin:0; height:"+size[1]+"px}";
blocks = blocks||7;
for (var i = 0; i < blocks; i++) // define bars/spaces
for (var j = 0; j < blocks; j++)
html += "."+s+" .bar"+i+j+" {border-left:"+i*size[0]+"px solid; margin-right:"+j*size[0]+"px}";
html += "</style><div class="+s+" style='line-height:"+size[1]+"px; display:inline-block'>";
for (i = 0; i < mat.length; i++) // convert matrix
for (j = 0; j < mat[i].length; ) {
if (i && !j) html += "<br style='clear:both' />";
for (b = 0; j < mat[i].length; b++, j++) // bars
if (!mat[i][j] || b+1 == blocks) break;
for (s = 0; j < mat[i].length; s++, j++) // spaces
if (mat[i][j] || s+1 == blocks) break;
html += "<div class=bar"+b+s+"></div>";
}
return html+"</div>"; // html/css of 2D barcode
}