[SCM] Multi-format 1D/2D barcode image processing library branch, upstream, updated. 24d4480bc48cf9eabf7b2bd2f528248b0e458809

srowen srowen at 59b500cc-1b3d-0410-9834-0bbf25fbcc57
Wed Aug 4 01:32:12 UTC 2010


The following commit has been merged in the upstream branch:
commit 9ceee6853d82d86e881fc3d6d7928ddd47df4cd0
Author: srowen <srowen at 59b500cc-1b3d-0410-9834-0bbf25fbcc57>
Date:   Wed Jun 16 19:49:22 2010 +0000

    Add support for UPC/EAN +5 extensions, plus basic tests, and display the content in Android client as metadata
    
    git-svn-id: http://zxing.googlecode.com/svn/trunk@1436 59b500cc-1b3d-0410-9834-0bbf25fbcc57

diff --git a/android/res/layout/capture.xml b/android/res/layout/capture.xml
index fd8ab7e..ec5a343 100755
--- a/android/res/layout/capture.xml
+++ b/android/res/layout/capture.xml
@@ -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
diff --git a/android/res/values-ar/strings.xml b/android/res/values-ar/strings.xml
index 638ae50..4ded52c 100644
--- a/android/res/values-ar/strings.xml
+++ b/android/res/values-ar/strings.xml
@@ -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>
diff --git a/android/res/values-cs/strings.xml b/android/res/values-cs/strings.xml
index b51ec57..61cad4f 100644
--- a/android/res/values-cs/strings.xml
+++ b/android/res/values-cs/strings.xml
@@ -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>
diff --git a/android/res/values-da/strings.xml b/android/res/values-da/strings.xml
index 54e331b..43c43a7 100644
--- a/android/res/values-da/strings.xml
+++ b/android/res/values-da/strings.xml
@@ -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>
diff --git a/android/res/values-de/strings.xml b/android/res/values-de/strings.xml
index 5e8d7fe..8e9fcd2 100644
--- a/android/res/values-de/strings.xml
+++ b/android/res/values-de/strings.xml
@@ -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>
diff --git a/android/res/values-es/strings.xml b/android/res/values-es/strings.xml
index bf59661..169828d 100644
--- a/android/res/values-es/strings.xml
+++ b/android/res/values-es/strings.xml
@@ -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>
diff --git a/android/res/values-fi/strings.xml b/android/res/values-fi/strings.xml
index 9a54420..874d579 100644
--- a/android/res/values-fi/strings.xml
+++ b/android/res/values-fi/strings.xml
@@ -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>
diff --git a/android/res/values-fr/strings.xml b/android/res/values-fr/strings.xml
index dcf012e..1dca67b 100644
--- a/android/res/values-fr/strings.xml
+++ b/android/res/values-fr/strings.xml
@@ -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>
diff --git a/android/res/values-hu/strings.xml b/android/res/values-hu/strings.xml
index 25622cd..e0d0125 100644
--- a/android/res/values-hu/strings.xml
+++ b/android/res/values-hu/strings.xml
@@ -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>
diff --git a/android/res/values-it/strings.xml b/android/res/values-it/strings.xml
index a2a8851..9f618fa 100644
--- a/android/res/values-it/strings.xml
+++ b/android/res/values-it/strings.xml
@@ -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>
diff --git a/android/res/values-ja-rJP/strings.xml b/android/res/values-ja-rJP/strings.xml
index e126b16..105287b 100644
--- a/android/res/values-ja-rJP/strings.xml
+++ b/android/res/values-ja-rJP/strings.xml
@@ -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>
diff --git a/android/res/values-nl/strings.xml b/android/res/values-nl/strings.xml
index 69d6f55..0584ab9 100644
--- a/android/res/values-nl/strings.xml
+++ b/android/res/values-nl/strings.xml
@@ -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>
diff --git a/android/res/values-pl/strings.xml b/android/res/values-pl/strings.xml
index 616e561..90dd961 100644
--- a/android/res/values-pl/strings.xml
+++ b/android/res/values-pl/strings.xml
@@ -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>
diff --git a/android/res/values-pt/strings.xml b/android/res/values-pt/strings.xml
index 8bed877..656b2d6 100644
--- a/android/res/values-pt/strings.xml
+++ b/android/res/values-pt/strings.xml
@@ -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>
diff --git a/android/res/values-ru/strings.xml b/android/res/values-ru/strings.xml
index b2a41ee..31f4e7f 100644
--- a/android/res/values-ru/strings.xml
+++ b/android/res/values-ru/strings.xml
@@ -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>
diff --git a/android/res/values-sv/strings.xml b/android/res/values-sv/strings.xml
index 29e5159..9a3ca1b 100644
--- a/android/res/values-sv/strings.xml
+++ b/android/res/values-sv/strings.xml
@@ -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>
diff --git a/android/res/values-zh-rCN/strings.xml b/android/res/values-zh-rCN/strings.xml
index 1f9c008..1b727b6 100644
--- a/android/res/values-zh-rCN/strings.xml
+++ b/android/res/values-zh-rCN/strings.xml
@@ -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>
diff --git a/android/res/values-zh-rTW/strings.xml b/android/res/values-zh-rTW/strings.xml
index abba6a4..15caf0a 100644
--- a/android/res/values-zh-rTW/strings.xml
+++ b/android/res/values-zh-rTW/strings.xml
@@ -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>
diff --git a/android/res/values/strings.xml b/android/res/values/strings.xml
index 94a3fa8..8b37e2a 100755
--- a/android/res/values/strings.xml
+++ b/android/res/values/strings.xml
@@ -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>
diff --git a/android/src/com/google/zxing/client/android/CaptureActivity.java b/android/src/com/google/zxing/client/android/CaptureActivity.java
index 13323d3..fbdb29d 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<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);
diff --git a/core/src/com/google/zxing/BarcodeFormat.java b/core/src/com/google/zxing/BarcodeFormat.java
index 032a1b6..e10a8a8 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 ca2edd3..9223ceb 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 722b76d..7e47b0e 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");
 
   /**
    * <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");
+
+  /**
+   * 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;
+  }
 
-  private ResultMetadataType() {
+  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 0000000..3076941
--- /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 <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);
+  }
+
+}
diff --git a/core/src/com/google/zxing/oned/UPCEANReader.java b/core/src/com/google/zxing/oned/UPCEANReader.java
index 803eaa6..6036157 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 0000000..2ef57e0
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 0000000..6ad3f36
--- /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 0000000..101471a
--- /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 0000000..b716cce
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 0000000..e14808d
--- /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 0000000..576a811
--- /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 d078fa1..5048343 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<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;
   }
 
diff --git a/core/test/src/com/google/zxing/oned/Code128BlackBox3TestCase.java b/core/test/src/com/google/zxing/oned/UPCEANExtensionBlackBox1TestCase.java
similarity index 77%
copy from core/test/src/com/google/zxing/oned/Code128BlackBox3TestCase.java
copy to core/test/src/com/google/zxing/oned/UPCEANExtensionBlackBox1TestCase.java
index 72f52ff..e1a5acf 100644
--- a/core/test/src/com/google/zxing/oned/Code128BlackBox3TestCase.java
+++ b/core/test/src/com/google/zxing/oned/UPCEANExtensionBlackBox1TestCase.java
@@ -23,12 +23,11 @@ import com.google.zxing.common.AbstractBlackBoxTestCase;
 /**
  * @author Sean Owen
  */
-public final class Code128BlackBox3TestCase extends AbstractBlackBoxTestCase {
+public final class UPCEANExtensionBlackBox1TestCase extends AbstractBlackBoxTestCase {
 
-  public Code128BlackBox3TestCase() {
-    super("test/data/blackbox/code128-3", new MultiFormatReader(), BarcodeFormat.CODE_128);
+  public UPCEANExtensionBlackBox1TestCase() {
+    super("test/data/blackbox/upcean-extension-1", new MultiFormatReader(), BarcodeFormat.EAN_13);
     addTest(2, 2, 0.0f);
-    addTest(2, 2, 180.0f);
   }
 
 }
\ No newline at end of file

-- 
Multi-format 1D/2D barcode image processing library



More information about the Pkg-google-commits mailing list