diff --git a/android/res/layout/capture.xml b/android/res/layout/capture.xml index fd8ab7e4e..ec5a3431e 100755 --- a/android/res/layout/capture.xml +++ b/android/res/layout/capture.xml @@ -126,6 +126,28 @@ + + + + + + + + قم بوضع الرمز الشريطي داخل حدود صورة الكاميرا ليتم مسحه. النوع الوقت + البيانات الوصفية تعذر إنشاء الرمز الشريطي المطلوب. تعذر ترميز رمز شريطي من البيانات المتوفرة. إنشاء رمز شريطي... diff --git a/android/res/values-cs/strings.xml b/android/res/values-cs/strings.xml index b51ec57fd..61cad4fd9 100644 --- a/android/res/values-cs/strings.xml +++ b/android/res/values-cs/strings.xml @@ -65,7 +65,8 @@ Ahoj Strefte se čtverečkem na čárový kód Typ - Čas + Čas + Metadata Nepodařilo se vytvořit požadovaný čárový kód. Nepodařilo se rozkódovat čárový kód z poskytnutých údajů. Generování kódu\u2026 diff --git a/android/res/values-da/strings.xml b/android/res/values-da/strings.xml index 54e331b84..43c43a731 100644 --- a/android/res/values-da/strings.xml +++ b/android/res/values-da/strings.xml @@ -66,6 +66,7 @@ Peg på en stregkode så den er inde i firkanten for at scanne den. Type Tid + Metadata Kunne ikke generere den ønskede stregkode. Kunne ikke indkode en stregkode fra oplysningerne. Genererer en stregkode\u2026 diff --git a/android/res/values-de/strings.xml b/android/res/values-de/strings.xml index 5e8d7fe0f..8e9fcd229 100644 --- a/android/res/values-de/strings.xml +++ b/android/res/values-de/strings.xml @@ -65,7 +65,8 @@ Hi Positionieren Sie den Barcode innerhalb des Rechteckes. Typ - Zeit + Zeit + Metadata Der gewünschte Barcode kann nicht erzeugt werden. Aus den Daten kann kein Barcode erzeugt werden. Erzeuge Barcode\u2026 diff --git a/android/res/values-es/strings.xml b/android/res/values-es/strings.xml index bf596617f..169828d35 100644 --- a/android/res/values-es/strings.xml +++ b/android/res/values-es/strings.xml @@ -65,7 +65,8 @@ Hola Coloque un código de barras en el interior del rectángulo de el visor para escanear. Tipo - Tiempo + Tiempo + Metadata No se pudo generar el código de barras solicitado. No puede codificar un código de barras con estos datos. Generando un código de barras\u2026 diff --git a/android/res/values-fi/strings.xml b/android/res/values-fi/strings.xml index 9a54420a6..874d57903 100644 --- a/android/res/values-fi/strings.xml +++ b/android/res/values-fi/strings.xml @@ -65,7 +65,8 @@ Hei Aseta viivakoodi neliön sisälle. Tyypi - Aika + Aika + Metadata Ei voinut generoida pyydettyä viivakoodia. Ei voinut purkaa viivakoodin sisältöä. Generoidaan viivakoodia\u2026 diff --git a/android/res/values-fr/strings.xml b/android/res/values-fr/strings.xml index dcf012ee0..1dca67b12 100644 --- a/android/res/values-fr/strings.xml +++ b/android/res/values-fr/strings.xml @@ -65,7 +65,8 @@ Salut Mettre un code barre à l\'intérieur du rectangle pour le scanner. Type - Temps + Temps + Métadonnées Impossible de générer le code barre demandé. Impossible de créer le code barre à partir des données fournies. Génération du code barre\u2026 diff --git a/android/res/values-hu/strings.xml b/android/res/values-hu/strings.xml index 25622cda0..e0d0125c1 100644 --- a/android/res/values-hu/strings.xml +++ b/android/res/values-hu/strings.xml @@ -68,7 +68,8 @@ Szia Helyezze a vonalkódot a kereső téglalapba a szkenneléshez. Típus - Idő + Idő + Metaadatok Nem lehetett legenerálni a kért vonalkódot. Nem lehetett lekódolni a vonalkódot a megadott tartalomból. Vonalkód generálása\u2026 diff --git a/android/res/values-it/strings.xml b/android/res/values-it/strings.xml index a2a88518b..9f618fa54 100644 --- a/android/res/values-it/strings.xml +++ b/android/res/values-it/strings.xml @@ -66,6 +66,7 @@ Posiziona un codice a barre dentro il mirino rettangolare per la scansione. Tipo Tempo + Metadati Impossibile generare il codice a barre richiesto. Impossibile codificare un codice a barre dai dati forniti. Generazione codice a barre\u2026 diff --git a/android/res/values-ja-rJP/strings.xml b/android/res/values-ja-rJP/strings.xml index e126b16f5..105287b68 100644 --- a/android/res/values-ja-rJP/strings.xml +++ b/android/res/values-ja-rJP/strings.xml @@ -66,6 +66,7 @@ バーコードをカメラ画面の読み取り範囲内に写してスキャンしてください。 タイプ 時間 + メタデータ バーコードを作成できませんでした。 このデータからバーコードを作成できませんでした。 バーコード作成中\u2026 diff --git a/android/res/values-nl/strings.xml b/android/res/values-nl/strings.xml index 69d6f5532..0584ab991 100644 --- a/android/res/values-nl/strings.xml +++ b/android/res/values-nl/strings.xml @@ -66,6 +66,7 @@ Plaats een barcode binnen de rechthoek om hem te scannen. Type Tijd + Metadata Kan de gevraagde barcode niet genereren. Kan geen barcode van de gegeven data maken. Barcode aan het genereren\u2026 diff --git a/android/res/values-pl/strings.xml b/android/res/values-pl/strings.xml index 616e56198..90dd9617e 100644 --- a/android/res/values-pl/strings.xml +++ b/android/res/values-pl/strings.xml @@ -66,6 +66,7 @@ Umieść kod paskowy w prostokącie wizjera aby zeskanować. Typ Czas + Metadanych Nie można wygenerować żądanego kodu paskowego. Nie można zakodować kodu paskowego z dostarczonych danych. Generowanie kodu paskowego\u2026 diff --git a/android/res/values-pt/strings.xml b/android/res/values-pt/strings.xml index 8bed87732..656b2d6f1 100644 --- a/android/res/values-pt/strings.xml +++ b/android/res/values-pt/strings.xml @@ -66,6 +66,7 @@ Coloque um código de barras dentro do retângulo do visor para verificar isso. Tipo Tempo + Metadados Não foi possível criar código de barras pedido. Não foi possível codificar código de barras dos dados fornecidos. A gerar código de barras\u2026 diff --git a/android/res/values-ru/strings.xml b/android/res/values-ru/strings.xml index b2a41ee32..31f4e7f7a 100644 --- a/android/res/values-ru/strings.xml +++ b/android/res/values-ru/strings.xml @@ -66,6 +66,7 @@ Чтобы сканировать штрих-код, поместите его в прямоугольник видоискателя. Тип Время + Метаданные Не получилось сгенерировать запрошенный штрих-код. Не могу закодировать штрих-код от источник данных. Генерация штрих-кода\u2026 diff --git a/android/res/values-sv/strings.xml b/android/res/values-sv/strings.xml index 29e51591c..9a3ca1b6c 100644 --- a/android/res/values-sv/strings.xml +++ b/android/res/values-sv/strings.xml @@ -66,6 +66,7 @@ Placera steckkoden inom sökarens rektangel för att läsa den. Typ Tid + Metadata Misslyckades med att skapa den önskade streckkoden. Kunde inte skape en streckkod från de givna uppgifterna. Genererar en streckkod\u2026 diff --git a/android/res/values-zh-rCN/strings.xml b/android/res/values-zh-rCN/strings.xml index 1f9c008cb..1b727b647 100644 --- a/android/res/values-zh-rCN/strings.xml +++ b/android/res/values-zh-rCN/strings.xml @@ -66,6 +66,7 @@ 将条码放置于镜头范围内进行扫描。 类型 时间 + 元数据 找不到条码。 不能从已有数据中读取条码。 正在生成条码\u2026 diff --git a/android/res/values-zh-rTW/strings.xml b/android/res/values-zh-rTW/strings.xml index abba6a40c..15caf0aa2 100644 --- a/android/res/values-zh-rTW/strings.xml +++ b/android/res/values-zh-rTW/strings.xml @@ -66,6 +66,7 @@ 將條碼放置於鏡頭範圍內進行掃描。 類型 時間 + 元數據 找不到條碼。 無法讀取條碼。 正在產生條碼\u2026 diff --git a/android/res/values/strings.xml b/android/res/values/strings.xml index 94a3fa8f8..8b37e2a48 100755 --- a/android/res/values/strings.xml +++ b/android/res/values/strings.xml @@ -63,6 +63,7 @@ Contents Format Hi + Metadata Place a barcode inside the viewfinder rectangle to scan it. Type Time diff --git a/android/src/com/google/zxing/client/android/CaptureActivity.java b/android/src/com/google/zxing/client/android/CaptureActivity.java index 13323d367..fbdb29d5c 100755 --- a/android/src/com/google/zxing/client/android/CaptureActivity.java +++ b/android/src/com/google/zxing/client/android/CaptureActivity.java @@ -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 DISPLAYABLE_METADATA_TYPES; + static { + DISPLAYABLE_METADATA_TYPES = new HashSet(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 metadata = + (Map) rawResult.getResultMetadata(); + if (metadata != null) { + StringBuilder metadataText = new StringBuilder(20); + for (Map.Entry 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); diff --git a/core/src/com/google/zxing/BarcodeFormat.java b/core/src/com/google/zxing/BarcodeFormat.java index 032a1b645..e10a8a819 100644 --- a/core/src/com/google/zxing/BarcodeFormat.java +++ b/core/src/com/google/zxing/BarcodeFormat.java @@ -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. */ diff --git a/core/src/com/google/zxing/Result.java b/core/src/com/google/zxing/Result.java index ca2edd3cb..9223ceb72 100644 --- a/core/src/com/google/zxing/Result.java +++ b/core/src/com/google/zxing/Result.java @@ -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; } diff --git a/core/src/com/google/zxing/ResultMetadataType.java b/core/src/com/google/zxing/ResultMetadataType.java index 722b76dd7..7e47b0e74 100644 --- a/core/src/com/google/zxing/ResultMetadataType.java +++ b/core/src/com/google/zxing/ResultMetadataType.java @@ -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"); /** *

2D barcode formats typically encode text, but allow for a sort of 'byte mode' @@ -49,15 +55,49 @@ public final class ResultMetadataType { *

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.

*/ - 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; } } diff --git a/core/src/com/google/zxing/oned/UPCEANExtensionSupport.java b/core/src/com/google/zxing/oned/UPCEANExtensionSupport.java new file mode 100644 index 000000000..3076941fd --- /dev/null +++ b/core/src/com/google/zxing/oned/UPCEANExtensionSupport.java @@ -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 null 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); + } + +} diff --git a/core/src/com/google/zxing/oned/UPCEANReader.java b/core/src/com/google/zxing/oned/UPCEANReader.java index 803eaa6c2..6036157f2 100644 --- a/core/src/com/google/zxing/oned/UPCEANReader.java +++ b/core/src/com/google/zxing/oned/UPCEANReader.java @@ -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; } diff --git a/core/test/data/blackbox/upcean-extension-1/1.gif b/core/test/data/blackbox/upcean-extension-1/1.gif new file mode 100644 index 000000000..2ef57e07f Binary files /dev/null and b/core/test/data/blackbox/upcean-extension-1/1.gif differ diff --git a/core/test/data/blackbox/upcean-extension-1/1.metadata.txt b/core/test/data/blackbox/upcean-extension-1/1.metadata.txt new file mode 100644 index 000000000..6ad3f363c --- /dev/null +++ b/core/test/data/blackbox/upcean-extension-1/1.metadata.txt @@ -0,0 +1 @@ +SUGGESTED_PRICE=$12.99 \ No newline at end of file diff --git a/core/test/data/blackbox/upcean-extension-1/1.txt b/core/test/data/blackbox/upcean-extension-1/1.txt new file mode 100644 index 000000000..101471a1f --- /dev/null +++ b/core/test/data/blackbox/upcean-extension-1/1.txt @@ -0,0 +1 @@ +9780735200449 \ No newline at end of file diff --git a/core/test/data/blackbox/upcean-extension-1/2.jpg b/core/test/data/blackbox/upcean-extension-1/2.jpg new file mode 100755 index 000000000..b716cce27 Binary files /dev/null and b/core/test/data/blackbox/upcean-extension-1/2.jpg differ diff --git a/core/test/data/blackbox/upcean-extension-1/2.metadata.txt b/core/test/data/blackbox/upcean-extension-1/2.metadata.txt new file mode 100644 index 000000000..e14808d31 --- /dev/null +++ b/core/test/data/blackbox/upcean-extension-1/2.metadata.txt @@ -0,0 +1 @@ +SUGGESTED_PRICE=$24.95 \ No newline at end of file diff --git a/core/test/data/blackbox/upcean-extension-1/2.txt b/core/test/data/blackbox/upcean-extension-1/2.txt new file mode 100644 index 000000000..576a81120 --- /dev/null +++ b/core/test/data/blackbox/upcean-extension-1/2.txt @@ -0,0 +1 @@ +9780884271789 \ No newline at end of file diff --git a/core/test/src/com/google/zxing/common/AbstractBlackBoxTestCase.java b/core/test/src/com/google/zxing/common/AbstractBlackBoxTestCase.java index d078fa121..504834396 100644 --- a/core/test/src/com/google/zxing/common/AbstractBlackBoxTestCase.java +++ b/core/test/src/com/google/zxing/common/AbstractBlackBoxTestCase.java @@ -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 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; } diff --git a/core/test/src/com/google/zxing/oned/UPCEANExtensionBlackBox1TestCase.java b/core/test/src/com/google/zxing/oned/UPCEANExtensionBlackBox1TestCase.java new file mode 100644 index 000000000..e1a5acf7e --- /dev/null +++ b/core/test/src/com/google/zxing/oned/UPCEANExtensionBlackBox1TestCase.java @@ -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); + } + +} \ No newline at end of file