Skip to content

Commit 0420d5e

Browse files
authored
Merge pull request #197 from serratus/feature/195
Added support for Code 93 barcodes. Closes #195
2 parents 2e4b14b + 57312a0 commit 0420d5e

19 files changed

+328
-33
lines changed

example/file_input.html

+1
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ <h3>Working with file-input</h3>
5353
<option value="codabar">Codabar</option>
5454
<option value="i2of5">Interleaved 2 of 5</option>
5555
<option value="2of5">Standard 2 of 5</option>
56+
<option value="code_93">Code 93</option>
5657
</select>
5758
</label>
5859
<label>

example/live_w_locator.html

+1
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ <h3>The user's camera</h3>
4545
<option value="codabar">Codabar</option>
4646
<option value="i2of5">Interleaved 2 of 5</option>
4747
<option value="2of5">Standard 2 of 5</option>
48+
<option value="code_93">Code 93</option>
4849
</select>
4950
</label>
5051
<label>

example/live_w_locator.js

+6-6
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,17 @@ $(function() {
33
capture: true,
44
capacity: 20,
55
blacklist: [{
6-
code: "9577149002", format: "2of5"
6+
code: "WIWV8ETQZ1", format: "code_93"
77
}, {
8-
code: "5776158811", format: "2of5"
8+
code: "EH3C-%GU23RK3", format: "code_93"
99
}, {
10-
code: "0463381455", format: "2of5"
10+
code: "O308SIHQOXN5SA/PJ", format: "code_93"
1111
}, {
12-
code: "3261594101", format: "2of5"
12+
code: "DG7Q$TV8JQ/EN", format: "code_93"
1313
}, {
14-
code: "6730705801", format: "2of5"
14+
code: "VOFD1DB5A.1F6QU", format: "code_93"
1515
}, {
16-
code: "8568166929", format: "2of5"
16+
code: "4SO64P4X8 U4YUU1T-", format: "code_93"
1717
}],
1818
filter: function(codeResult) {
1919
// only store results which match this constraint

example/static_images.html

+1
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ <h3>Working with static images</h3>
4747
<option value="i2of5">I2of5</option>
4848
<option value="i2of5">Interleaved 2 of 5</option>
4949
<option value="2of5">Standard 2 of 5</option>
50+
<option value="code_93">Code 93</option>
5051
</select>
5152
</fieldset>
5253
</div>

src/decoder/barcode_decoder.js

+2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import EAN5Reader from '../reader/ean_5_reader';
1212
import UPCEReader from '../reader/upc_e_reader';
1313
import I2of5Reader from '../reader/i2of5_reader';
1414
import TwoOfFiveReader from '../reader/2of5_reader';
15+
import Code93Reader from '../reader/code_93_reader';
1516

1617
const READERS = {
1718
code_128_reader: Code128Reader,
@@ -26,6 +27,7 @@ const READERS = {
2627
upc_e_reader: UPCEReader,
2728
i2of5_reader: I2of5Reader,
2829
'2of5_reader': TwoOfFiveReader,
30+
code_93_reader: Code93Reader
2931
};
3032
export default {
3133
create: function(config, inputImageWrapper) {

src/reader/barcode_reader.js

+29
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import ArrayHelper from '../common/array_helper';
2+
13
function BarcodeReader(config, supplements) {
24
this._row = [];
35
this.config = config || {};
@@ -195,6 +197,33 @@ BarcodeReader.prototype._fillCounters = function(offset, end, isWhite) {
195197
return counters;
196198
};
197199

200+
BarcodeReader.prototype._toCounters = function(start, counter) {
201+
var self = this,
202+
numCounters = counter.length,
203+
end = self._row.length,
204+
isWhite = !self._row[start],
205+
i,
206+
counterPos = 0;
207+
208+
ArrayHelper.init(counter, 0);
209+
210+
for ( i = start; i < end; i++) {
211+
if (self._row[i] ^ isWhite) {
212+
counter[counterPos]++;
213+
} else {
214+
counterPos++;
215+
if (counterPos === numCounters) {
216+
break;
217+
} else {
218+
counter[counterPos] = 1;
219+
isWhite = !isWhite;
220+
}
221+
}
222+
}
223+
224+
return counter;
225+
};
226+
198227
Object.defineProperty(BarcodeReader.prototype, "FORMAT", {
199228
value: 'unknown',
200229
writeable: false

src/reader/code_39_reader.js

-27
Original file line numberDiff line numberDiff line change
@@ -20,33 +20,6 @@ var properties = {
2020
Code39Reader.prototype = Object.create(BarcodeReader.prototype, properties);
2121
Code39Reader.prototype.constructor = Code39Reader;
2222

23-
Code39Reader.prototype._toCounters = function(start, counter) {
24-
var self = this,
25-
numCounters = counter.length,
26-
end = self._row.length,
27-
isWhite = !self._row[start],
28-
i,
29-
counterPos = 0;
30-
31-
ArrayHelper.init(counter, 0);
32-
33-
for ( i = start; i < end; i++) {
34-
if (self._row[i] ^ isWhite) {
35-
counter[counterPos]++;
36-
} else {
37-
counterPos++;
38-
if (counterPos === numCounters) {
39-
break;
40-
} else {
41-
counter[counterPos] = 1;
42-
isWhite = !isWhite;
43-
}
44-
}
45-
}
46-
47-
return counter;
48-
};
49-
5023
Code39Reader.prototype._decode = function() {
5124
var self = this,
5225
counters = [0, 0, 0, 0, 0, 0, 0, 0, 0],

src/reader/code_93_reader.js

+251
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,251 @@
1+
import BarcodeReader from './barcode_reader';
2+
import ArrayHelper from '../common/array_helper';
3+
4+
function Code93Reader() {
5+
BarcodeReader.call(this);
6+
}
7+
8+
const ALPHABETH_STRING = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. $/+%abcd*";
9+
10+
var properties = {
11+
ALPHABETH_STRING: {value: ALPHABETH_STRING},
12+
ALPHABET: {value: ALPHABETH_STRING.split('').map(char => char.charCodeAt(0))},
13+
CHARACTER_ENCODINGS: {value: [
14+
0x114, 0x148, 0x144, 0x142, 0x128, 0x124, 0x122, 0x150, 0x112, 0x10A,
15+
0x1A8, 0x1A4, 0x1A2, 0x194, 0x192, 0x18A, 0x168, 0x164, 0x162, 0x134,
16+
0x11A, 0x158, 0x14C, 0x146, 0x12C, 0x116, 0x1B4, 0x1B2, 0x1AC, 0x1A6,
17+
0x196, 0x19A, 0x16C, 0x166, 0x136, 0x13A, 0x12E, 0x1D4, 0x1D2, 0x1CA,
18+
0x16E, 0x176, 0x1AE, 0x126, 0x1DA, 0x1D6, 0x132, 0x15E
19+
]},
20+
ASTERISK: {value: 0x15E},
21+
FORMAT: {value: "code_93", writeable: false}
22+
};
23+
24+
Code93Reader.prototype = Object.create(BarcodeReader.prototype, properties);
25+
Code93Reader.prototype.constructor = Code93Reader;
26+
27+
Code93Reader.prototype._decode = function() {
28+
var self = this,
29+
counters = [0, 0, 0, 0, 0, 0],
30+
result = [],
31+
start = self._findStart(),
32+
decodedChar,
33+
lastStart,
34+
pattern,
35+
nextStart;
36+
37+
if (!start) {
38+
return null;
39+
}
40+
nextStart = self._nextSet(self._row, start.end);
41+
42+
do {
43+
counters = self._toCounters(nextStart, counters);
44+
pattern = self._toPattern(counters);
45+
if (pattern < 0) {
46+
return null;
47+
}
48+
decodedChar = self._patternToChar(pattern);
49+
if (decodedChar < 0){
50+
return null;
51+
}
52+
result.push(decodedChar);
53+
lastStart = nextStart;
54+
nextStart += ArrayHelper.sum(counters);
55+
nextStart = self._nextSet(self._row, nextStart);
56+
} while (decodedChar !== '*');
57+
result.pop();
58+
59+
if (!result.length) {
60+
return null;
61+
}
62+
63+
if (!self._verifyEnd(lastStart, nextStart, counters)) {
64+
return null;
65+
}
66+
67+
if (!self._verifyChecksums(result)) {
68+
return null;
69+
}
70+
71+
result = result.slice(0, result.length - 2);
72+
if ((result = self._decodeExtended(result)) === null) {
73+
return null;
74+
};
75+
76+
return {
77+
code: result.join(""),
78+
start: start.start,
79+
end: nextStart,
80+
startInfo: start,
81+
decodedCodes: result
82+
};
83+
};
84+
85+
Code93Reader.prototype._verifyEnd = function(lastStart, nextStart) {
86+
if (lastStart === nextStart || !this._row[nextStart]) {
87+
return false;
88+
}
89+
return true;
90+
};
91+
92+
Code93Reader.prototype._patternToChar = function(pattern) {
93+
var i,
94+
self = this;
95+
96+
for (i = 0; i < self.CHARACTER_ENCODINGS.length; i++) {
97+
if (self.CHARACTER_ENCODINGS[i] === pattern) {
98+
return String.fromCharCode(self.ALPHABET[i]);
99+
}
100+
}
101+
return -1;
102+
};
103+
104+
Code93Reader.prototype._toPattern = function(counters) {
105+
const numCounters = counters.length;
106+
let pattern = 0;
107+
let sum = 0;
108+
for (let i = 0; i < numCounters; i++) {
109+
sum += counters[i];
110+
}
111+
112+
for (let i = 0; i < numCounters; i++) {
113+
let normalized = Math.round(counters[i] * 9 / sum);
114+
if (normalized < 1 || normalized > 4) {
115+
return -1;
116+
}
117+
if ((i & 1) === 0) {
118+
for (let j = 0; j < normalized; j++) {
119+
pattern = (pattern << 1) | 1;
120+
}
121+
} else {
122+
pattern <<= normalized;
123+
}
124+
}
125+
126+
return pattern;
127+
};
128+
129+
Code93Reader.prototype._findStart = function() {
130+
var self = this,
131+
offset = self._nextSet(self._row),
132+
patternStart = offset,
133+
counter = [0, 0, 0, 0, 0, 0],
134+
counterPos = 0,
135+
isWhite = false,
136+
i,
137+
j,
138+
whiteSpaceMustStart;
139+
140+
for ( i = offset; i < self._row.length; i++) {
141+
if (self._row[i] ^ isWhite) {
142+
counter[counterPos]++;
143+
} else {
144+
if (counterPos === counter.length - 1) {
145+
// find start pattern
146+
if (self._toPattern(counter) === self.ASTERISK) {
147+
whiteSpaceMustStart = Math.floor(Math.max(0, patternStart - ((i - patternStart) / 4)));
148+
if (self._matchRange(whiteSpaceMustStart, patternStart, 0)) {
149+
return {
150+
start: patternStart,
151+
end: i
152+
};
153+
}
154+
}
155+
156+
patternStart += counter[0] + counter[1];
157+
for ( j = 0; j < 4; j++) {
158+
counter[j] = counter[j + 2];
159+
}
160+
counter[4] = 0;
161+
counter[5] = 0;
162+
counterPos--;
163+
} else {
164+
counterPos++;
165+
}
166+
counter[counterPos] = 1;
167+
isWhite = !isWhite;
168+
}
169+
}
170+
return null;
171+
};
172+
173+
Code93Reader.prototype._decodeExtended = function(charArray) {
174+
const length = charArray.length;
175+
const result = [];
176+
for (let i = 0; i < length; i++) {
177+
const char = charArray[i];
178+
if (char >= 'a' && char <= 'd') {
179+
if (i > (length - 2)) {
180+
return null;
181+
}
182+
const nextChar = charArray[++i];
183+
const nextCharCode = nextChar.charCodeAt(0);
184+
let decodedChar;
185+
switch (char) {
186+
case 'a':
187+
if (nextChar >= 'A' && nextChar <= 'Z') {
188+
decodedChar = String.fromCharCode(nextCharCode - 64);
189+
} else {
190+
return null;
191+
}
192+
break;
193+
case 'b':
194+
if (nextChar >= 'A' && nextChar <= 'E') {
195+
decodedChar = String.fromCharCode(nextCharCode - 38);
196+
} else if (nextChar >= 'F' && nextChar <= 'J') {
197+
decodedChar = String.fromCharCode(nextCharCode - 11);
198+
} else if (nextChar >= 'K' && nextChar <= 'O') {
199+
decodedChar = String.fromCharCode(nextCharCode + 16);
200+
} else if (nextChar >= 'P' && nextChar <= 'S') {
201+
decodedChar = String.fromCharCode(nextCharCode + 43);
202+
} else if (nextChar >= 'T' && nextChar <= 'Z') {
203+
decodedChar = String.fromCharCode(127);
204+
} else {
205+
return null;
206+
}
207+
break;
208+
case 'c':
209+
if (nextChar >= 'A' && nextChar <= 'O') {
210+
decodedChar = String.fromCharCode(nextCharCode - 32);
211+
} else if (nextChar === 'Z') {
212+
decodedChar = ':';
213+
} else {
214+
return null;
215+
}
216+
break;
217+
case 'd':
218+
if (nextChar >= 'A' && nextChar <= 'Z') {
219+
decodedChar = String.fromCharCode(nextCharCode + 32);
220+
} else {
221+
return null;
222+
}
223+
break;
224+
}
225+
result.push(decodedChar);
226+
} else {
227+
result.push(char);
228+
}
229+
}
230+
return result;
231+
};
232+
233+
Code93Reader.prototype._verifyChecksums = function(charArray) {
234+
return this._matchCheckChar(charArray, charArray.length - 2, 20)
235+
&& this._matchCheckChar(charArray, charArray.length - 1, 15);
236+
};
237+
238+
Code93Reader.prototype._matchCheckChar = function(charArray, index, maxWeight) {
239+
const arrayToCheck = charArray.slice(0, index);
240+
const length = arrayToCheck.length;
241+
const weightedSums = arrayToCheck.reduce((sum, char, i) => {
242+
const weight = (((i * -1) + (length - 1)) % maxWeight) + 1;
243+
const value = this.ALPHABET.indexOf(char.charCodeAt(0));
244+
return sum + (weight * value);
245+
}, 0);
246+
247+
const checkChar = this.ALPHABET[(weightedSums % 47)];
248+
return checkChar === charArray[index].charCodeAt(0);
249+
};
250+
251+
export default Code93Reader;

test/fixtures/code_93/image-001.jpg

143 KB
Loading

test/fixtures/code_93/image-002.jpg

160 KB
Loading

test/fixtures/code_93/image-003.jpg

151 KB
Loading

test/fixtures/code_93/image-004.jpg

128 KB
Loading

test/fixtures/code_93/image-005.jpg

154 KB
Loading

test/fixtures/code_93/image-006.jpg

111 KB
Loading

test/fixtures/code_93/image-007.jpg

117 KB
Loading

test/fixtures/code_93/image-008.jpg

121 KB
Loading

test/fixtures/code_93/image-009.jpg

144 KB
Loading

test/fixtures/code_93/image-010.jpg

140 KB
Loading

0 commit comments

Comments
 (0)