Skip to content

Commit

Permalink
#195: Added Code93 barcode reader;
Browse files Browse the repository at this point in the history
  • Loading branch information
serratus committed May 23, 2017
1 parent 2e4b14b commit e2b37c3
Show file tree
Hide file tree
Showing 17 changed files with 326 additions and 6 deletions.
1 change: 1 addition & 0 deletions example/file_input.html
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ <h3>Working with file-input</h3>
<option value="codabar">Codabar</option>
<option value="i2of5">Interleaved 2 of 5</option>
<option value="2of5">Standard 2 of 5</option>
<option value="code_93">Code 93</option>
</select>
</label>
<label>
Expand Down
1 change: 1 addition & 0 deletions example/live_w_locator.html
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ <h3>The user's camera</h3>
<option value="codabar">Codabar</option>
<option value="i2of5">Interleaved 2 of 5</option>
<option value="2of5">Standard 2 of 5</option>
<option value="code_93">Code 93</option>
</select>
</label>
<label>
Expand Down
12 changes: 6 additions & 6 deletions example/live_w_locator.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,17 @@ $(function() {
capture: true,
capacity: 20,
blacklist: [{
code: "9577149002", format: "2of5"
code: "WIWV8ETQZ1", format: "code_93"
}, {
code: "5776158811", format: "2of5"
code: "EH3C-%GU23RK3", format: "code_93"
}, {
code: "0463381455", format: "2of5"
code: "O308SIHQOXN5SA/PJ", format: "code_93"
}, {
code: "3261594101", format: "2of5"
code: "DG7Q$TV8JQ/EN", format: "code_93"
}, {
code: "6730705801", format: "2of5"
code: "VOFD1DB5A.1F6QU", format: "code_93"
}, {
code: "8568166929", format: "2of5"
code: "4SO64P4X8 U4YUU1T-", format: "code_93"
}],
filter: function(codeResult) {
// only store results which match this constraint
Expand Down
1 change: 1 addition & 0 deletions example/static_images.html
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ <h3>Working with static images</h3>
<option value="i2of5">I2of5</option>
<option value="i2of5">Interleaved 2 of 5</option>
<option value="2of5">Standard 2 of 5</option>
<option value="code_93">Code 93</option>
</select>
</fieldset>
</div>
Expand Down
2 changes: 2 additions & 0 deletions src/decoder/barcode_decoder.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import EAN5Reader from '../reader/ean_5_reader';
import UPCEReader from '../reader/upc_e_reader';
import I2of5Reader from '../reader/i2of5_reader';
import TwoOfFiveReader from '../reader/2of5_reader';
import Code93Reader from '../reader/code_93_reader';

const READERS = {
code_128_reader: Code128Reader,
Expand All @@ -26,6 +27,7 @@ const READERS = {
upc_e_reader: UPCEReader,
i2of5_reader: I2of5Reader,
'2of5_reader': TwoOfFiveReader,
code_93_reader: Code93Reader
};
export default {
create: function(config, inputImageWrapper) {
Expand Down
278 changes: 278 additions & 0 deletions src/reader/code_93_reader.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,278 @@
import BarcodeReader from './barcode_reader';
import ArrayHelper from '../common/array_helper';

function Code93Reader() {
BarcodeReader.call(this);
}

const ALPHABETH_STRING = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. $/+%abcd*";

var properties = {
ALPHABETH_STRING: {value: ALPHABETH_STRING},
ALPHABET: {value: ALPHABETH_STRING.split('').map(char => char.charCodeAt(0))},
CHARACTER_ENCODINGS: {value: [
0x114, 0x148, 0x144, 0x142, 0x128, 0x124, 0x122, 0x150, 0x112, 0x10A,
0x1A8, 0x1A4, 0x1A2, 0x194, 0x192, 0x18A, 0x168, 0x164, 0x162, 0x134,
0x11A, 0x158, 0x14C, 0x146, 0x12C, 0x116, 0x1B4, 0x1B2, 0x1AC, 0x1A6,
0x196, 0x19A, 0x16C, 0x166, 0x136, 0x13A, 0x12E, 0x1D4, 0x1D2, 0x1CA,
0x16E, 0x176, 0x1AE, 0x126, 0x1DA, 0x1D6, 0x132, 0x15E
]},
ASTERISK: {value: 0x15E},
FORMAT: {value: "code_93", writeable: false}
};

Code93Reader.prototype = Object.create(BarcodeReader.prototype, properties);
Code93Reader.prototype.constructor = Code93Reader;

Code93Reader.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;
};

Code93Reader.prototype._decode = function() {
var self = this,
counters = [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);
if (decodedChar < 0){
return null;
}
result.push(decodedChar);
lastStart = nextStart;
nextStart += ArrayHelper.sum(counters);
nextStart = self._nextSet(self._row, nextStart);
} while (decodedChar !== '*');
result.pop();

if (!result.length) {
return null;
}

if (!self._verifyEnd(lastStart, nextStart, counters)) {
return null;
}

if (!self._verifyChecksums(result)) {
return null;
}

result = result.slice(0, result.length - 2);
if ((result = self._decodeExtended(result)) === null) {
return null;
};

return {
code: result.join(""),
start: start.start,
end: nextStart,
startInfo: start,
decodedCodes: result
};
};

Code93Reader.prototype._verifyEnd = function(lastStart, nextStart) {
if (lastStart === nextStart || !this._row[nextStart]) {
return false;
}
return true;
};

Code93Reader.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]);
}
}
return -1;
};

Code93Reader.prototype._toPattern = function(counters) {
const numCounters = counters.length;
let pattern = 0;
let sum = 0;
for (let i = 0; i < numCounters; i++) {
sum += counters[i];
}

for (let i = 0; i < numCounters; i++) {
let normalized = Math.round(counters[i] * 9 / sum);
if (normalized < 1 || normalized > 4) {
return -1;
}
if ((i & 1) === 0) {
for (let j = 0; j < normalized; j++) {
pattern = (pattern << 1) | 1;
}
} else {
pattern <<= normalized;
}
}

return pattern;
};

Code93Reader.prototype._findStart = function() {
var self = this,
offset = self._nextSet(self._row),
patternStart = offset,
counter = [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 < 4; j++) {
counter[j] = counter[j + 2];
}
counter[4] = 0;
counter[5] = 0;
counterPos--;
} else {
counterPos++;
}
counter[counterPos] = 1;
isWhite = !isWhite;
}
}
return null;
};

Code93Reader.prototype._decodeExtended = function(charArray) {
const length = charArray.length;
const result = [];
for (let i = 0; i < length; i++) {
const char = charArray[i];
if (char >= 'a' && char <= 'd') {
if (i > (length - 2)) {
return null;
}
const nextChar = charArray[++i];
const nextCharCode = nextChar.charCodeAt(0);
let decodedChar;
switch (char) {
case 'a':
if (nextChar >= 'A' && nextChar <= 'Z') {
decodedChar = String.fromCharCode(nextCharCode - 64);
} else {
return null;
}
break;
case 'b':
if (nextChar >= 'A' && nextChar <= 'E') {
decodedChar = String.fromCharCode(nextCharCode - 38);
} else if (nextChar >= 'F' && nextChar <= 'J') {
decodedChar = String.fromCharCode(nextCharCode - 11);
} else if (nextChar >= 'K' && nextChar <= 'O') {
decodedChar = String.fromCharCode(nextCharCode + 16);
} else if (nextChar >= 'P' && nextChar <= 'S') {
decodedChar = String.fromCharCode(nextCharCode + 43);
} else if (nextChar >= 'T' && nextChar <= 'Z') {
decodedChar = String.fromCharCode(127);
} else {
return null;
}
break;
case 'c':
if (nextChar >= 'A' && nextChar <= 'O') {
decodedChar = String.fromCharCode(nextCharCode - 32);
} else if (nextChar == 'Z') {
decodedChar = ':';
} else {
return null;
}
break;
case 'd':
if (nextChar >= 'A' && nextChar <= 'Z') {
decodedChar = String.fromCharCode(nextCharCode + 32);
} else {
return null;
}
break;
}
result.push(decodedChar);
} else {
result.push(char);
}
}
return result;
};

Code93Reader.prototype._verifyChecksums = function(charArray) {
return this._matchCheckChar(charArray, charArray.length - 2, 20)
&& this._matchCheckChar(charArray, charArray.length - 1, 15);
};

Code93Reader.prototype._matchCheckChar = function(charArray, index, maxWeight) {
const arrayToCheck = charArray.slice(0, index);
const length = arrayToCheck.length;
const weightedSums = arrayToCheck.reduce((sum, char, i) => {
const weight = (((i * -1) + (length - 1)) % maxWeight) + 1;
const value = this.ALPHABET.indexOf(char.charCodeAt(0));
return sum + (weight * value);
}, 0);

const checkChar = this.ALPHABET[(weightedSums % 47)];
return checkChar === charArray[index].charCodeAt(0);
};

export default Code93Reader;
Binary file added test/fixtures/code_93/image-001.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added test/fixtures/code_93/image-002.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added test/fixtures/code_93/image-003.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added test/fixtures/code_93/image-004.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added test/fixtures/code_93/image-005.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added test/fixtures/code_93/image-006.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added test/fixtures/code_93/image-007.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added test/fixtures/code_93/image-008.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added test/fixtures/code_93/image-009.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added test/fixtures/code_93/image-010.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
37 changes: 37 additions & 0 deletions test/integration/integration.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -341,4 +341,41 @@ describe('decodeSingle', function () {

_runTestSet(testSet, config);
});

describe("code_93", function() {
var config = config = {
inputStream: {
size: 800,
singleChannel: false
},
locator: {
patchSize: "large",
halfSample: true
},
numOfWorkers: 0,
decoder: {
readers: ["code_93_reader"]
},
locate: true,
src: null
},
testSet = [
{"name": "image-001.jpg", "result": "WIWV8ETQZ1"},
{"name": "image-002.jpg", "result": "EH3C-%GU23RK3"},
{"name": "image-003.jpg", "result": "O308SIHQOXN5SA/PJ"},
{"name": "image-004.jpg", "result": "DG7Q$TV8JQ/EN"},
{"name": "image-005.jpg", "result": "DG7Q$TV8JQ/EN"},
{"name": "image-006.jpg", "result": "O308SIHQOXN5SA/PJ"},
{"name": "image-007.jpg", "result": "VOFD1DB5A.1F6QU"},
{"name": "image-008.jpg", "result": "WIWV8ETQZ1"},
{"name": "image-009.jpg", "result": "4SO64P4X8 U4YUU1T-"},
{"name": "image-010.jpg", "result": "4SO64P4X8 U4YUU1T-"}
];

testSet.forEach(function(sample) {
sample.format = "code_93";
});

_runTestSet(testSet, config);
});
});

0 comments on commit e2b37c3

Please sign in to comment.