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.ResultPoint;
26 import com.google.zxing.common.BitArray;
28 final class UPCEANExtensionSupport {
30 private static final int[] EXTENSION_START_PATTERN = {1,1,2};
31 private static final int[] CHECK_DIGIT_ENCODINGS = {
32 0x18, 0x14, 0x12, 0x11, 0x0C, 0x06, 0x03, 0x0A, 0x09, 0x05
35 private final int[] decodeMiddleCounters = new int[4];
36 private final StringBuffer decodeRowStringBuffer = new StringBuffer();
38 Result decodeRow(int rowNumber, BitArray row, int rowOffset) throws NotFoundException {
40 int[] extensionStartRange = UPCEANReader.findGuardPattern(row, rowOffset, false, EXTENSION_START_PATTERN);
42 StringBuffer result = decodeRowStringBuffer;
44 int end = decodeMiddle(row, extensionStartRange, result);
46 String resultString = result.toString();
47 Hashtable extensionData = parseExtensionString(resultString);
49 Result extensionResult =
50 new Result(resultString,
53 new ResultPoint((extensionStartRange[0] + extensionStartRange[1]) / 2.0f, (float) rowNumber),
54 new ResultPoint((float) end, (float) rowNumber),
56 BarcodeFormat.UPC_EAN_EXTENSION);
57 if (extensionData != null) {
58 extensionResult.putAllMetadata(extensionData);
60 return extensionResult;
63 int decodeMiddle(BitArray row, int[] startRange, StringBuffer resultString) throws NotFoundException {
64 int[] counters = decodeMiddleCounters;
69 int end = row.getSize();
70 int rowOffset = startRange[1];
72 int lgPatternFound = 0;
74 for (int x = 0; x < 5 && rowOffset < end; x++) {
75 int bestMatch = UPCEANReader.decodeDigit(row, counters, rowOffset, UPCEANReader.L_AND_G_PATTERNS);
76 resultString.append((char) ('0' + bestMatch % 10));
77 for (int i = 0; i < counters.length; i++) {
78 rowOffset += counters[i];
80 if (bestMatch >= 10) {
81 lgPatternFound |= 1 << (4 - x);
84 // Read off separator if not last
85 while (rowOffset < end && !row.get(rowOffset)) {
88 while (rowOffset < end && row.get(rowOffset)) {
94 if (resultString.length() != 5) {
95 throw NotFoundException.getNotFoundInstance();
98 int checkDigit = determineCheckDigit(lgPatternFound);
99 if (extensionChecksum(resultString.toString()) != checkDigit) {
100 throw NotFoundException.getNotFoundInstance();
106 private static int extensionChecksum(String s) {
107 int length = s.length();
109 for (int i = length - 2; i >= 0; i -= 2) {
110 sum += (int) s.charAt(i) - (int) '0';
113 for (int i = length - 1; i >= 0; i -= 2) {
114 sum += (int) s.charAt(i) - (int) '0';
120 private static int determineCheckDigit(int lgPatternFound)
121 throws NotFoundException {
122 for (int d = 0; d < 10; d++) {
123 if (lgPatternFound == CHECK_DIGIT_ENCODINGS[d]) {
127 throw NotFoundException.getNotFoundInstance();
131 * @param raw raw content of extension
132 * @return formatted interpretation of raw content as a {@link Hashtable} mapping
133 * one {@link ResultMetadataType} to appropriate value, or <code>null</code> if not known
135 private static Hashtable parseExtensionString(String raw) {
136 ResultMetadataType type;
138 switch (raw.length()) {
140 type = ResultMetadataType.ISSUE_NUMBER;
141 value = parseExtension2String(raw);
144 type = ResultMetadataType.SUGGESTED_PRICE;
145 value = parseExtension5String(raw);
153 Hashtable result = new Hashtable(1);
154 result.put(type, value);
158 private static Integer parseExtension2String(String raw) {
159 return Integer.valueOf(raw);
162 private static String parseExtension5String(String raw) {
164 switch (raw.charAt(0)) {
172 // Reference: http://www.jollytech.com
173 if ("90000".equals(raw)) {
174 // No suggested retail price
176 } else if ("99991".equals(raw)) {
179 } else if ("99990".equals(raw)) {
182 // Otherwise... unknown currency?
189 int rawAmount = Integer.parseInt(raw.substring(1));
190 String unitsString = String.valueOf(rawAmount / 100);
191 int hundredths = rawAmount % 100;
192 String hundredthsString = hundredths < 10 ? "0" + hundredths : String.valueOf(hundredths);
193 return currency + unitsString + '.' + hundredthsString;