• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2008 ZXing authors
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.google.zxing.oned;
18 
19 import com.google.zxing.BarcodeFormat;
20 import com.google.zxing.ChecksumException;
21 import com.google.zxing.DecodeHintType;
22 import com.google.zxing.FormatException;
23 import com.google.zxing.NotFoundException;
24 import com.google.zxing.Result;
25 import com.google.zxing.ResultMetadataType;
26 import com.google.zxing.ResultPoint;
27 import com.google.zxing.common.BitArray;
28 
29 import java.util.ArrayList;
30 import java.util.List;
31 import java.util.Map;
32 
33 /**
34  * <p>Decodes Code 128 barcodes.</p>
35  *
36  * @author Sean Owen
37  */
38 public final class Code128Reader extends OneDReader {
39 
40   static final int[][] CODE_PATTERNS = {
41       {2, 1, 2, 2, 2, 2}, // 0
42       {2, 2, 2, 1, 2, 2},
43       {2, 2, 2, 2, 2, 1},
44       {1, 2, 1, 2, 2, 3},
45       {1, 2, 1, 3, 2, 2},
46       {1, 3, 1, 2, 2, 2}, // 5
47       {1, 2, 2, 2, 1, 3},
48       {1, 2, 2, 3, 1, 2},
49       {1, 3, 2, 2, 1, 2},
50       {2, 2, 1, 2, 1, 3},
51       {2, 2, 1, 3, 1, 2}, // 10
52       {2, 3, 1, 2, 1, 2},
53       {1, 1, 2, 2, 3, 2},
54       {1, 2, 2, 1, 3, 2},
55       {1, 2, 2, 2, 3, 1},
56       {1, 1, 3, 2, 2, 2}, // 15
57       {1, 2, 3, 1, 2, 2},
58       {1, 2, 3, 2, 2, 1},
59       {2, 2, 3, 2, 1, 1},
60       {2, 2, 1, 1, 3, 2},
61       {2, 2, 1, 2, 3, 1}, // 20
62       {2, 1, 3, 2, 1, 2},
63       {2, 2, 3, 1, 1, 2},
64       {3, 1, 2, 1, 3, 1},
65       {3, 1, 1, 2, 2, 2},
66       {3, 2, 1, 1, 2, 2}, // 25
67       {3, 2, 1, 2, 2, 1},
68       {3, 1, 2, 2, 1, 2},
69       {3, 2, 2, 1, 1, 2},
70       {3, 2, 2, 2, 1, 1},
71       {2, 1, 2, 1, 2, 3}, // 30
72       {2, 1, 2, 3, 2, 1},
73       {2, 3, 2, 1, 2, 1},
74       {1, 1, 1, 3, 2, 3},
75       {1, 3, 1, 1, 2, 3},
76       {1, 3, 1, 3, 2, 1}, // 35
77       {1, 1, 2, 3, 1, 3},
78       {1, 3, 2, 1, 1, 3},
79       {1, 3, 2, 3, 1, 1},
80       {2, 1, 1, 3, 1, 3},
81       {2, 3, 1, 1, 1, 3}, // 40
82       {2, 3, 1, 3, 1, 1},
83       {1, 1, 2, 1, 3, 3},
84       {1, 1, 2, 3, 3, 1},
85       {1, 3, 2, 1, 3, 1},
86       {1, 1, 3, 1, 2, 3}, // 45
87       {1, 1, 3, 3, 2, 1},
88       {1, 3, 3, 1, 2, 1},
89       {3, 1, 3, 1, 2, 1},
90       {2, 1, 1, 3, 3, 1},
91       {2, 3, 1, 1, 3, 1}, // 50
92       {2, 1, 3, 1, 1, 3},
93       {2, 1, 3, 3, 1, 1},
94       {2, 1, 3, 1, 3, 1},
95       {3, 1, 1, 1, 2, 3},
96       {3, 1, 1, 3, 2, 1}, // 55
97       {3, 3, 1, 1, 2, 1},
98       {3, 1, 2, 1, 1, 3},
99       {3, 1, 2, 3, 1, 1},
100       {3, 3, 2, 1, 1, 1},
101       {3, 1, 4, 1, 1, 1}, // 60
102       {2, 2, 1, 4, 1, 1},
103       {4, 3, 1, 1, 1, 1},
104       {1, 1, 1, 2, 2, 4},
105       {1, 1, 1, 4, 2, 2},
106       {1, 2, 1, 1, 2, 4}, // 65
107       {1, 2, 1, 4, 2, 1},
108       {1, 4, 1, 1, 2, 2},
109       {1, 4, 1, 2, 2, 1},
110       {1, 1, 2, 2, 1, 4},
111       {1, 1, 2, 4, 1, 2}, // 70
112       {1, 2, 2, 1, 1, 4},
113       {1, 2, 2, 4, 1, 1},
114       {1, 4, 2, 1, 1, 2},
115       {1, 4, 2, 2, 1, 1},
116       {2, 4, 1, 2, 1, 1}, // 75
117       {2, 2, 1, 1, 1, 4},
118       {4, 1, 3, 1, 1, 1},
119       {2, 4, 1, 1, 1, 2},
120       {1, 3, 4, 1, 1, 1},
121       {1, 1, 1, 2, 4, 2}, // 80
122       {1, 2, 1, 1, 4, 2},
123       {1, 2, 1, 2, 4, 1},
124       {1, 1, 4, 2, 1, 2},
125       {1, 2, 4, 1, 1, 2},
126       {1, 2, 4, 2, 1, 1}, // 85
127       {4, 1, 1, 2, 1, 2},
128       {4, 2, 1, 1, 1, 2},
129       {4, 2, 1, 2, 1, 1},
130       {2, 1, 2, 1, 4, 1},
131       {2, 1, 4, 1, 2, 1}, // 90
132       {4, 1, 2, 1, 2, 1},
133       {1, 1, 1, 1, 4, 3},
134       {1, 1, 1, 3, 4, 1},
135       {1, 3, 1, 1, 4, 1},
136       {1, 1, 4, 1, 1, 3}, // 95
137       {1, 1, 4, 3, 1, 1},
138       {4, 1, 1, 1, 1, 3},
139       {4, 1, 1, 3, 1, 1},
140       {1, 1, 3, 1, 4, 1},
141       {1, 1, 4, 1, 3, 1}, // 100
142       {3, 1, 1, 1, 4, 1},
143       {4, 1, 1, 1, 3, 1},
144       {2, 1, 1, 4, 1, 2},
145       {2, 1, 1, 2, 1, 4},
146       {2, 1, 1, 2, 3, 2}, // 105
147       {2, 3, 3, 1, 1, 1, 2}
148   };
149 
150   private static final float MAX_AVG_VARIANCE = 0.25f;
151   private static final float MAX_INDIVIDUAL_VARIANCE = 0.7f;
152 
153   private static final int CODE_SHIFT = 98;
154 
155   private static final int CODE_CODE_C = 99;
156   private static final int CODE_CODE_B = 100;
157   private static final int CODE_CODE_A = 101;
158 
159   private static final int CODE_FNC_1 = 102;
160   private static final int CODE_FNC_2 = 97;
161   private static final int CODE_FNC_3 = 96;
162   private static final int CODE_FNC_4_A = 101;
163   private static final int CODE_FNC_4_B = 100;
164 
165   private static final int CODE_START_A = 103;
166   private static final int CODE_START_B = 104;
167   private static final int CODE_START_C = 105;
168   private static final int CODE_STOP = 106;
169 
findStartPattern(BitArray row)170   private static int[] findStartPattern(BitArray row) throws NotFoundException {
171     int width = row.getSize();
172     int rowOffset = row.getNextSet(0);
173 
174     int counterPosition = 0;
175     int[] counters = new int[6];
176     int patternStart = rowOffset;
177     boolean isWhite = false;
178     int patternLength = counters.length;
179 
180     for (int i = rowOffset; i < width; i++) {
181       if (row.get(i) != isWhite) {
182         counters[counterPosition]++;
183       } else {
184         if (counterPosition == patternLength - 1) {
185           float bestVariance = MAX_AVG_VARIANCE;
186           int bestMatch = -1;
187           for (int startCode = CODE_START_A; startCode <= CODE_START_C; startCode++) {
188             float variance = patternMatchVariance(counters, CODE_PATTERNS[startCode],
189                 MAX_INDIVIDUAL_VARIANCE);
190             if (variance < bestVariance) {
191               bestVariance = variance;
192               bestMatch = startCode;
193             }
194           }
195           // Look for whitespace before start pattern, >= 50% of width of start pattern
196           if (bestMatch >= 0 &&
197               row.isRange(Math.max(0, patternStart - (i - patternStart) / 2), patternStart, false)) {
198             return new int[]{patternStart, i, bestMatch};
199           }
200           patternStart += counters[0] + counters[1];
201           System.arraycopy(counters, 2, counters, 0, counterPosition - 1);
202           counters[counterPosition - 1] = 0;
203           counters[counterPosition] = 0;
204           counterPosition--;
205         } else {
206           counterPosition++;
207         }
208         counters[counterPosition] = 1;
209         isWhite = !isWhite;
210       }
211     }
212     throw NotFoundException.getNotFoundInstance();
213   }
214 
decodeCode(BitArray row, int[] counters, int rowOffset)215   private static int decodeCode(BitArray row, int[] counters, int rowOffset)
216       throws NotFoundException {
217     recordPattern(row, rowOffset, counters);
218     float bestVariance = MAX_AVG_VARIANCE; // worst variance we'll accept
219     int bestMatch = -1;
220     for (int d = 0; d < CODE_PATTERNS.length; d++) {
221       int[] pattern = CODE_PATTERNS[d];
222       float variance = patternMatchVariance(counters, pattern, MAX_INDIVIDUAL_VARIANCE);
223       if (variance < bestVariance) {
224         bestVariance = variance;
225         bestMatch = d;
226       }
227     }
228     // TODO We're overlooking the fact that the STOP pattern has 7 values, not 6.
229     if (bestMatch >= 0) {
230       return bestMatch;
231     } else {
232       throw NotFoundException.getNotFoundInstance();
233     }
234   }
235 
236   @Override
decodeRow(int rowNumber, BitArray row, Map<DecodeHintType,?> hints)237   public Result decodeRow(int rowNumber, BitArray row, Map<DecodeHintType,?> hints)
238       throws NotFoundException, FormatException, ChecksumException {
239 
240     boolean convertFNC1 = hints != null && hints.containsKey(DecodeHintType.ASSUME_GS1);
241 
242     int symbologyModifier = 0;
243 
244     int[] startPatternInfo = findStartPattern(row);
245     int startCode = startPatternInfo[2];
246 
247     List<Byte> rawCodes = new ArrayList<>(20);
248     rawCodes.add((byte) startCode);
249 
250     int codeSet;
251     switch (startCode) {
252       case CODE_START_A:
253         codeSet = CODE_CODE_A;
254         break;
255       case CODE_START_B:
256         codeSet = CODE_CODE_B;
257         break;
258       case CODE_START_C:
259         codeSet = CODE_CODE_C;
260         break;
261       default:
262         throw FormatException.getFormatInstance();
263     }
264 
265     boolean done = false;
266     boolean isNextShifted = false;
267 
268     StringBuilder result = new StringBuilder(20);
269 
270     int lastStart = startPatternInfo[0];
271     int nextStart = startPatternInfo[1];
272     int[] counters = new int[6];
273 
274     int lastCode = 0;
275     int code = 0;
276     int checksumTotal = startCode;
277     int multiplier = 0;
278     boolean lastCharacterWasPrintable = true;
279     boolean upperMode = false;
280     boolean shiftUpperMode = false;
281 
282     while (!done) {
283 
284       boolean unshift = isNextShifted;
285       isNextShifted = false;
286 
287       // Save off last code
288       lastCode = code;
289 
290       // Decode another code from image
291       code = decodeCode(row, counters, nextStart);
292 
293       rawCodes.add((byte) code);
294 
295       // Remember whether the last code was printable or not (excluding CODE_STOP)
296       if (code != CODE_STOP) {
297         lastCharacterWasPrintable = true;
298       }
299 
300       // Add to checksum computation (if not CODE_STOP of course)
301       if (code != CODE_STOP) {
302         multiplier++;
303         checksumTotal += multiplier * code;
304       }
305 
306       // Advance to where the next code will to start
307       lastStart = nextStart;
308       for (int counter : counters) {
309         nextStart += counter;
310       }
311 
312       // Take care of illegal start codes
313       switch (code) {
314         case CODE_START_A:
315         case CODE_START_B:
316         case CODE_START_C:
317           throw FormatException.getFormatInstance();
318       }
319 
320       switch (codeSet) {
321 
322         case CODE_CODE_A:
323           if (code < 64) {
324             if (shiftUpperMode == upperMode) {
325               result.append((char) (' ' + code));
326             } else {
327               result.append((char) (' ' + code + 128));
328             }
329             shiftUpperMode = false;
330           } else if (code < 96) {
331             if (shiftUpperMode == upperMode) {
332               result.append((char) (code - 64));
333             } else {
334               result.append((char) (code + 64));
335             }
336             shiftUpperMode = false;
337           } else {
338             // Don't let CODE_STOP, which always appears, affect whether whether we think the last
339             // code was printable or not.
340             if (code != CODE_STOP) {
341               lastCharacterWasPrintable = false;
342             }
343             switch (code) {
344               case CODE_FNC_1:
345                 if (result.length() == 0) { // FNC1 at first or second character determines the symbology
346                   symbologyModifier = 1;
347                 } else if (result.length() == 1) {
348                   symbologyModifier = 2;
349                 }
350                 if (convertFNC1) {
351                   if (result.length() == 0) {
352                     // GS1 specification 5.4.3.7. and 5.4.6.4. If the first char after the start code
353                     // is FNC1 then this is GS1-128. We add the symbology identifier.
354                     result.append("]C1");
355                   } else {
356                     // GS1 specification 5.4.7.5. Every subsequent FNC1 is returned as ASCII 29 (GS)
357                     result.append((char) 29);
358                   }
359                 }
360                 break;
361               case CODE_FNC_2:
362                 symbologyModifier = 4;
363                 break;
364               case CODE_FNC_3:
365                 // do nothing?
366                 break;
367               case CODE_FNC_4_A:
368                 if (!upperMode && shiftUpperMode) {
369                   upperMode = true;
370                   shiftUpperMode = false;
371                 } else if (upperMode && shiftUpperMode) {
372                   upperMode = false;
373                   shiftUpperMode = false;
374                 } else {
375                   shiftUpperMode = true;
376                 }
377                 break;
378               case CODE_SHIFT:
379                 isNextShifted = true;
380                 codeSet = CODE_CODE_B;
381                 break;
382               case CODE_CODE_B:
383                 codeSet = CODE_CODE_B;
384                 break;
385               case CODE_CODE_C:
386                 codeSet = CODE_CODE_C;
387                 break;
388               case CODE_STOP:
389                 done = true;
390                 break;
391             }
392           }
393           break;
394         case CODE_CODE_B:
395           if (code < 96) {
396             if (shiftUpperMode == upperMode) {
397               result.append((char) (' ' + code));
398             } else {
399               result.append((char) (' ' + code + 128));
400             }
401             shiftUpperMode = false;
402           } else {
403             if (code != CODE_STOP) {
404               lastCharacterWasPrintable = false;
405             }
406             switch (code) {
407               case CODE_FNC_1:
408                 if (result.length() == 0) { // FNC1 at first or second character determines the symbology
409                   symbologyModifier = 1;
410                 } else if (result.length() == 1) {
411                   symbologyModifier = 2;
412                 }
413                 if (convertFNC1) {
414                   if (result.length() == 0) {
415                     // GS1 specification 5.4.3.7. and 5.4.6.4. If the first char after the start code
416                     // is FNC1 then this is GS1-128. We add the symbology identifier.
417                     result.append("]C1");
418                   } else {
419                     // GS1 specification 5.4.7.5. Every subsequent FNC1 is returned as ASCII 29 (GS)
420                     result.append((char) 29);
421                   }
422                 }
423                 break;
424               case CODE_FNC_2:
425                 symbologyModifier = 4;
426                 break;
427               case CODE_FNC_3:
428                 // do nothing?
429                 break;
430               case CODE_FNC_4_B:
431                 if (!upperMode && shiftUpperMode) {
432                   upperMode = true;
433                   shiftUpperMode = false;
434                 } else if (upperMode && shiftUpperMode) {
435                   upperMode = false;
436                   shiftUpperMode = false;
437                 } else {
438                   shiftUpperMode = true;
439                 }
440                 break;
441               case CODE_SHIFT:
442                 isNextShifted = true;
443                 codeSet = CODE_CODE_A;
444                 break;
445               case CODE_CODE_A:
446                 codeSet = CODE_CODE_A;
447                 break;
448               case CODE_CODE_C:
449                 codeSet = CODE_CODE_C;
450                 break;
451               case CODE_STOP:
452                 done = true;
453                 break;
454             }
455           }
456           break;
457         case CODE_CODE_C:
458           if (code < 100) {
459             if (code < 10) {
460               result.append('0');
461             }
462             result.append(code);
463           } else {
464             if (code != CODE_STOP) {
465               lastCharacterWasPrintable = false;
466             }
467             switch (code) {
468               case CODE_FNC_1:
469                 if (result.length() == 0) { // FNC1 at first or second character determines the symbology
470                   symbologyModifier = 1;
471                 } else if (result.length() == 1) {
472                   symbologyModifier = 2;
473                 }
474                 if (convertFNC1) {
475                   if (result.length() == 0) {
476                     // GS1 specification 5.4.3.7. and 5.4.6.4. If the first char after the start code
477                     // is FNC1 then this is GS1-128. We add the symbology identifier.
478                     result.append("]C1");
479                   } else {
480                     // GS1 specification 5.4.7.5. Every subsequent FNC1 is returned as ASCII 29 (GS)
481                     result.append((char) 29);
482                   }
483                 }
484                 break;
485               case CODE_CODE_A:
486                 codeSet = CODE_CODE_A;
487                 break;
488               case CODE_CODE_B:
489                 codeSet = CODE_CODE_B;
490                 break;
491               case CODE_STOP:
492                 done = true;
493                 break;
494             }
495           }
496           break;
497       }
498 
499       // Unshift back to another code set if we were shifted
500       if (unshift) {
501         codeSet = codeSet == CODE_CODE_A ? CODE_CODE_B : CODE_CODE_A;
502       }
503 
504     }
505 
506     int lastPatternSize = nextStart - lastStart;
507 
508     // Check for ample whitespace following pattern, but, to do this we first need to remember that
509     // we fudged decoding CODE_STOP since it actually has 7 bars, not 6. There is a black bar left
510     // to read off. Would be slightly better to properly read. Here we just skip it:
511     nextStart = row.getNextUnset(nextStart);
512     if (!row.isRange(nextStart,
513                      Math.min(row.getSize(), nextStart + (nextStart - lastStart) / 2),
514                      false)) {
515       throw NotFoundException.getNotFoundInstance();
516     }
517 
518     // Pull out from sum the value of the penultimate check code
519     checksumTotal -= multiplier * lastCode;
520     // lastCode is the checksum then:
521     if (checksumTotal % 103 != lastCode) {
522       throw ChecksumException.getChecksumInstance();
523     }
524 
525     // Need to pull out the check digits from string
526     int resultLength = result.length();
527     if (resultLength == 0) {
528       // false positive
529       throw NotFoundException.getNotFoundInstance();
530     }
531 
532     // Only bother if the result had at least one character, and if the checksum digit happened to
533     // be a printable character. If it was just interpreted as a control code, nothing to remove.
534     if (resultLength > 0 && lastCharacterWasPrintable) {
535       if (codeSet == CODE_CODE_C) {
536         result.delete(resultLength - 2, resultLength);
537       } else {
538         result.delete(resultLength - 1, resultLength);
539       }
540     }
541 
542     float left = (startPatternInfo[1] + startPatternInfo[0]) / 2.0f;
543     float right = lastStart + lastPatternSize / 2.0f;
544 
545     int rawCodesSize = rawCodes.size();
546     byte[] rawBytes = new byte[rawCodesSize];
547     for (int i = 0; i < rawCodesSize; i++) {
548       rawBytes[i] = rawCodes.get(i);
549     }
550     Result resultObject = new Result(
551         result.toString(),
552         rawBytes,
553         new ResultPoint[]{
554             new ResultPoint(left, rowNumber),
555             new ResultPoint(right, rowNumber)},
556         BarcodeFormat.CODE_128);
557     resultObject.putMetadata(ResultMetadataType.SYMBOLOGY_IDENTIFIER, "]C" + symbologyModifier);
558     return resultObject;
559 
560   }
561 
562 }
563