You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

559 lines
10 KiB
JavaScript

/**
https://github.com/datalog/datamatrix-svg
*/
'use strict';
function DATAMatrix( Q ) {
var
M = []
,xx = 0
,yy = 0
,bit = function( x, y ) {
M[ y ] = M[ y ] || [],
M[ y ][ x ] = 1;
}
,toAscii = function( t ) {
var
r = [],
l = t.length;
for( var i = 0; i < l; i++ ) {
var
c = t.charCodeAt( i ),
c1 = ( i + 1 < l ) ? t.charCodeAt( i + 1 ) : 0;
if( c > 47 && c < 58 && c1 > 47 && c1 < 58 ) { /* 2 digits */
r.push( ( c - 48 ) * 10 + c1 + 82 ), /* - 48 + 130 = 82 */
i++;
} else if( c > 127 ) { /* extended char */
r.push( 235 ),
r.push( ( c - 127 ) & 255 );
} else r.push( c + 1 ); /* char */
}
return r;
}
,toBase = function( t ) {
var
r = [ 231 ], /* switch to Base 256 */
l = t.length;
if( 250 < l ) {
r.push( 37 + ( l / 250 | 0 ) & 255 ); /* length high byte (in 255 state algo) */
}
r.push( l % 250 + 149 * ( r.length + 1 ) % 255 + 1 & 255 ); /* length low byte (in 255 state algo) */
for( var i = 0; i < l; i++ ) {
r.push( t.charCodeAt( i ) + 149 * ( r.length + 1 ) % 255 + 1 & 255 ); /* data in 255 state algo */
}
return r;
}
,toEdifact = function( t ) {
var
n = t.length,
l = ( n + 1 ) & -4, cw = 0, ch,
r = ( l > 0 ) ? [ 240 ] : []; /* switch to Edifact */
for( var i = 0; i < l; i++ ) {
if( i < l - 1 ) {
/* encode char */
ch = t.charCodeAt( i );
if( ch < 32 || ch > 94 ) return []; /* not in set */
} else ch = 31; /* return to ASCII */
cw = cw * 64 + ( ch & 63 );
if(( i & 3 ) == 3 ) {
/* 4 data in 3 words */
r.push( cw >> 16 ),
r.push( cw >> 8& 255 ),
r.push( cw & 255 ),
cw = 0;
}
};
return l > n ? r : r.concat( toAscii( t.substr( l == 0 ? 0 : l - 1 ) ) ); /* last chars*/
}
,toText = function( t, s ) {
var
i, j,
cc = 0,
cw = 0,
l = t.length,
r = [ s[ 0 ] ], /* start switch */
push = function( v ) {
/* pack 3 chars in 2 codes */
cw = 40 * cw + v;
/* add code */
if( cc++ == 2 ) {
r.push( ++cw >> 8 ),
r.push( cw & 255 ),
cc = cw = 0;
}
};
for( i = 0; i < l; i++ ) {
/* last char in ASCII is shorter */
if( 0 == cc && i == l - 1 ) break;
var
ch = t.charCodeAt(i);
if( ch > 127 && 238 != r[ 0 ] ) { /* extended char */
push( 1 ),
push( 30 ),
ch -= 128; /* hi bit in C40 & TEXT */
}
for( j = 1; ch > s[ j ]; j += 3 ); /* select char set */
var
x = s[ j + 1 ]; /* shift */
if( 8 == x || ( 9 == x && 0 == cc && i == l - 1 ) ) return []; /* char not in set or padding fails */
if( x < 5 && cc == 2 && i == l-1) break; /* last char in ASCII */
if( x < 5 ) push( x ); /* shift */
push( ch - s[ j + 2 ] ); /* char offset */
}
if(2 == cc && 238 !== r[ 0 ] ) { /* add pad */
push( 0 );
}
r.push( 254 ); /* return to ASCII */
if( cc > 0 || i < l ) r = r.concat( toAscii( t.substr( i - cc ) ) ); /* last chars */
return r;
}
,encodeMsg = function( text, rct ) {
text = unescape( encodeURI( text ) );
var
M = [];
var
enc = toAscii( text ),
el = enc.length,
k = toText( text, [ /* C40 */
230,
31, 0, 0,
32, 9, 29,
47, 1, 33,
57, 9, 44,
64, 1, 43,
90, 9, 51,
95, 1, 69,
127, 2, 96,
255, 1, 0
]),
l = k.length;
if( l > 0 && l < el ) enc = k, el = l;
k = toText( text, [ /* TEXT */
239,
31, 0, 0,
32, 9, 29,
47, 1, 33,
57, 9, 44,
64, 1, 43,
90, 2, 64,
95, 1, 69,
122, 9, 83,
127, 2, 96,
255, 1, 0
]);
l = k.length;
if( l > 0 && l < el ) enc = k, el = l;
k = toText( text, [ /* X12*/
238,
12, 8, 0,
13, 9, 13,
31, 8, 0,
32, 9, 29,
41, 8, 0,
42, 9, 41,
47, 8, 0,
57, 9, 44,
64, 8, 0,
90, 9, 51,
255, 8, 0
]);
l = k.length;
if( l > 0 && l < el ) enc = k, el = l;
k = toEdifact( text ); l = k.length;
if( l > 0 && l < el ) enc = k, el = l;
k = toBase( text ); l = k.length;
if( l > 0 && l < el ) enc = k, el = l;
//console.log('a', el )
//console.log('b', l )
var
h, w, nc = 1, nr = 1, fw, fh, /* symbol size, regions, region size */
i, j = - 1, c, r, s, b = 1, /* compute symbol size */
rs = new Array( 70 ), /* reed / solomon code */
rc = new Array( 70 ),
lg = new Array( 256 ), /* log / exp table for multiplication */
ex = new Array( 255 );
if( rct && el < 50 ) {
/* rect */
k = [ /* symbol width, checkwords */
16, 7,
28, 11,
24, 14,
32, 18,
32, 24,
44, 28
];
do {
w = k[ ++j ]; /* width */
h = 6 + ( j & 12 ); /* height */
l = w * h / 8; /* bytes count in symbol */
} while( l - k[ ++j ] < el ); /* could we fill the rect? */
/* column regions */
if( w > 25 ) nc = 2;
} else {
/* square */
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 [ 0, 0 ]; /* msg is too long */
if( w > 11 * i ) i = 4 + i & 12; /* advance increment */
w = h += i;
l = ( w * h ) >> 3;
} while( l - k[ j ] < el );
if( w > 27 ) nr = nc = 2 * ( w / 54 | 0 ) + 2; /* regions */
if( l > 255 ) b = 2 * ( l >> 9 ) + 2; /* blocks */
}
s = k[ j ], /* rs checkwords */
fw = w / nc, /* region size */
fh = h / nr;
/* first padding */
if( el < l - s ) enc[ el++ ] = 129;
/* more padding */
while( el < l - s ) {
enc[ el++ ] = ( ( ( 149 * el ) % 253 ) + 130 ) % 254;
}
/* Reed Solomon error detection and correction */
s /= b;
/* log / exp table of Galois field */
for( j = 1, i = 0; i < 255; i++ ) {
ex[ i ] = j,
lg[ j ] = i,
j += j;
if( j > 255 ) j ^= 301; /* 301 == a^8 + a^5 + a^3 + a^2 + 1 */
}
/* RS generator polynomial */
for( rs[ s ] = 0, i = 1; i <= s; i++ )
for( j = s - i, rs[ j ] = 1; j < s; j++ )
rs[ j ] = rs[ j + 1 ] ^ ex[ ( lg[ rs[ j ] ] + i ) % 255 ];
/* RS correction data for each block */
for( c = 0; c < b; c++ ) {
for( i = 0; i <= s; i++ ) rc[ i ] = 0;
for( i = c; i < el; i += b )
for( j = 0, x = rc[ 0 ] ^ enc[ i ]; j < s; j++ )
rc[ j ] = rc[ j + 1 ] ^ ( x ? ex[ ( lg[ rs[ j ] ] + lg[ x ] ) % 255 ] : 0 );
/* interleaved correction data */
for( i = 0; i < s; i++ )
enc[ el + c + i * b ] = rc[ i ];
}
/* layout perimeter finder pattern */
/* horizontal */
for( i = 0; i < h + 2 * nr; i += fh + 2 )
for( j = 0; j < w + 2 * nc; j++ ) {
bit( j, i + fh + 1 );
if( ( j & 1 ) == 0 ) bit( j, i );
}
/* vertical */
for( i = 0; i < w + 2 * nc; i += fw + 2 )
for( j = 0; j < h; j++ ) {
bit( i, j + ( j / fh | 0 ) * 2 + 1 );
if( ( j & 1 ) == 1 ) bit( i + fw + 1, j + ( j / fh | 0 ) * 2 );
}
s = 2, /* step */
c = 0, /* column */
r = 4, /* row */
b = [ /* nominal byte layout */
0, 0,
-1, 0,
-2, 0,
0, -1,
-1, -1,
-2, -1,
-1, -2,
-2, -2
];
/* diagonal steps */
for( i = 0; i < l; r -= s, c += s ) {
if( r == h - 3 && c == - 1 )
k = [ /* corner A layout */
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 )
k = [ /* corner D layout */
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, /* turn around */
r += 2 + s / 2,
c += 2 - s / 2;
while( r < 0 || c >= w || r >= h || c < 0 ) {
r -= s,
c += s;
}
}
if( r == h - 2 && c == 0 && ( w & 3 ) )
k = [ /* corner B layout */
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 )
k = [ /* corner C layout */
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 = b; /* nominal L - shape layout */
}
/* layout each bit */
for( el = enc[ i++ ], j = 0; el > 0; j += 2, el >>= 1 ) {
if( el & 1 ) {
var
x = c + k[ j ],
y = r + k[ j + 1 ];
/* wrap around */
if( x < 0 ) x += w, y += 4 - ( ( w + 4 ) & 7 );
if( y < 0 ) y += h, x += 4 - ( ( h + 4 ) & 7 );
/* region gap */
bit( x + 2 * ( x / fw | 0 ) + 1, y + 2 * ( y / fh | 0 ) + 1 );
}
}
}
/* unfilled corner */
for( i = w; i & 3; i-- ) {
bit( i, i );
}
xx = w + 2 * nc,
yy = h + 2 * nr;
}
return ( function() {
function ishex( c ) {
return /^#[0-9a-f]{3}(?:[0-9a-f]{3})?$/i.test( c );
}
function svg( n, a ) {
n = document.createElementNS( ns, n );
for( var o in a || {} ) {
n.setAttribute( o, a[ o ] );
}
return n;
}
var
abs = Math.abs,
r, x, y, d, sx, sy,
ns = 'http://www.w3.org/2000/svg',
path = '',
q = ('string' == typeof Q ) ? { msg: Q } : Q || {},
p = q.pal || ['#000'],
dm = abs( q.dim ) || 256,
pd = abs( q.pad ), pd = ( pd > -1 ) ? pd : 0,
mx = [ 1, 0, 0, 1, pd, pd ],
fg = p[ 0 ], fg = ishex( fg ) ? fg : '#000',
bg = p[ 1 ], bg = ishex( bg ) ? bg : 0,
/* render optimized or verbose svg */
optimized = ( q.vrb ) ? 0 : 1;
encodeMsg( q.msg || '', q.rct );
sx = xx + pd * 2,
sy = yy + pd * 2;
y = yy;
while( y-- ) {
d = 0, x = xx;
while( x-- ) {
if( M[ y ][ x ] ) {
if( optimized ) {
d++;
if( !M[ y ][ x - 1 ] )
path += 'M' + x + ',' + y + 'h' + d +'v1h-' + d + 'v-1z', d = 0;
} else path += 'M' + x + ',' + y + 'h1v1h-1v-1z';
}
}
}
r = svg('svg', {
'viewBox' : [ 0, 0, sx, sy ].join(' ')
,'width' : dm / sy * sx | 0
,'height' : dm
,'fill' : fg
,'shape-rendering' : 'crispEdges'
,'xmlns' : ns
,'version' : '1.1'
} );
if( bg ) r.appendChild( svg('path', {
'fill' : bg
,'d' : 'M0,0v' + sy + 'h' + sx + 'V0H0Z'
} ) );
r.appendChild( svg('path', {
'transform' : 'matrix(' + mx + ')'
,'d' : path
} ) );
return r;
} )();
}