Add support for UPC/EAN +5 extensions, plus basic tests, and display the content in Android client as metadata

git-svn-id: https://zxing.googlecode.com/svn/trunk@1436 59b500cc-1b3d-0410-9834-0bbf25fbcc57
This commit is contained in:
srowen 2010-06-16 19:49:22 +00:00
parent 2223a5cf42
commit 8cbe558ec3
33 changed files with 418 additions and 26 deletions

View file

@ -126,6 +126,28 @@
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView android:id="@+id/meta_text_view_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/msg_default_meta"
android:textColor="@color/result_minor_text"
android:textStyle="bold"
android:textSize="14sp"
android:paddingRight="4dip"/>
<TextView android:id="@+id/meta_text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/result_minor_text"
android:textSize="14sp"/>
</LinearLayout>
</LinearLayout>
<ScrollView

View file

@ -66,6 +66,7 @@
<string name="msg_default_status">قم بوضع الرمز الشريطي داخل حدود صورة الكاميرا ليتم مسحه.</string>
<string name="msg_default_type">النوع</string>
<string name="msg_default_time">الوقت</string>
<string name="msg_default_meta">البيانات الوصفية</string>
<string name="msg_encode_barcode_failed">تعذر إنشاء الرمز الشريطي المطلوب.</string>
<string name="msg_encode_contents_failed">تعذر ترميز رمز شريطي من البيانات المتوفرة.</string>
<string name="msg_encode_in_progress">إنشاء رمز شريطي...</string>

View file

@ -65,7 +65,8 @@
<string name="msg_default_mms_subject">Ahoj</string>
<string name="msg_default_status">Strefte se čtverečkem na čárový kód</string>
<string name="msg_default_type">Typ</string>
<string name="msg_default_time">Čas</string>
<string name="msg_default_time">Čas</string>
<string name="msg_default_meta">Metadata</string>
<string name="msg_encode_barcode_failed">Nepodařilo se vytvořit požadovaný čárový kód.</string>
<string name="msg_encode_contents_failed">Nepodařilo se rozkódovat čárový kód z poskytnutých údajů.</string>
<string name="msg_encode_in_progress">Generování kódu\u2026</string>

View file

@ -66,6 +66,7 @@
<string name="msg_default_status">Peg på en stregkode så den er inde i firkanten for at scanne den.</string>
<string name="msg_default_type">Type</string>
<string name="msg_default_time">Tid</string>
<string name="msg_default_meta">Metadata</string>
<string name="msg_encode_barcode_failed">Kunne ikke generere den ønskede stregkode.</string>
<string name="msg_encode_contents_failed">Kunne ikke indkode en stregkode fra oplysningerne.</string>
<string name="msg_encode_in_progress">Genererer en stregkode\u2026</string>

View file

@ -65,7 +65,8 @@
<string name="msg_default_mms_subject">Hi</string>
<string name="msg_default_status">Positionieren Sie den Barcode innerhalb des Rechteckes.</string>
<string name="msg_default_type">Typ</string>
<string name="msg_default_time">Zeit</string>
<string name="msg_default_time">Zeit</string>
<string name="msg_default_meta">Metadata</string>
<string name="msg_encode_barcode_failed">Der gewünschte Barcode kann nicht erzeugt werden.</string>
<string name="msg_encode_contents_failed">Aus den Daten kann kein Barcode erzeugt werden.</string>
<string name="msg_encode_in_progress">Erzeuge Barcode\u2026</string>

View file

@ -65,7 +65,8 @@
<string name="msg_default_mms_subject">Hola</string>
<string name="msg_default_status">Coloque un código de barras en el interior del rectángulo de el visor para escanear.</string>
<string name="msg_default_type">Tipo</string>
<string name="msg_default_time">Tiempo</string>
<string name="msg_default_time">Tiempo</string>
<string name="msg_default_meta">Metadata</string>
<string name="msg_encode_barcode_failed">No se pudo generar el código de barras solicitado.</string>
<string name="msg_encode_contents_failed">No puede codificar un código de barras con estos datos.</string>
<string name="msg_encode_in_progress">Generando un código de barras\u2026</string>

View file

@ -65,7 +65,8 @@
<string name="msg_default_mms_subject">Hei</string>
<string name="msg_default_status">Aseta viivakoodi neliön sisälle.</string>
<string name="msg_default_type">Tyypi</string>
<string name="msg_default_time">Aika</string>
<string name="msg_default_time">Aika</string>
<string name="msg_default_meta">Metadata</string>
<string name="msg_encode_barcode_failed">Ei voinut generoida pyydettyä viivakoodia.</string>
<string name="msg_encode_contents_failed">Ei voinut purkaa viivakoodin sisältöä.</string>
<string name="msg_encode_in_progress">Generoidaan viivakoodia\u2026</string>

View file

@ -65,7 +65,8 @@
<string name="msg_default_mms_subject">Salut</string>
<string name="msg_default_status">Mettre un code barre à l\'intérieur du rectangle pour le scanner.</string>
<string name="msg_default_type">Type</string>
<string name="msg_default_time">Temps</string>
<string name="msg_default_time">Temps</string>
<string name="msg_default_meta">Métadonnées</string>
<string name="msg_encode_barcode_failed">Impossible de générer le code barre demandé.</string>
<string name="msg_encode_contents_failed">Impossible de créer le code barre à partir des données fournies.</string>
<string name="msg_encode_in_progress">Génération du code barre\u2026</string>

View file

@ -68,7 +68,8 @@
<string name="msg_default_mms_subject">Szia</string>
<string name="msg_default_status">Helyezze a vonalkódot a kereső téglalapba a szkenneléshez.</string>
<string name="msg_default_type">Típus</string>
<string name="msg_default_time">Idő</string>
<string name="msg_default_time">Idő</string>
<string name="msg_default_meta">Metaadatok</string>
<string name="msg_encode_barcode_failed">Nem lehetett legenerálni a kért vonalkódot.</string>
<string name="msg_encode_contents_failed">Nem lehetett lekódolni a vonalkódot a megadott tartalomból.</string>
<string name="msg_encode_in_progress">Vonalkód generálása\u2026</string>

View file

@ -66,6 +66,7 @@
<string name="msg_default_status">Posiziona un codice a barre dentro il mirino rettangolare per la scansione.</string>
<string name="msg_default_type">Tipo</string>
<string name="msg_default_time">Tempo</string>
<string name="msg_default_meta">Metadati</string>
<string name="msg_encode_barcode_failed">Impossibile generare il codice a barre richiesto.</string>
<string name="msg_encode_contents_failed">Impossibile codificare un codice a barre dai dati forniti.</string>
<string name="msg_encode_in_progress">Generazione codice a barre\u2026</string>

View file

@ -66,6 +66,7 @@
<string name="msg_default_status">バーコードをカメラ画面の読み取り範囲内に写してスキャンしてください。</string>
<string name="msg_default_type">タイプ</string>
<string name="msg_default_time">時間</string>
<string name="msg_default_meta">メタデータ</string>
<string name="msg_encode_barcode_failed">バーコードを作成できませんでした。</string>
<string name="msg_encode_contents_failed">このデータからバーコードを作成できませんでした。</string>
<string name="msg_encode_in_progress">バーコード作成中\u2026</string>

View file

@ -66,6 +66,7 @@
<string name="msg_default_status">Plaats een barcode binnen de rechthoek om hem te scannen.</string>
<string name="msg_default_type">Type</string>
<string name="msg_default_time">Tijd</string>
<string name="msg_default_meta">Metadata</string>
<string name="msg_encode_barcode_failed">Kan de gevraagde barcode niet genereren.</string>
<string name="msg_encode_contents_failed">Kan geen barcode van de gegeven data maken.</string>
<string name="msg_encode_in_progress">Barcode aan het genereren\u2026</string>

View file

@ -66,6 +66,7 @@
<string name="msg_default_status">Umieść kod paskowy w prostokącie wizjera aby zeskanować.</string>
<string name="msg_default_type">Typ</string>
<string name="msg_default_time">Czas</string>
<string name="msg_default_meta">Metadanych</string>
<string name="msg_encode_barcode_failed">Nie można wygenerować żądanego kodu paskowego.</string>
<string name="msg_encode_contents_failed">Nie można zakodować kodu paskowego z dostarczonych danych.</string>
<string name="msg_encode_in_progress">Generowanie kodu paskowego\u2026</string>

View file

@ -66,6 +66,7 @@
<string name="msg_default_status">Coloque um código de barras dentro do retângulo do visor para verificar isso.</string>
<string name="msg_default_type">Tipo</string>
<string name="msg_default_time">Tempo</string>
<string name="msg_default_meta">Metadados</string>
<string name="msg_encode_barcode_failed">Não foi possível criar código de barras pedido.</string>
<string name="msg_encode_contents_failed">Não foi possível codificar código de barras dos dados fornecidos.</string>
<string name="msg_encode_in_progress">A gerar código de barras\u2026</string>

View file

@ -66,6 +66,7 @@
<string name="msg_default_status">Чтобы сканировать штрих-код, поместите его в прямоугольник видоискателя.</string>
<string name="msg_default_type">Тип</string>
<string name="msg_default_time">Время</string>
<string name="msg_default_meta">Метаданные</string>
<string name="msg_encode_barcode_failed">Не получилось сгенерировать запрошенный штрих-код.</string>
<string name="msg_encode_contents_failed">Не могу закодировать штрих-код от источник данных.</string>
<string name="msg_encode_in_progress">Генерация штрих-кода\u2026</string>

View file

@ -66,6 +66,7 @@
<string name="msg_default_status">Placera steckkoden inom sökarens rektangel för att läsa den.</string>
<string name="msg_default_type">Typ</string>
<string name="msg_default_time">Tid</string>
<string name="msg_default_meta">Metadata</string>
<string name="msg_encode_barcode_failed">Misslyckades med att skapa den önskade streckkoden.</string>
<string name="msg_encode_contents_failed">Kunde inte skape en streckkod från de givna uppgifterna.</string>
<string name="msg_encode_in_progress">Genererar en streckkod\u2026</string>

View file

@ -66,6 +66,7 @@
<string name="msg_default_status">将条码放置于镜头范围内进行扫描。 </string>
<string name="msg_default_type">类型</string>
<string name="msg_default_time">时间</string>
<string name="msg_default_meta">元数据</string>
<string name="msg_encode_barcode_failed">找不到条码。</string>
<string name="msg_encode_contents_failed">不能从已有数据中读取条码。 </string>
<string name="msg_encode_in_progress">正在生成条码\u2026</string>

View file

@ -66,6 +66,7 @@
<string name="msg_default_status">將條碼放置於鏡頭範圍內進行掃描。 </string>
<string name="msg_default_type">類型</string>
<string name="msg_default_time">時間</string>
<string name="msg_default_meta">元數據</string>
<string name="msg_encode_barcode_failed">找不到條碼。</string>
<string name="msg_encode_contents_failed">無法讀取條碼。 </string>
<string name="msg_encode_in_progress">正在產生條碼\u2026</string>

View file

@ -63,6 +63,7 @@
<string name="msg_default_contents">Contents</string>
<string name="msg_default_format">Format</string>
<string name="msg_default_mms_subject">Hi</string>
<string name="msg_default_meta">Metadata</string>
<string name="msg_default_status">Place a barcode inside the viewfinder rectangle to scan it.</string>
<string name="msg_default_type">Type</string>
<string name="msg_default_time">Time</string>

View file

@ -19,6 +19,7 @@ package com.google.zxing.client.android;
import android.util.TypedValue;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.Result;
import com.google.zxing.ResultMetadataType;
import com.google.zxing.ResultPoint;
import com.google.zxing.client.android.camera.CameraManager;
import com.google.zxing.client.android.history.HistoryManager;
@ -66,8 +67,12 @@ import android.widget.TextView;
import java.io.IOException;
import java.text.DateFormat;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.List;
import java.util.Date;
import java.util.Map;
import java.util.Set;
import java.util.Vector;
import java.util.regex.Pattern;
@ -125,6 +130,14 @@ public final class CaptureActivity extends Activity implements SurfaceHolder.Cal
ALL_FORMATS.addAll(QR_CODE_FORMATS);
}
private static final Set<ResultMetadataType> DISPLAYABLE_METADATA_TYPES;
static {
DISPLAYABLE_METADATA_TYPES = new HashSet<ResultMetadataType>(3);
DISPLAYABLE_METADATA_TYPES.add(ResultMetadataType.ISSUE_NUMBER);
DISPLAYABLE_METADATA_TYPES.add(ResultMetadataType.SUGGESTED_PRICE);
DISPLAYABLE_METADATA_TYPES.add(ResultMetadataType.ERROR_CORRECTION_LEVEL);
}
private enum Source {
NATIVE_APP_INTENT,
PRODUCT_SEARCH_LINK,
@ -504,20 +517,39 @@ public final class CaptureActivity extends Activity implements SurfaceHolder.Cal
barcodeImageView.setVisibility(View.VISIBLE);
TextView formatTextView = (TextView) findViewById(R.id.format_text_view);
formatTextView.setVisibility(View.VISIBLE);
formatTextView.setText(rawResult.getBarcodeFormat().toString());
ResultHandler resultHandler = ResultHandlerFactory.makeResultHandler(this, rawResult);
TextView typeTextView = (TextView) findViewById(R.id.type_text_view);
typeTextView.setVisibility(View.VISIBLE);
typeTextView.setText(resultHandler.getType().toString());
DateFormat formatter = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT);
String formattedTime = formatter.format(new Date(rawResult.getTimestamp()));
TextView timeTextView = (TextView) findViewById(R.id.time_text_view);
timeTextView.setVisibility(View.VISIBLE);
timeTextView.setText(formattedTime);
TextView metaTextView = (TextView) findViewById(R.id.meta_text_view);
View metaTextViewLabel = findViewById(R.id.meta_text_view_label);
metaTextView.setVisibility(View.GONE);
metaTextViewLabel.setVisibility(View.GONE);
Map<ResultMetadataType,Object> metadata =
(Map<ResultMetadataType,Object>) rawResult.getResultMetadata();
if (metadata != null) {
StringBuilder metadataText = new StringBuilder(20);
for (Map.Entry<ResultMetadataType,Object> entry : metadata.entrySet()) {
if (DISPLAYABLE_METADATA_TYPES.contains(entry.getKey())) {
metadataText.append(entry.getValue()).append('\n');
}
}
if (metadataText.length() > 0) {
metadataText.setLength(metadataText.length() - 1);
metaTextView.setText(metadataText);
metaTextView.setVisibility(View.VISIBLE);
metaTextViewLabel.setVisibility(View.VISIBLE);
}
}
TextView contentsTextView = (TextView) findViewById(R.id.contents_text_view);
CharSequence displayContents = resultHandler.getDisplayContents();
contentsTextView.setText(displayContents);

View file

@ -47,6 +47,9 @@ public final class BarcodeFormat {
/** EAN-13 1D format. */
public static final BarcodeFormat EAN_13 = new BarcodeFormat("EAN_13");
/** UPC/EAN extension format. Not a stand-alone format. */
public static final BarcodeFormat UPC_EAN_EXTENSION = new BarcodeFormat("UPC_EAN_EXTENSION");
/** Code 128 1D format. */
public static final BarcodeFormat CODE_128 = new BarcodeFormat("CODE_128");
@ -54,9 +57,9 @@ public final class BarcodeFormat {
public static final BarcodeFormat CODE_39 = new BarcodeFormat("CODE_39");
/** Code 93 1D format. */
public static final BarcodeFormat CODE_93 = new BarcodeFormat("CODE_93");
/** CODABAR 1D format. */
public static final BarcodeFormat CODE_93 = new BarcodeFormat("CODE_93");
/** CODABAR 1D format. */
public static final BarcodeFormat CODABAR = new BarcodeFormat("CODABAR");
/** ITF (Interleaved Two of Five) 1D format. */

View file

@ -16,6 +16,7 @@
package com.google.zxing;
import java.util.Enumeration;
import java.util.Hashtable;
/**
@ -101,6 +102,21 @@ public final class Result {
resultMetadata.put(type, value);
}
public void putAllMetadata(Hashtable metadata) {
if (metadata != null) {
if (resultMetadata == null) {
resultMetadata = metadata;
} else {
Enumeration e = metadata.keys();
while (e.hasMoreElements()) {
ResultMetadataType key = (ResultMetadataType) e.nextElement();
Object value = metadata.get(key);
resultMetadata.put(key, value);
}
}
}
}
public long getTimestamp() {
return timestamp;
}

View file

@ -16,6 +16,8 @@
package com.google.zxing;
import java.util.Hashtable;
/**
* Represents some type of metadata about the result of the decoding that the decoder
* wishes to communicate back to the caller.
@ -26,10 +28,14 @@ public final class ResultMetadataType {
// No, we can't use an enum here. J2ME doesn't support it.
private static final Hashtable VALUES = new Hashtable();
// No, we can't use an enum here. J2ME doesn't support it.
/**
* Unspecified, application-specific metadata. Maps to an unspecified {@link Object}.
*/
public static final ResultMetadataType OTHER = new ResultMetadataType();
public static final ResultMetadataType OTHER = new ResultMetadataType("OTHER");
/**
* Denotes the likely approximate orientation of the barcode in the image. This value
@ -38,7 +44,7 @@ public final class ResultMetadataType {
* said to have orientation "90". This key maps to an {@link Integer} whose
* value is in the range [0,360).
*/
public static final ResultMetadataType ORIENTATION = new ResultMetadataType();
public static final ResultMetadataType ORIENTATION = new ResultMetadataType("ORIENTATION");
/**
* <p>2D barcode formats typically encode text, but allow for a sort of 'byte mode'
@ -49,15 +55,49 @@ public final class ResultMetadataType {
* <p>This maps to a {@link java.util.Vector} of byte arrays corresponding to the
* raw bytes in the byte segments in the barcode, in order.</p>
*/
public static final ResultMetadataType BYTE_SEGMENTS = new ResultMetadataType();
public static final ResultMetadataType BYTE_SEGMENTS = new ResultMetadataType("BYTE_SEGMENTS");
/**
* Error correction level used, if applicable. The value type depends on the
* format, but is typically a String.
*/
public static final ResultMetadataType ERROR_CORRECTION_LEVEL = new ResultMetadataType();
public static final ResultMetadataType ERROR_CORRECTION_LEVEL = new ResultMetadataType("ERROR_CORRECTION_LEVEL");
private ResultMetadataType() {
/**
* For some periodicals, indicates the issue number as an {@link Integer}.
*/
public static final ResultMetadataType ISSUE_NUMBER = new ResultMetadataType("ISSUE_NUMBER");
/**
* For some products, indicates the suggested retail price in the barcode as a
* formatted {@link String}.
*/
public static final ResultMetadataType SUGGESTED_PRICE = new ResultMetadataType("SUGGESTED_PRICE");
private final String name;
private ResultMetadataType(String name) {
this.name = name;
VALUES.put(name, this);
}
public String getName() {
return name;
}
public String toString() {
return name;
}
public static ResultMetadataType valueOf(String name) {
if (name == null || name.length() == 0) {
throw new IllegalArgumentException();
}
ResultMetadataType format = (ResultMetadataType) VALUES.get(name);
if (format == null) {
throw new IllegalArgumentException();
}
return format;
}
}

View file

@ -0,0 +1,189 @@
/*
* Copyright (C) 2010 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.zxing.oned;
import java.util.Hashtable;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.NotFoundException;
import com.google.zxing.Result;
import com.google.zxing.ResultMetadataType;
import com.google.zxing.common.BitArray;
final class UPCEANExtensionSupport {
private static final int[] EXTENSION_START_PATTERN = {1,1,2};
private static final int[] CHECK_DIGIT_ENCODINGS = {
0x18, 0x14, 0x12, 0x11, 0x0C, 0x06, 0x03, 0x0A, 0x09, 0x05
};
private static final int[][] SEPARATOR_PATTERNS = {{1,1}};
private final int[] decodeMiddleCounters = new int[4];
private final int[] separatorCounters = new int[2];
private final StringBuffer decodeRowStringBuffer = new StringBuffer();
Result decodeRow(BitArray row, int rowOffset) throws NotFoundException {
int[] extensionStartRange = UPCEANReader.findGuardPattern(row, rowOffset, false, EXTENSION_START_PATTERN);
StringBuffer result = decodeRowStringBuffer;
result.setLength(0);
decodeMiddle(row, extensionStartRange, result);
String resultString = result.toString();
Hashtable extensionData = parseExtensionString(resultString);
Result extensionResult = new Result(resultString, null, null, BarcodeFormat.UPC_EAN_EXTENSION);
if (extensionData != null) {
extensionResult.putAllMetadata(extensionData);
}
return extensionResult;
}
int decodeMiddle(BitArray row, int[] startRange, StringBuffer resultString) throws NotFoundException {
int[] counters = decodeMiddleCounters;
counters[0] = 0;
counters[1] = 0;
counters[2] = 0;
counters[3] = 0;
int[] separatorCounters = this.separatorCounters;
separatorCounters[0] = 0;
separatorCounters[1] = 0;
int end = row.getSize();
int rowOffset = startRange[1];
int lgPatternFound = 0;
for (int x = 0; x < 5 && rowOffset < end; x++) {
int bestMatch = UPCEANReader.decodeDigit(row, counters, rowOffset, UPCEANReader.L_AND_G_PATTERNS);
resultString.append((char) ('0' + bestMatch % 10));
for (int i = 0; i < counters.length; i++) {
rowOffset += counters[i];
}
if (bestMatch >= 10) {
lgPatternFound |= 1 << (4 - x);
}
// Read off separator
/*
try {
UPCEANReader.decodeDigit(row, separatorCounters, rowOffset, SEPARATOR_PATTERNS);
rowOffset += separatorCounters[0] + separatorCounters[1];
} catch (NotFoundException nfe) {
break;
}
*/
while (rowOffset < end && !row.get(rowOffset)) {
rowOffset++;
}
while (rowOffset < end && row.get(rowOffset)) {
rowOffset++;
}
}
if (resultString.length() != 5) {
throw NotFoundException.getNotFoundInstance();
}
int checkDigit = determineCheckDigit(lgPatternFound);
if (extensionChecksum(resultString.toString()) != checkDigit) {
throw NotFoundException.getNotFoundInstance();
}
return rowOffset;
}
private static int extensionChecksum(String s) {
int length = s.length();
int sum = 0;
for (int i = length - 2; i >= 0; i -= 2) {
sum += (int) s.charAt(i) - (int) '0';
}
sum *= 3;
for (int i = length - 1; i >= 0; i -= 2) {
sum += (int) s.charAt(i) - (int) '0';
}
sum *= 3;
return sum % 10;
}
private static int determineCheckDigit(int lgPatternFound)
throws NotFoundException {
for (int d = 0; d < 10; d++) {
if (lgPatternFound == CHECK_DIGIT_ENCODINGS[d]) {
return d;
}
}
throw NotFoundException.getNotFoundInstance();
}
/**
* @param raw raw content of extension
* @return formatted interpretation of raw content as a {@link Hashtable} mapping
* one {@link ResultMetadataType} to appropriate value, or <code>null</code> if not known
*/
private static Hashtable parseExtensionString(String raw) {
ResultMetadataType type;
Object value;
switch (raw.length()) {
case 2:
type = ResultMetadataType.ISSUE_NUMBER;
value = parseExtension2String(raw);
break;
case 5:
type = ResultMetadataType.SUGGESTED_PRICE;
value = parseExtension5String(raw);
break;
default:
return null;
}
if (value == null) {
return null;
}
Hashtable result = new Hashtable(1);
result.put(type, value);
return result;
}
private static Integer parseExtension2String(String raw) {
return Integer.valueOf(raw);
}
private static String parseExtension5String(String raw) {
String currency = null;
switch (raw.charAt(0)) {
case '0':
currency = "£";
break;
case '5':
currency = "$";
break;
case '9':
if ("99991".equals(raw)) {
return "0.00";
} else if ("99990".equals(raw)) {
return "Used";
}
break;
default:
currency = "";
break;
}
int rawAmount = Integer.parseInt(raw.substring(1));
return currency + (rawAmount / 100) + '.' + (rawAmount % 100);
}
}

View file

@ -21,6 +21,7 @@ import com.google.zxing.ChecksumException;
import com.google.zxing.DecodeHintType;
import com.google.zxing.FormatException;
import com.google.zxing.NotFoundException;
import com.google.zxing.ReaderException;
import com.google.zxing.Result;
import com.google.zxing.ResultPoint;
import com.google.zxing.ResultPointCallback;
@ -91,9 +92,11 @@ public abstract class UPCEANReader extends OneDReader {
}
private final StringBuffer decodeRowStringBuffer;
private final UPCEANExtensionSupport extensionReader;
protected UPCEANReader() {
decodeRowStringBuffer = new StringBuffer(20);
extensionReader = new UPCEANExtensionSupport();
}
static int[] findStartGuardPattern(BitArray row) throws NotFoundException {
@ -171,12 +174,20 @@ public abstract class UPCEANReader extends OneDReader {
float left = (float) (startGuardRange[1] + startGuardRange[0]) / 2.0f;
float right = (float) (endRange[1] + endRange[0]) / 2.0f;
return new Result(resultString,
Result decodeResult = new Result(resultString,
null, // no natural byte representation for these barcodes
new ResultPoint[]{
new ResultPoint(left, (float) rowNumber),
new ResultPoint(right, (float) rowNumber)},
getBarcodeFormat());
try {
Result extensionResult = extensionReader.decodeRow(row, endRange[1]);
decodeResult.putAllMetadata(extensionResult.getResultMetadata());
} catch (ReaderException re) {
// continue
}
return decodeResult;
}
/**
@ -193,9 +204,8 @@ public abstract class UPCEANReader extends OneDReader {
* @param s string of digits to check
* @return true iff string of digits passes the UPC/EAN checksum algorithm
* @throws FormatException if the string does not contain only digits
* @throws ChecksumException if checksum mismatches
*/
private static boolean checkStandardUPCEANChecksum(String s) throws ChecksumException, FormatException {
private static boolean checkStandardUPCEANChecksum(String s) throws FormatException {
int length = s.length();
if (length == 0) {
return false;
@ -213,7 +223,7 @@ public abstract class UPCEANReader extends OneDReader {
for (int i = length - 1; i >= 0; i -= 2) {
int digit = (int) s.charAt(i) - (int) '0';
if (digit < 0 || digit > 9) {
throw ChecksumException.getChecksumInstance();
throw FormatException.getFormatInstance();
}
sum += digit;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 KiB

View file

@ -0,0 +1 @@
SUGGESTED_PRICE=$12.99

View file

@ -0,0 +1 @@
9780735200449

Binary file not shown.

After

Width:  |  Height:  |  Size: 252 KiB

View file

@ -0,0 +1 @@
SUGGESTED_PRICE=$24.95

View file

@ -0,0 +1 @@
9780884271789

View file

@ -23,6 +23,7 @@ import com.google.zxing.LuminanceSource;
import com.google.zxing.Reader;
import com.google.zxing.ReaderException;
import com.google.zxing.Result;
import com.google.zxing.ResultMetadataType;
import com.google.zxing.client.j2se.BufferedImageLuminanceSource;
import junit.framework.TestCase;
@ -40,6 +41,8 @@ import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.Properties;
/**
* @author Sean Owen
@ -174,19 +177,25 @@ public abstract class AbstractBlackBoxTestCase extends TestCase {
BufferedImage image = ImageIO.read(testImage);
String testImageFileName = testImage.getName();
File expectedTextFile = new File(testBase,
testImageFileName.substring(0, testImageFileName.indexOf('.')) + ".txt");
String fileBaseName = testImageFileName.substring(0, testImageFileName.indexOf('.'));
File expectedTextFile = new File(testBase, fileBaseName + ".txt");
String expectedText = readFileAsString(expectedTextFile);
File expectedMetadataFile = new File(testBase, fileBaseName + ".metadata.txt");
Properties expectedMetadata = new Properties();
if (expectedMetadataFile.exists()) {
expectedMetadata.load(new FileInputStream(expectedMetadataFile));
}
for (int x = 0; x < testCount; x++) {
float rotation = testResults.get(x).getRotation();
BufferedImage rotatedImage = rotateImage(image, rotation);
LuminanceSource source = new BufferedImageLuminanceSource(rotatedImage);
BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
if (decode(bitmap, rotation, expectedText, false)) {
if (decode(bitmap, rotation, expectedText, expectedMetadata, false)) {
passedCounts[x]++;
}
if (decode(bitmap, rotation, expectedText, true)) {
if (decode(bitmap, rotation, expectedText, expectedMetadata, true)) {
tryHarderCounts[x]++;
}
}
@ -231,7 +240,10 @@ public abstract class AbstractBlackBoxTestCase extends TestCase {
return new SummaryResults(totalFound, totalMustPass, totalTests);
}
private boolean decode(BinaryBitmap source, float rotation, String expectedText,
private boolean decode(BinaryBitmap source,
float rotation,
String expectedText,
Properties expectedMetadata,
boolean tryHarder) {
Result result;
String suffix = " (" + (tryHarder ? "try harder, " : "") + "rotation: " + rotation + ')';
@ -263,6 +275,19 @@ public abstract class AbstractBlackBoxTestCase extends TestCase {
'\'' + suffix);
return false;
}
Hashtable resultMetadata = result.getResultMetadata();
for (Map.Entry<Object,Object> metadatum : expectedMetadata.entrySet()) {
ResultMetadataType key = ResultMetadataType.valueOf(metadatum.getKey().toString());
Object expectedValue = metadatum.getValue();
Object actualValue = resultMetadata == null ? null : resultMetadata.get(key);
if (!expectedValue.equals(actualValue)) {
System.out.println("Metadata mismatch: for key '" + key + "' expected '" + expectedValue +
"' but got '" + actualValue + '\'');
return false;
}
}
return true;
}

View file

@ -0,0 +1,33 @@
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.zxing.oned;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.MultiFormatReader;
import com.google.zxing.common.AbstractBlackBoxTestCase;
/**
* @author Sean Owen
*/
public final class UPCEANExtensionBlackBox1TestCase extends AbstractBlackBoxTestCase {
public UPCEANExtensionBlackBox1TestCase() {
super("test/data/blackbox/upcean-extension-1", new MultiFormatReader(), BarcodeFormat.EAN_13);
addTest(2, 2, 0.0f);
}
}