rescode
Version:
Generate Codes (EAN13, QRCODE ..)
938 lines (849 loc) • 25.6 kB
JavaScript
var BWIPJS = function() {
// PostScript state
this.ptr = 0; // operand stack pointer
this.stk = []; // operand stack
this.dict = {}; // current dictionary
this.dstk = [this.dict]; // dictionary stack
this.gstk = []; // graphics state stack
this.bmap = null; // host bitmap instance
// dict-stack lookup
this.dstk.get = function(id) {
for (var i = this.length-1; i >= 0; i--) {
if (this[i][id] !== undefined) {
//BWIPJS.print('dstk::get=' + BWIPJS.pstostring(this[i][id]) +
// ' (' + (this[i][id] instanceof Function) + ')');
return this[i][id];
}
}
};
// Initialize the graphics
this.greset();
};
// Objects for globally registering the encoders and fonts
BWIPJS.bwipp = {};
BWIPJS.fonts = {};
// Host-specific overrides
BWIPJS.print = function(s) {};
BWIPJS.debug = function(s) {};
//BWIPJS.load = function(s) {}; // force a run-time error
BWIPJS.psarray = function(v) {
if (!(this instanceof BWIPJS.psarray))
return new BWIPJS.psarray(v);
if (typeof(v) == "number") {
var a = [];
for (var i = 0; i < v; i++)
a[i] = null;
this.value = a;
this.length = v;
this.offset = 0;
}
else if (v instanceof Array) {
this.value = v;
this.offset = 0;
this.length = v.length;
}
else { // v is a psarray
this.value = [];
this.length = v.length;
this.offset = 0;
for (var i = 0; i < v.length; i++)
this.value[i] = v.value[v.offset+i];
}
// Define the indexes (just for show) so forall works correctly.
// The actual implementation uses get/set.
for (var i = 0; i < this.length; i++)
this[i] = undefined;
}
BWIPJS.psarray.prototype.toString = function() {
var s = '';
for (var i = this.offset; i < this.offset+this.length; i++)
s += ' ' + BWIPJS.pstostring(this.value[i]);
return '[' + s.substr(1) + ']';
}
BWIPJS.psarray.prototype.valueOf = function() {
var s = '';
for (var i = this.offset; i < this.offset+this.length; i++)
s += ' ' + BWIPJS.pstostring(this.value[i]);
return '[' + s.substr(1) + ']';
}
BWIPJS.psarray.prototype.get = function(n) {
return this.value[this.offset+parseFloat(n)];
}
BWIPJS.psarray.prototype.set = function(n, v) {
this.value[this.offset+parseFloat(n)] = v;
}
// extracts a live subset/range of elements
BWIPJS.psarray.prototype.subset = function(offset, length) {
if (isNaN(length) || offset+length > this.length)
length = this.length-offset;
var copy = new BWIPJS.psarray(length);
copy.value = this.value;
copy.offset = this.offset + offset;
return copy;
}
// assigns a subset/range of elements
BWIPJS.psarray.prototype.assign = function(offset, source) {
if (source instanceof Array) {
// array optimization
if (this.length == this.value.length && this.length == source.length)
this.value = source;
else
for (var i = 0; i < source.length; i++)
this.value[this.offset+offset+i] = source[i];
}
else
for (var i = 0; i < source.length; i++)
this.value[this.offset+offset+i] = source.value[source.offset+i];
}
BWIPJS.psstring = function(v) {
if (!(this instanceof BWIPJS.psstring))
return new BWIPJS.psstring(v);
if (typeof(v) == 'number') {
this.value = [];
this.length = v;
this.offset = 0;
for (var i = 0; i < v; i++)
this.value[i] = 0;
}
else if (typeof(v) == 'string') {
this.value = [];
this.length = v.length;
this.offset = 0;
for (var i = 0; i < v.length; i++)
this.value[i] = v.charCodeAt(i);
}
else { // v is a psstring
this.value = [];
this.length = v.length;
this.offset = 0;
for (var i = 0; i < v.length; i++)
this.value[i] = v.value[v.offset+i];
}
// Define the indexes (just for show) so forall works correctly.
// The actual implementation uses get/set.
for (var i = 0; i < this.length; i++)
this[i] = NaN;
}
BWIPJS.psstring.prototype.toString = function() {
var s = '';
for (var i = this.offset; i < this.offset+this.length; i++)
s += String.fromCharCode(this.value[i]);
return s;
}
BWIPJS.psstring.prototype.valueOf = function() {
var s = '';
for (var i = this.offset; i < this.offset+this.length; i++)
s += String.fromCharCode(this.value[i]);
return s;
}
BWIPJS.psstring.prototype.get = function(n) {
return this.value[this.offset+parseFloat(n)];
}
BWIPJS.psstring.prototype.set = function(n, v) {
this.value[this.offset+parseFloat(n)] = v;
}
// extracts a live subset/range of elements
BWIPJS.psstring.prototype.subset = function(offset, length) {
if (isNaN(length) || offset+length > this.length)
length = this.length-offset;
var copy = new BWIPJS.psstring(length);
copy.value = this.value;
copy.offset = this.offset + offset;
return copy;
}
// assigns a subset/range of elements
BWIPJS.psstring.prototype.assign = function(offset, source) {
if (typeof(source) === 'string') {
for (var i = 0; i < source.length; i++)
this.value[this.offset+offset+i] = source.charCodeAt(i);
}
else {
for (var i = 0; i < source.length; i++)
this.value[this.offset+offset+i] = source.value[source.offset+i];
}
}
BWIPJS.psstring.prototype.indexOf = function(s) {
return this.toString().indexOf(s.toString());
}
BWIPJS.pstype = function(v) {
// null can cause trouble - get it out of the way
if (v === null || v === undefined)
return 'nulltype';
var t = typeof(v);
if (t == 'number')
return v % 1 ? 'realtype' : 'integertype';
if (t == 'boolean')
return 'booleantype';
if (v instanceof BWIPJS.psarray)
return 'arraytype';
if (v instanceof BWIPJS.psstring)
return 'stringtype';
return 'dicttype';
}
BWIPJS.pstostring = function(v) {
// null can cause trouble - get it out of the way
if (v === null)
return 'null';
if (typeof(v) == 'function')
return '--function--';
if (v instanceof BWIPJS.psarray)
return v.toString();
if (v instanceof BWIPJS.psstring) {
// postscript strings often contain binary data...
var s = '(';
for (var i = 0; i < v.length; i++) {
var cd = v.value[v.offset+i];
switch (cd) {
case 92: s += '\\\\'; break;
case 10: s += '\\n'; break;
case 13: s += '\\r'; break;
case 9: s += '\\t'; break;
case 8: s += '\\b'; break;
case 40: s += '\\('; break;
case 41: s += '\\)'; break;
default:
if (cd < 32 || cd > 127)
s += '\\'+(function(s) { return '000'.substr(s.length)+s; })(cd.toString(8));
else
s += String.fromCharCode(cd);
}
}
return s + ')';
}
if (typeof(v) == 'object') {
var s = '';
for (var i in v)
s += ' /' + i + ' ' + BWIPJS.pstostring(v[i]);
return '<<' + s + ' >>';
}
// Watch for the usual floating-point nonsense
if (typeof(v) == 'number' && v % 1) {
return v.toPrecision(12).replace(/0*$/,'');
}
return '' + v;
}
// BEGIN BWIPJS METHODS
// Host bitmap getter/setter
BWIPJS.prototype.bitmap = function(bmap) {
if (!bmap)
return this.bmap;
this.bmap = bmap;
}
// Converts a javascript value into a postscript value
BWIPJS.prototype.value = function(v) {
if (v === true || v === false || v === null)
return v;
var t = typeof(v);
if (t == 'number')
return v;
if (t == 'string')
return BWIPJS.psstring(v);
if (v instanceof Array)
return BWIPJS.psarray(v);
return v; // s.b. a dictionary object
}
BWIPJS.prototype.push = function(v) {
this.stk[this.ptr++] = this.value(v);
}
BWIPJS.prototype.pop = function() {
if (this.ptr <= 0)
throw '--underflow--';
return this.stk[--this.ptr];
}
BWIPJS.prototype.call = function(name) {
// Make sure the encoder is loaded
if (!BWIPJS.bwipp[name])
BWIPJS.load('bwipp/' + name + '.js');
if (!BWIPJS.bwipp[name])
throw name + ': --undefined--';
// Load into the dictionary
this.dict[name] = BWIPJS.bwipp[name];
// Make the call
BWIPJS.bwipp[name].call(this);
}
// eval on a psstring - emulates postscript '<string> exec'.
// BWIPP only requires support for two forms of eval; hex string literals in
// the form <rrggbb> and <ccmmyykk>.
BWIPJS.prototype.eval = function(src) {
src = src.toString(); // work with a javascript string
if (!/^<(([0-9A-F][0-9A-F])*)>$/i.test(src))
throw 'eval: not a hex string literal';
var dst = new BWIPJS.psstring((src.length-2)/2); // the RE above ensures pairs of digits
var idx = 0;
for (var i = 1; i < src.length-1; i += 2)
dst.set(idx++, parseInt(src.substr(i, 2), 16));
// Push the string onto the stack
this.stk[this.ptr++] = dst;
}
// All graphics state that must be gsaved/grestored are given
// an identifying prefix of g_
BWIPJS.prototype.greset = function() {
// Current Transform Matrix - since we don't do rotation, we can fake
// the matrix math
this.g_tdx = 0; // CTM x-offset
this.g_tdy = 0; // CTM y-offset
this.g_tsx = 1; // CTM x-scale factor
this.g_tsy = 1; // CTM y-scale factor
this.g_posx = 0; // current x position
this.g_posy = 0; // current y position
this.g_penw = 1; // current line/pen width
this.g_path = []; // current path
this.g_font = null; // current font object
this.g_rgb = [0,0,0]; // current color (black)
}
BWIPJS.prototype.currentpoint = function() {
return { x:(this.g_posx-this.g_tdx)/this.g_tsx, y:(this.g_posy-this.g_tdy)/this.g_tsy };
}
BWIPJS.prototype.currentfont = function() {
return this.g_font;
}
BWIPJS.prototype.findfont = function(name) { // name is a psstring
return { FontName:name };
}
// dtransform is a no-op
BWIPJS.prototype.dtransform = function(mtx,dx,dy) {
return { dx:dx, dy:dy };
}
BWIPJS.prototype.translate = function(x, y) {
this.g_tdx = this.g_tsx * x;
this.g_tdy = this.g_tsy * y;
}
BWIPJS.prototype.scale = function(x, y) {
this.g_tsx *= x;
this.g_tsy *= y;
}
BWIPJS.prototype.setlinewidth = function(w) {
this.g_penw = w;
}
BWIPJS.prototype.setfont = function(f) {
this.g_font = f;
}
BWIPJS.prototype.setrgb = function(r, g, b) {
var r = Math.round(r*255);
var g = Math.round(g*255);
var b = Math.round(b*255);
this.bmap.color(r, g, b);
this.g_rgb = [ r, g, b ];
}
BWIPJS.prototype.setcmyk = function(c, m, y, k) {
var r = Math.round((1-c) * (1-k) * 255);
var g = Math.round((1-m) * (1-k) * 255);
var b = Math.round((1-y) * (1-k) * 255);
this.bmap.color(r, g, b);
this.g_rgb = [ r, g, b ];
}
BWIPJS.prototype.newpath = function() {
this.g_path = [];
}
BWIPJS.prototype.closepath = function() {
if (this.g_path.length) {
var c0 = this.g_path[0];
var c1 = this.g_path[this.g_path.length-1];
this.g_path.push([ c1[0], c1[1] ]);
this.g_path.push(['c']);
this.g_path.push([ c0[0], c0[1] ]);
}
}
BWIPJS.prototype.moveto = function(x,y) {
this.g_posx = this.g_tdx + this.g_tsx * x;
this.g_posy = this.g_tdy + this.g_tsy * y;
BWIPJS.debug('moveto: posx,posy=(' + this.g_posx + ',' + this.g_posy + ')');
}
BWIPJS.prototype.rmoveto = function(x,y) {
this.g_posx += this.g_tsx * x;
this.g_posy += this.g_tsy * y;
BWIPJS.debug('rmoveto: posx,posy=(' + this.g_posx + ',' + this.g_posy + ')');
}
BWIPJS.prototype.lineto = function(x,y) {
this.g_path.push([this.g_posx, this.g_posy]);
this.g_path.push(['l']);
this.g_posx = this.g_tdx + this.g_tsx * x;
this.g_posy = this.g_tdy + this.g_tsy * y;
this.g_path.push([this.g_posx, this.g_posy]);
BWIPJS.debug('lineto: posx,posy=(' + this.g_posx + ',' + this.g_posy + ')');
}
BWIPJS.prototype.rlineto = function(x,y) {
this.g_path.push([this.g_posx, this.g_posy]);
this.g_path.push(['l']);
this.g_posx += this.g_tsx * x;
this.g_posy += this.g_tsy * y;
this.g_path.push([this.g_posx, this.g_posy]);
BWIPJS.debug('rlineto: posx,posy=(' + this.g_posx + ',' + this.g_posy + ')');
}
// implements both arc and arcn
BWIPJS.prototype.arc = function(x,y,r,sa,ea,ccw) {
if (sa == ea)
return;
// TBD: For now, we only implement full circles...
if (sa != 0 && sa != 360 || ea != 0 && ea != 360)
throw 'arc: not a full circle (' + sa + ',' + ea + ')';
// Calculate the bounding rect
x = this.g_tdx + this.g_tsx * x;
y = this.g_tdy + this.g_tsy * y;
var rx = r * this.g_tsx;
var ry = r * this.g_tsy;
this.g_path.push([ x-rx, y-ry ]);
this.g_path.push([ 'a', { x:x, y:y, rx:rx, ry:ry, sa:sa, ea:ea, ccw:ccw } ]);
this.g_path.push([ x+rx, y+ry ]);
}
// We don't have detailed font metrics to use for calculating correct
// font size. Therefore, out bitmaps are multiples of the base sizes
// used in barcode.ps: 10pt and 12pt
BWIPJS.prototype.getfont = function() {
// font scale factor
var fs = Math.floor(this.g_tsx);
if (fs < 1)
fs = 1;
else if (fs > 10)
fs = 10;
var key = this.g_font.FontSize + (fs < 10 ? '-0' : '-') + fs;
// Has the font been loaded?
if (!BWIPJS.fonts.OCRB || !BWIPJS.fonts.OCRB[key])
// Invoke the file loader
BWIPJS.load('fonts/ocrb' + key + '.js');
return !BWIPJS.fonts.OCRB ? undefined : BWIPJS.fonts.OCRB[key];
}
BWIPJS.prototype.stringwidth = function(str) {
var fn = this.getfont();
if (!fn) return { w:0, h:0 };
// width, ascent, and descent of the char-path
var w = 0, a = 0, d = 0;
for (var i = 0; i < str.length; i++) {
var ch = String.fromCharCode(str.get(i));
var g = fn.g[ch]; // The glyph
if (!g) {
w += fn.w; // Normalized char width
} else {
w += Math.max(g.l + g.w, fn.w);
if (g.t < 0) {
if (d < g.h - g.t)
d = g.h - g.t;
} else {
if (a < g.t)
a = g.t;
if (d < g.h - g.t)
d = g.h - g.t;
}
}
}
w += (str.length-1) * Math.floor(fn.w/4); // interchar gap
return { w:w/this.g_tsx, h:(a+d)/this.g_tsy };
}
BWIPJS.prototype.charpath = function(str, b) {
var sw = this.stringwidth(str);
// Emulate the char-path by placing a rectangle around it
this.rlineto(sw.w, 0);
this.rlineto(0, sw.h);
this.rlineto(-sw.w, 0);
}
BWIPJS.prototype.pathbbox = function() {
if (!this.g_path.length) throw 'pathbbox: --nocurrentpoint--';
var pth = this.g_path;
var llx = pth[0][0];
var lly = pth[0][1];
var urx = 0;
var ury = 0;
for (var i = 2, inc = 2; i < pth.length; i += inc) {
if (llx > pth[i][0]) llx = pth[i][0];
if (urx < pth[i][0]) urx = pth[i][0];
if (lly > pth[i][1]) lly = pth[i][1];
if (ury < pth[i][1]) ury = pth[i][1];
inc = (inc == 2 ? 1 : 2);
}
// Convert to user-space coordinates
return { llx:(llx-this.g_tdx)/this.g_tsx, lly:(lly-this.g_tdy)/this.g_tsy,
urx:(urx-this.g_tdx)/this.g_tsx, ury:(ury-this.g_tdy)/this.g_tsy };
}
BWIPJS.prototype.gsave = function() {
// clone all g_ properties
var ctx = {};
for (id in this)
if (id.indexOf('g_') == 0)
ctx[id] = this.gclone(this[id]);
this.gstk.push(ctx);
}
BWIPJS.prototype.grestore = function() {
if (!this.gstk.length)
throw 'grestore: stack underflow';
var ctx = this.gstk.pop();
for (id in ctx)
this[id] = ctx[id];
// color is part of the bitmap interface and must be restored separately
this.bmap.color(this.g_rgb[0], this.g_rgb[1], this.g_rgb[2]);
}
BWIPJS.prototype.stroke = function() {
var penx = this.g_penw*this.g_tsx;
var peny = this.g_penw*this.g_tsy;
var segs = this.g_path.length / 3; // number of line segments
if (this.g_path[this.g_path.length-2][0] == 'c')
segs--;
for (var i = 0; i < this.g_path.length; ) {
var s = this.g_path[i++]; // start point
var a = this.g_path[i++]; // args
var e = this.g_path[i++]; // end point
switch (a[0]) {
case 'l': // line
this.drawline(true, s[0], s[1], e[0], e[1], penx, peny, segs > 1);
break;
case 'a':
this.drawarc(s[0], s[1], e[0], e[1], a[1].sa, a[1].se, penx, peny);
break;
case 'c': // closepath
break;
default:
throw 'stroke: undefined opcode: ' + a[0];
}
}
this.g_path = [];
}
BWIPJS.prototype.fill = function() {
if (!this.g_path.length) // Nothing to do?
return;
if (this.g_path[this.g_path.length-2][0] != 'c') // Is the path closed?
this.closepath();
// Save off the existing bitmap and install a custom one for
// fast filling
var bmap = this.bmap;
this.bmap = new BWIPJS.fillmap;
for (var i = 0; i < this.g_path.length; ) {
var s = this.g_path[i++]; // start point
var a = this.g_path[i++]; // args
var e = this.g_path[i++]; // end point
switch (a[0]) {
case 'l': // line
this.drawline(false, s[0], s[1], e[0], e[1], 1, 1);
break;
case 'a': // arc/arcn
this.drawarc(s[0], s[1], e[0], e[1], a[1].sa, a[1].se, 1, 1);
break;
case 'c': // closepath
this.bmap.fill();
break;
default:
throw 'fill: undefined opcode: ' + a[0];
}
}
// Transfer the fill to the actual bitmap
this.bmap.xfer(bmap);
// Restore the actual bitmap
this.bmap = bmap;
this.g_path = [];
}
// source is an 8-bit bitmask
BWIPJS.prototype.imagemask = function(width, height, polarity, matrix, source) {
// Extract the matrix values for easier usage
var ma = matrix.get(0);
var mb = matrix.get(1);
var mc = matrix.get(2);
var md = matrix.get(3);
var mx = matrix.get(4);
var my = matrix.get(5);
var sx = this.g_tsx;
var sy = this.g_tsy;
var w2 = width * width;
var h2 = height * height;
var da = (ma < 0 ? 1 : 0);
var db = (mb < 0 ? 1 : 0);
var dc = (mc < 0 ? 1 : 0);
var dd = (md < 0 ? 1 : 0);
var rl = Math.ceil(width / 8); // row length (bytes per row)
for (var y = 0; y < height; y++) {
for (var x = 0; x < width; x++) {
var by = source.get(y*rl + Math.floor(x/8));
var bt = by & (1 << 7-(x%8));
if (bt && !polarity || !bt && polarity)
continue;
var x0 = Math.floor(this.g_tdx + ((x+da-mx)*ma + (y+dc-my)*mc) * sx / w2);
var y0 = Math.floor(this.g_tdy + ((y+dd-my)*md + (x+db-mx)*mb) * sy / h2);
var x1 = Math.floor(x0 + sx/width);
var y1 = Math.floor(y0 + sy/height);
for (var j = y0; j < y1; j++) {
for (var i = x0; i < x1; i++)
this.bmap.set(i,j);
}
}
}
}
// dx,dy are inter-character gaps
BWIPJS.prototype.show = function(str, dx, dy) { // str is a psstring
// The bitmap fonts were crafted to correspond to the scaling factor.
// Specifically to render correctly with the UPC/EAN text segments.
// Since UPC/EAN is a rather narrow code (in terms of modules/symbol),
// the scaling factor works for the other codes as well, including
// code128 in numeric mode with two digits per symbol.
var fn = this.getfont();
if (!fn) return;
// Convert dx,dy to device space
dx = this.g_tsx * dx;
dy = this.g_tsy * dy;
// PostScript renders bottom-up, so we must render the glyphs inverted.
for (var i = 0; i < str.length; i++) {
var ch = String.fromCharCode(str.get(i));
var g = fn.g[ch]; // The glyph
if (!g) {
this.g_posx += fn.w + Math.floor(fn.w/4) + dx; // w + interchar-gap
continue;
}
var bm = g.m; // The bitmap
// Adjust for the glyph's metrics
// The -2: -1 for the usual height-1 accounting;
// -1 because the bitmap generator has an off-by-one bug
// that we aren't going to fix.
var l = this.g_posx + g.l;
var t = this.g_posy + g.t + dy - 2;
var e = bm.charAt(0); // encoding
var tmin = t - g.h;
var lmax = l + g.w;
var bx = 1; // bitmap index
var by; // current bitmap byte
var rl; // run-length
if (e == 'b') {
// uncompressed bit-map
for (var y = t; y > tmin; y--)
for (var x = 0; x < g.w; x++) {
if (!(x % 8)) {
by = (parseInt(bm.charAt(bx),16) << 4) | parseInt(bm.charAt(bx+1),16);
bx += 2;
}
if (by & 0x80)
this.bmap.set(l+x, y);
by <<= 1;
}
} else if (e == 'x') {
// x-run encoding
for (var y = t; y > tmin; y--) {
for (var x = l; x < lmax; ) {
by = (parseInt(bm.charAt(bx),16) << 4) | parseInt(bm.charAt(bx+1),16);
rl = by >> 1;
bx += 2;
if (by & 1) {
while (rl--)
this.bmap.set(x++, y);
} else
x += rl;
}
}
} else if (e == 'y') {
// y-run encoding
for (var x = l; x < lmax; x++) {
for (var y = t; y > tmin; ) {
by = (parseInt(bm.charAt(bx),16) << 4) | parseInt(bm.charAt(bx+1),16);
rl = by >> 1;
bx += 2;
if (by & 1) {
while (rl--)
this.bmap.set(x, y--);
} else
y -= rl;
}
}
} else
throw 'unknown font bitmap encoding: ' + e;
this.g_posx += Math.max(g.l+g.w, fn.w) + Math.floor(fn.w/4) + dx;
}
}
// Perform a deep clone of the graphics state properties
BWIPJS.prototype.gclone = function(o) {
if (o instanceof Array) {
var t = [];
for (var i = 0; i < o.length; i++)
t[i] = this.gclone(o[i]);
return t;
}
//if (o instanceof Function)
// return o;
if (o instanceof Object) {
var t = {};
for (i in o)
t[i] = this.gclone(o[i]);
return t;
}
return o;
}
// Line algorithm that produces a more symmetric line than Bresenham's
//
// optmz == boolean
// x1,y1 == starting coordinates
// x2,y2 == ending coordinates
// penx,peny == pen dimensions
// merge == multi-line : merge the end points
//
// When optmz is true, we use the fast vertical/horizontal line drawing
// optimizations. This works well for single lines.
// When optmz is false, we always use the arbitrary line drawing alg, as
// it better connects one line with the next.
BWIPJS.prototype.drawline = function(optmz, x1, y1, x2, y2, penx, peny, merge) {
if (optmz && (x1 == x2 || y1 == y2)) {
var lx = Math.round(penx);
var ly = Math.round(peny);
if (y2 < y1) { var t = y1; y1 = y2; y2 = t; }
if (x2 < x1) { var t = x1; x1 = x2; x2 = t; }
// Horizontal or vertical line?
if (x1 == x2) {
// Vertical line
x1 = Math.round(x1 - lx/2);
x2 = Math.round(x2 + lx/2);
y1 = Math.round(y1 - (merge ? ly/2 : 0));
y2 = Math.round(y2 + (merge ? ly/2 : 0));
} else {
// Horizontal line
y1 = Math.round(y1 - ly/2);
y2 = Math.round(y2 + ly/2);
x1 = Math.round(x1 - (merge ? lx/2 : 0));
x2 = Math.round(x2 + (merge ? lx/2 : 0));
}
for (var y = y1; y < y2; y++)
for (var x = x1; x < x2; x++)
this.bmap.set(x,y);
return;
}
// Draw an arbitrary line
x1 = Math.floor(x1);
x2 = Math.floor(x2);
y1 = Math.floor(y1);
y2 = Math.floor(y2);
var du = Math.abs(x2-x1);
var dv = Math.abs(y2-y1);
var kx = (x2 < x1 ? -1 : 1);
var ky = (y2 < y1 ? -1 : 1);
var x = x1;
var y = y1;
var d = 0;
// Calculate the effect of pen width
var penw = Math.floor(Math.sqrt(penx*penx + peny*peny));
var pixh = Math.round(Math.sqrt((penw*penw)/((dv*dv)/(du*du)+1))) || 1;
var pixw = Math.round(Math.sqrt(penw*penw-pixh*pixh)) || 1;
if (du >= dv) {
// Increment on x
while (x != x2) {
for (var j = 0; j < pixh; j++)
this.bmap.set(x, y+j);
d += dv;
if (d >= du) {
d -= du;
y += ky;
}
x += kx;
}
for (var j = 0; j < pixh; j++)
this.bmap.set(x, y+j);
} else {
// Increment on y
while (y != y2) {
for (var j = 0; j < pixw; j++)
this.bmap.set(x+j, y);
d += du;
if (d >= dv) {
d -= dv;
x += kx;
}
y += ky;
}
for (var j = 0; j < pixw; j++)
this.bmap.set(x+j, y);
}
} // end of drawline()
BWIPJS.prototype.drawarc = function(x0, y0, x1, y1, sa, se, penx, peny) {
var a = Math.abs(x1-x0);
var b = Math.abs(y1-y0);
var b1 = b & 1;
var dx = 4*(1-a)*b*b;
var dy = 4*(b1+1)*a*a;
var err = dx + dy + b1*a*a;
var e2;
if (x0 > x1) { x0 = x1; x1 += a; }
if (y0 > y1) y0 = y1;
y0 += Math.floor((b+1)/2);
y1 = y0 - b1;
a *= 8*a; b1 = 8*b*b;
do {
this.bmap.set(x1, y0); // 1st quadrant
this.bmap.set(x0, y0); // 2nd quadrant
this.bmap.set(x0, y1); // 3rd quadrant
this.bmap.set(x1, y1); // 4th quadrant
e2 = 2*err;
if (e2 >= dx) { x0++; x1--; dx += b1; err += dx; }
if (e2 <= dy) { y0++; y1--; dy += a; err += dy; }
} while (x0 <= x1);
while (y0-y1 < b) { // too early stop of flat ellipse
this.bmap.set(x0-1, y0);
this.bmap.set(x1+1, y0++);
this.bmap.set(x0-1, y1);
this.bmap.set(x1+1, y1--);
}
}
// A bitmap implementation for faster filling.
// TBD: Too simplistic algorithm - needs to be enhanced.
// barcode.ps has very simple fill rqmts - we implement fill by walking
// away from the center of the path-box. And to handle the concentric
// rings in maxicode, each fill operation inverts the previous content.
//
// The bitmap will contain three possible values per bit:
// undefined : hasn't been set()
// 0 (zero) : was set by the current path operation
// 1 (one) : was set by the previous fill()
//
// When filling, all zeros and undefines (bounded by the path) will be
// inverted to ones. All ones will be inverted to undefined.
BWIPJS.fillmap = function() {
var bmap = [];
var minx = Infinity;
var miny = Infinity;
var maxx = -Infinity;
var maxy = -Infinity;
this.set = function(x,y) {
x = ~~x;
y = ~~y;
if (!bmap[y])
bmap[y] = [];
bmap[y][x] = 0;
if (minx > x) minx = x;
if (maxx < x) maxx = x;
if (miny > y) miny = y;
if (maxy < y) maxy = y;
}
function get(x,y) {
//return (bmap[y] ? bmap[y][x] : undefined);
return bmap[y][x];
}
function inv(x,y) {
bmap[y][x] = bmap[y][x] == 1 ? undefined : 1;
}
// A very simplistic filling algorithm - will only handle regular shapes
this.fill = function() {
var x0 = Math.floor(minx + (maxx-minx) / 2);
var y0 = Math.floor(miny + (maxy-miny) / 2);
// Fill up
for (var y = y0; y <= maxy; y++) {
// Find the bounds for this line
for (var t0 = minx; t0 <= maxx && get(t0,y) !== 0; t0++);
for (var t1 = maxx; t1 >= minx && get(t1,y) !== 0; t1--);
while (t0 <= t1)
inv(t0++, y);
}
// Fill down
for (var y = y0-1; y >= miny; y--) {
// Find the bounds for this line
for (var t0 = minx; t0 <= maxx && get(t0,y) !== 0; t0++);
for (var t1 = maxx; t1 >= minx && get(t1,y) !== 0; t1--);
while (t0 <= t1)
inv(t0++, y);
}
}
this.xfer = function(bmap) {
var x0 = minx;
var x1 = maxx;
var y0 = miny;
var y1 = maxy;
var out = '';
for (var y = y0; y <= y1; y++) {
for (var x = x0; x <= x1; x++) {
if (get(x,y) === 1)
bmap.set(x,y);
out += get(x,y) === 1 ? 'X' : '0';
}
out += '\r\n';
}
}
}
module.exports = BWIPJS;