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
34 private final int[] decodeMiddleCounters = new int[4];
35 private final StringBuffer decodeRowStringBuffer = new StringBuffer();
37 Result decodeRow(BitArray row, int rowOffset) throws NotFoundException {
39 int[] extensionStartRange = UPCEANReader.findGuardPattern(row, rowOffset, false, EXTENSION_START_PATTERN);
41 StringBuffer result = decodeRowStringBuffer;
43 decodeMiddle(row, extensionStartRange, result);
45 String resultString = result.toString();
46 Hashtable extensionData = parseExtensionString(resultString);
48 Result extensionResult = new Result(resultString, null, null, BarcodeFormat.UPC_EAN_EXTENSION);
49 if (extensionData != null) {
50 extensionResult.putAllMetadata(extensionData);
52 return extensionResult;
55 int decodeMiddle(BitArray row, int[] startRange, StringBuffer resultString) throws NotFoundException {
56 int[] counters = decodeMiddleCounters;
61 int end = row.getSize();
62 int rowOffset = startRange[1];
64 int lgPatternFound = 0;
66 for (int x = 0; x < 5 && rowOffset < end; x++) {
67 int bestMatch = UPCEANReader.decodeDigit(row, counters, rowOffset, UPCEANReader.L_AND_G_PATTERNS);
68 resultString.append((char) ('0' + bestMatch % 10));
69 for (int i = 0; i < counters.length; i++) {
70 rowOffset += counters[i];
72 if (bestMatch >= 10) {
73 lgPatternFound |= 1 << (4 - x);
76 while (rowOffset < end && !row.get(rowOffset)) {
79 while (rowOffset < end && row.get(rowOffset)) {
84 if (resultString.length() != 5) {
85 throw NotFoundException.getNotFoundInstance();
88 int checkDigit = determineCheckDigit(lgPatternFound);
89 if (extensionChecksum(resultString.toString()) != checkDigit) {
90 throw NotFoundException.getNotFoundInstance();
96 private static int extensionChecksum(String s) {
97 int length = s.length();
99 for (int i = length - 2; i >= 0; i -= 2) {
100 sum += (int) s.charAt(i) - (int) '0';
103 for (int i = length - 1; i >= 0; i -= 2) {
104 sum += (int) s.charAt(i) - (int) '0';
110 private static int determineCheckDigit(int lgPatternFound)
111 throws NotFoundException {
112 for (int d = 0; d < 10; d++) {
113 if (lgPatternFound == CHECK_DIGIT_ENCODINGS[d]) {
117 throw NotFoundException.getNotFoundInstance();
121 * @param raw raw content of extension
122 * @return formatted interpretation of raw content as a {@link Hashtable} mapping
123 * one {@link ResultMetadataType} to appropriate value, or <code>null</code> if not known
125 private static Hashtable parseExtensionString(String raw) {
126 ResultMetadataType type;
128 switch (raw.length()) {
130 type = ResultMetadataType.ISSUE_NUMBER;
131 value = parseExtension2String(raw);
134 type = ResultMetadataType.SUGGESTED_PRICE;
135 value = parseExtension5String(raw);
143 Hashtable result = new Hashtable(1);
144 result.put(type, value);
148 private static Integer parseExtension2String(String raw) {
149 return Integer.valueOf(raw);
152 private static String parseExtension5String(String raw) {
153 String currency = null;
154 switch (raw.charAt(0)) {
162 if ("99991".equals(raw)) {
164 } else if ("99990".equals(raw)) {
172 int rawAmount = Integer.parseInt(raw.substring(1));
173 return currency + (rawAmount / 100) + '.' + (rawAmount % 100);