2 * Copyright (C) 2010 ZXing authors
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.google.zxing.oned;
19 import java.util.Hashtable;
21 import com.google.zxing.BarcodeFormat;
22 import com.google.zxing.NotFoundException;
23 import com.google.zxing.Result;
24 import com.google.zxing.ResultMetadataType;
25 import com.google.zxing.common.BitArray;
27 final class UPCEANExtensionSupport {
29 private static final int[] EXTENSION_START_PATTERN = {1,1,2};
30 private static final int[] CHECK_DIGIT_ENCODINGS = {
31 0x18, 0x14, 0x12, 0x11, 0x0C, 0x06, 0x03, 0x0A, 0x09, 0x05
33 private static final int[][] SEPARATOR_PATTERNS = {{1,1}};
35 private final int[] decodeMiddleCounters = new int[4];
36 private final int[] separatorCounters = new int[2];
37 private final StringBuffer decodeRowStringBuffer = new StringBuffer();
39 Result decodeRow(BitArray row, int rowOffset) throws NotFoundException {
41 int[] extensionStartRange = UPCEANReader.findGuardPattern(row, rowOffset, false, EXTENSION_START_PATTERN);
43 StringBuffer result = decodeRowStringBuffer;
45 decodeMiddle(row, extensionStartRange, result);
47 String resultString = result.toString();
48 Hashtable extensionData = parseExtensionString(resultString);
50 Result extensionResult = new Result(resultString, null, null, BarcodeFormat.UPC_EAN_EXTENSION);
51 if (extensionData != null) {
52 extensionResult.putAllMetadata(extensionData);
54 return extensionResult;
57 int decodeMiddle(BitArray row, int[] startRange, StringBuffer resultString) throws NotFoundException {
58 int[] counters = decodeMiddleCounters;
63 int[] separatorCounters = this.separatorCounters;
64 separatorCounters[0] = 0;
65 separatorCounters[1] = 0;
66 int end = row.getSize();
67 int rowOffset = startRange[1];
69 int lgPatternFound = 0;
71 for (int x = 0; x < 5 && rowOffset < end; x++) {
72 int bestMatch = UPCEANReader.decodeDigit(row, counters, rowOffset, UPCEANReader.L_AND_G_PATTERNS);
73 resultString.append((char) ('0' + bestMatch % 10));
74 for (int i = 0; i < counters.length; i++) {
75 rowOffset += counters[i];
77 if (bestMatch >= 10) {
78 lgPatternFound |= 1 << (4 - x);
83 UPCEANReader.decodeDigit(row, separatorCounters, rowOffset, SEPARATOR_PATTERNS);
84 rowOffset += separatorCounters[0] + separatorCounters[1];
85 } catch (NotFoundException nfe) {
89 while (rowOffset < end && !row.get(rowOffset)) {
92 while (rowOffset < end && row.get(rowOffset)) {
97 if (resultString.length() != 5) {
98 throw NotFoundException.getNotFoundInstance();
101 int checkDigit = determineCheckDigit(lgPatternFound);
102 if (extensionChecksum(resultString.toString()) != checkDigit) {
103 throw NotFoundException.getNotFoundInstance();
109 private static int extensionChecksum(String s) {
110 int length = s.length();
112 for (int i = length - 2; i >= 0; i -= 2) {
113 sum += (int) s.charAt(i) - (int) '0';
116 for (int i = length - 1; i >= 0; i -= 2) {
117 sum += (int) s.charAt(i) - (int) '0';
123 private static int determineCheckDigit(int lgPatternFound)
124 throws NotFoundException {
125 for (int d = 0; d < 10; d++) {
126 if (lgPatternFound == CHECK_DIGIT_ENCODINGS[d]) {
130 throw NotFoundException.getNotFoundInstance();
134 * @param raw raw content of extension
135 * @return formatted interpretation of raw content as a {@link Hashtable} mapping
136 * one {@link ResultMetadataType} to appropriate value, or <code>null</code> if not known
138 private static Hashtable parseExtensionString(String raw) {
139 ResultMetadataType type;
141 switch (raw.length()) {
143 type = ResultMetadataType.ISSUE_NUMBER;
144 value = parseExtension2String(raw);
147 type = ResultMetadataType.SUGGESTED_PRICE;
148 value = parseExtension5String(raw);
156 Hashtable result = new Hashtable(1);
157 result.put(type, value);
161 private static Integer parseExtension2String(String raw) {
162 return Integer.valueOf(raw);
165 private static String parseExtension5String(String raw) {
166 String currency = null;
167 switch (raw.charAt(0)) {
175 if ("99991".equals(raw)) {
177 } else if ("99990".equals(raw)) {
185 int rawAmount = Integer.parseInt(raw.substring(1));
186 return currency + (rawAmount / 100) + '.' + (rawAmount % 100);