diff --git a/README.md b/README.md index 450f801..3530e49 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ quaggaJS ======== -- [Changelog](#changelog) (2015-01-21) +- [Changelog](#changelog) (2015-03-04) QuaggaJS is a barcode-scanner entirely written in JavaScript supporting real-time localization and decoding of various types of barcodes such as __EAN__ and __CODE128__. The library is also capable of using `getUserMedia` to get direct @@ -262,6 +262,10 @@ In case you want to take a deeper dive into the inner workings of Quagga, get to ## Changelog +### 2015-03-04 +- Features + - Added support for [Code 39][code39_wiki] barcodes + ### 2015-01-21 - Features - Added support for web-worker (using 4 workers as default, can be changed through `config.numOfWorkers`) @@ -283,3 +287,4 @@ In the course of implementing web-workers some breaking changes were introduced [chaiUrl]: http://chaijs.com/ [mochaUrl]: https://github.com/mochajs/mocha [karmaUrl]: http://karma-runner.github.io/ +[code39_wiki]: http://en.wikipedia.org/wiki/Code_39 diff --git a/dist/quagga.js b/dist/quagga.js index d51693e..e2497ab 100644 --- a/dist/quagga.js +++ b/dist/quagga.js @@ -470,10 +470,11 @@ define( return error; }; - BarcodeReader.prototype._nextSet = function(line) { + BarcodeReader.prototype._nextSet = function(line, offset) { var i; - - for (i = 0; i < line.length; i++) { + + offset = offset || 0; + for (i = offset; i < line.length; i++) { if (line[i]) { return i; } @@ -596,6 +597,18 @@ define( } return result; }; + + BarcodeReader.prototype._matchRange = function(start, end, value) { + var i; + + start = start < 0 ? 0 : start; + for (i = start; i < end; i++) { + if (this._row[i] !== value) { + return false; + } + } + return true; + }; BarcodeReader.DIRECTION = { FORWARD : 1, @@ -4180,6 +4193,16 @@ define('array_helper',[],function() { } } return max; + }, + + sum: function(arr) { + var length = arr.length, + sum = 0; + + while(length--) { + sum += arr[length]; + } + return sum; } }; }); @@ -6416,12 +6439,213 @@ define('bresenham',[],function() { /* jshint undef: true, unused: true, browser:true, devel: true */ /* global define */ -define('barcode_decoder',["bresenham", "image_debug", 'code_128_reader', 'ean_reader'], function(Bresenham, ImageDebug, Code128Reader, EANReader) { +define( + 'code_39_reader',[ + "./barcode_reader", + "./array_helper" + ], + function(BarcodeReader, ArrayHelper) { + + + function Code39Reader() { + BarcodeReader.call(this); + } + + var properties = { + ALPHABETH_STRING: {value: "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. *$/+%"}, + ALPHABET: {value: [48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 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, 45, 46, 32, 42, 36, 47, 43, 37]}, + CHARACTER_ENCODINGS: {value: [0x034, 0x121, 0x061, 0x160, 0x031, 0x130, 0x070, 0x025, 0x124, 0x064, 0x109, 0x049, 0x148, 0x019, 0x118, 0x058, 0x00D, 0x10C, 0x04C, 0x01C, 0x103, 0x043, 0x142, 0x013, 0x112, 0x052, 0x007, 0x106, 0x046, 0x016, 0x181, 0x0C1, 0x1C0, 0x091, 0x190, 0x0D0, 0x085, 0x184, 0x0C4, 0x094, 0x0A8, 0x0A2, 0x08A, 0x02A]}, + ASTERISK: {value: 0x094} + }; + + Code39Reader.prototype = Object.create(BarcodeReader.prototype, properties); + Code39Reader.prototype.constructor = Code39Reader; + + Code39Reader.prototype._toCounters = function(start, counter) { + var self = this, + numCounters = counter.length, + end = self._row.length, + isWhite = !self._row[start], + i, + counterPos = 0; + + ArrayHelper.init(counter, 0); + + for ( i = start; i < end; i++) { + if (self._row[i] ^ isWhite) { + counter[counterPos]++; + } else { + counterPos++; + if (counterPos === numCounters) { + break; + } else { + counter[counterPos] = 1; + isWhite = !isWhite; + } + } + } + + return counter; + }; + + Code39Reader.prototype._decode = function() { + var self = this, + counters = [0,0,0,0,0,0,0,0,0], + result = [], + start = self._findStart(), + decodedChar, + lastStart, + pattern, + nextStart; + + if (!start) { + return null; + } + nextStart = self._nextSet(self._row, start.end); + + do { + counters = self._toCounters(nextStart, counters); + pattern = self._toPattern(counters); + if (pattern < 0) { + return null; + } + decodedChar = self._patternToChar(pattern); + result.push(decodedChar); + lastStart = nextStart; + nextStart += ArrayHelper.sum(counters); + nextStart = self._nextSet(self._row, nextStart); + } while(decodedChar !== '*'); + result.pop(); + + + + return { + code : result.join(""), + start : start.start, + end : nextStart, + startInfo : start, + decodedCodes : result + }; + }; + + Code39Reader.prototype._patternToChar = function(pattern) { + var i, + self = this; + + for (i = 0; i < self.CHARACTER_ENCODINGS.length; i++) { + if (self.CHARACTER_ENCODINGS[i] === pattern) { + return String.fromCharCode(self.ALPHABET[i]); + } + } + }; + + Code39Reader.prototype._findNextWidth = function(counters, current) { + var i, + minWidth = Number.MAX_VALUE; + + for (i = 0; i < counters.length; i++) { + if (counters[i] < minWidth && counters[i] > current) { + minWidth = counters[i]; + } + } + + return minWidth; + }; + + Code39Reader.prototype._toPattern = function(counters) { + var numCounters = counters.length, + maxNarrowWidth = 0, + numWideBars = numCounters, + wideBarWidth = 0, + self = this, + pattern, + i; + + while(numWideBars > 3) { + maxNarrowWidth = self._findNextWidth(counters, maxNarrowWidth); + numWideBars = 0; + pattern = 0; + for (i = 0; i < numCounters; i++) { + if (counters[i] > maxNarrowWidth) { + pattern |= 1 << (numCounters - 1 - i); + numWideBars++; + wideBarWidth += counters[i]; + } + } + + if (numWideBars === 3) { + for (i = 0; i < numCounters && numWideBars > 0; i++) { + if (counters[i] > maxNarrowWidth) { + numWideBars--; + if ((counters[i] * 3) >= wideBarWidth) { + return -1; + } + } + } + return pattern; + } + } + return -1; + }; + + Code39Reader.prototype._findStart = function() { + var self = this, + offset = self._nextSet(self._row), + patternStart = offset, + counter = [0,0,0,0,0,0,0,0,0], + counterPos = 0, + isWhite = false, + i, + j, + whiteSpaceMustStart; + + for ( i = offset; i < self._row.length; i++) { + if (self._row[i] ^ isWhite) { + counter[counterPos]++; + } else { + if (counterPos === counter.length - 1) { + + // find start pattern + if (self._toPattern(counter) === self.ASTERISK) { + whiteSpaceMustStart = Math.floor(Math.max(0, patternStart - ((i - patternStart) / 4))); + if (self._matchRange(whiteSpaceMustStart, patternStart, 0)) { + return { + start: patternStart, + end: i + }; + } + } + + patternStart += counter[0] + counter[1]; + for ( j = 0; j < 7; j++) { + counter[j] = counter[j + 2]; + } + counter[7] = 0; + counter[8] = 0; + counterPos--; + } else { + counterPos++; + } + counter[counterPos] = 1; + isWhite = !isWhite; + } + } + return null; + }; + + return (Code39Reader); + } +); +/* jshint undef: true, unused: true, browser:true, devel: true */ +/* global define */ + +define('barcode_decoder',["bresenham", "image_debug", 'code_128_reader', 'ean_reader', 'code_39_reader'], function(Bresenham, ImageDebug, Code128Reader, EANReader, Code39Reader) { var readers = { code_128_reader: Code128Reader, - ean_reader: EANReader + ean_reader: EANReader, + code_39_reader: Code39Reader }; var BarcodeDecoder = { create : function(config, inputImageWrapper) { @@ -6967,7 +7191,7 @@ define('camera_access',["html_utils"], function(HtmlUtils) { } /** - * Tries to attach the camer-stream to a given video-element + * Tries to attach the camera-stream to a given video-element * and calls the callback function when the content is ready * @param {Object} constraints * @param {Object} video @@ -7001,6 +7225,12 @@ define('camera_access',["html_utils"], function(HtmlUtils) { }); } + /** + * Normalizes the incoming constraints to satisfy the current browser + * @param config + * @param cb Callback which is called whenever constraints are created + * @returns {*} + */ function normalizeConstraints(config, cb) { var constraints = { audio: false, diff --git a/example/live_w_locator.html b/example/live_w_locator.html index 9849539..f9b1f6b 100644 --- a/example/live_w_locator.html +++ b/example/live_w_locator.html @@ -33,6 +33,8 @@ + +
diff --git a/example/static_images.html b/example/static_images.html index bbbce6d..e95d159 100644 --- a/example/static_images.html +++ b/example/static_images.html @@ -31,8 +31,10 @@
+ + - +
@@ -41,7 +43,7 @@
-
+