"Split" ReaderException into subclasses to enable more useful error reporting
[zxing.git] / core / src / com / google / zxing / oned / EAN13Reader.java
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.NotFoundException;
21 import com.google.zxing.common.BitArray;
22
23 /**
24  * <p>Implements decoding of the EAN-13 format.</p>
25  *
26  * @author dswitkin@google.com (Daniel Switkin)
27  * @author Sean Owen
28  * @author alasdair@google.com (Alasdair Mackintosh)
29  */
30 public final class EAN13Reader extends UPCEANReader {
31
32   // For an EAN-13 barcode, the first digit is represented by the parities used
33   // to encode the next six digits, according to the table below. For example,
34   // if the barcode is 5 123456 789012 then the value of the first digit is
35   // signified by using odd for '1', even for '2', even for '3', odd for '4',
36   // odd for '5', and even for '6'. See http://en.wikipedia.org/wiki/EAN-13
37   //
38   //                Parity of next 6 digits
39   //    Digit   0     1     2     3     4     5
40   //       0    Odd   Odd   Odd   Odd   Odd   Odd
41   //       1    Odd   Odd   Even  Odd   Even  Even
42   //       2    Odd   Odd   Even  Even  Odd   Even
43   //       3    Odd   Odd   Even  Even  Even  Odd
44   //       4    Odd   Even  Odd   Odd   Even  Even
45   //       5    Odd   Even  Even  Odd   Odd   Even
46   //       6    Odd   Even  Even  Even  Odd   Odd
47   //       7    Odd   Even  Odd   Even  Odd   Even
48   //       8    Odd   Even  Odd   Even  Even  Odd
49   //       9    Odd   Even  Even  Odd   Even  Odd
50   //
51   // Note that the encoding for '0' uses the same parity as a UPC barcode. Hence
52   // a UPC barcode can be converted to an EAN-13 barcode by prepending a 0.
53   //
54   // The encoding is represented by the following array, which is a bit pattern
55   // using Odd = 0 and Even = 1. For example, 5 is represented by:
56   //
57   //              Odd Even Even Odd Odd Even
58   // in binary:
59   //                0    1    1   0   0    1   == 0x19
60   //
61   static final int[] FIRST_DIGIT_ENCODINGS = {
62       0x00, 0x0B, 0x0D, 0xE, 0x13, 0x19, 0x1C, 0x15, 0x16, 0x1A
63   };
64
65   private final int[] decodeMiddleCounters;
66
67   public EAN13Reader() {
68     decodeMiddleCounters = new int[4];
69   }
70
71   protected int decodeMiddle(BitArray row, int[] startRange, StringBuffer resultString)
72       throws NotFoundException {
73     int[] counters = decodeMiddleCounters;
74     counters[0] = 0;
75     counters[1] = 0;
76     counters[2] = 0;
77     counters[3] = 0;
78     int end = row.getSize();
79     int rowOffset = startRange[1];
80
81     int lgPatternFound = 0;
82
83     for (int x = 0; x < 6 && rowOffset < end; x++) {
84       int bestMatch = decodeDigit(row, counters, rowOffset, L_AND_G_PATTERNS);
85       resultString.append((char) ('0' + bestMatch % 10));
86       for (int i = 0; i < counters.length; i++) {
87         rowOffset += counters[i];
88       }
89       if (bestMatch >= 10) {
90         lgPatternFound |= 1 << (5 - x);
91       }
92     }
93
94     determineFirstDigit(resultString, lgPatternFound);
95
96     int[] middleRange = findGuardPattern(row, rowOffset, true, MIDDLE_PATTERN);
97     rowOffset = middleRange[1];
98
99     for (int x = 0; x < 6 && rowOffset < end; x++) {
100       int bestMatch = decodeDigit(row, counters, rowOffset, L_PATTERNS);
101       resultString.append((char) ('0' + bestMatch));
102       for (int i = 0; i < counters.length; i++) {
103         rowOffset += counters[i];
104       }
105     }
106
107     return rowOffset;
108   }
109
110   BarcodeFormat getBarcodeFormat() {
111     return BarcodeFormat.EAN_13;
112   }
113
114   /**
115    * Based on pattern of odd-even ('L' and 'G') patterns used to encoded the explicitly-encoded
116    * digits in a barcode, determines the implicitly encoded first digit and adds it to the
117    * result string.
118    *
119    * @param resultString string to insert decoded first digit into
120    * @param lgPatternFound int whose bits indicates the pattern of odd/even L/G patterns used to
121    *  encode digits
122    * @throws NotFoundException if first digit cannot be determined
123    */
124   private static void determineFirstDigit(StringBuffer resultString, int lgPatternFound)
125       throws NotFoundException {
126     for (int d = 0; d < 10; d++) {
127       if (lgPatternFound == FIRST_DIGIT_ENCODINGS[d]) {
128         resultString.insert(0, (char) ('0' + d));
129         return;
130       }
131     }
132     throw NotFoundException.getNotFoundInstance();
133   }
134
135 }