Display possible country origin info on product scan
authorsrowen <srowen@59b500cc-1b3d-0410-9834-0bbf25fbcc57>
Wed, 16 Jun 2010 21:13:47 +0000 (21:13 +0000)
committersrowen <srowen@59b500cc-1b3d-0410-9834-0bbf25fbcc57>
Wed, 16 Jun 2010 21:13:47 +0000 (21:13 +0000)
git-svn-id: http://zxing.googlecode.com/svn/trunk@1438 59b500cc-1b3d-0410-9834-0bbf25fbcc57

android/src/com/google/zxing/client/android/CaptureActivity.java
core/src/com/google/zxing/ResultMetadataType.java
core/src/com/google/zxing/oned/EANManufacturerOrgSupport.java [new file with mode: 0644]
core/src/com/google/zxing/oned/UPCEANExtensionSupport.java
core/src/com/google/zxing/oned/UPCEANReader.java
core/test/src/com/google/zxing/oned/EANManufacturerOrgSupportTest.java [new file with mode: 0644]

index fbdb29d..02cb6b0 100755 (executable)
@@ -68,7 +68,6 @@ 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;
@@ -132,10 +131,11 @@ public final class CaptureActivity extends Activity implements SurfaceHolder.Cal
 
   private static final Set<ResultMetadataType> DISPLAYABLE_METADATA_TYPES;
   static {
-    DISPLAYABLE_METADATA_TYPES = new HashSet<ResultMetadataType>(3);
+    DISPLAYABLE_METADATA_TYPES = new HashSet<ResultMetadataType>(5);
     DISPLAYABLE_METADATA_TYPES.add(ResultMetadataType.ISSUE_NUMBER);
     DISPLAYABLE_METADATA_TYPES.add(ResultMetadataType.SUGGESTED_PRICE);
     DISPLAYABLE_METADATA_TYPES.add(ResultMetadataType.ERROR_CORRECTION_LEVEL);
+    DISPLAYABLE_METADATA_TYPES.add(ResultMetadataType.POSSIBLE_COUNTRY);
   }
 
   private enum Source {
index 7e47b0e..33d69d9 100644 (file)
@@ -74,6 +74,12 @@ public final class ResultMetadataType {
    */
   public static final ResultMetadataType SUGGESTED_PRICE = new ResultMetadataType("SUGGESTED_PRICE");
 
+  /**
+   * For some products, the possible country of manufacture as a {@link String} denoting the
+   * ISO country code. Some map to multiple possible countries, like "US/CA".
+   */
+  public static final ResultMetadataType POSSIBLE_COUNTRY = new ResultMetadataType("POSSIBLE_COUNTRY");
+
   private final String name;
 
   private ResultMetadataType(String name) {
diff --git a/core/src/com/google/zxing/oned/EANManufacturerOrgSupport.java b/core/src/com/google/zxing/oned/EANManufacturerOrgSupport.java
new file mode 100644 (file)
index 0000000..c477ca8
--- /dev/null
@@ -0,0 +1,170 @@
+/*
+ * 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.Vector;
+
+/**
+ * Records EAN prefix to GS1 Member Organization, where the member organization
+ * correlates strongly with a country. This is an imperfect means of identifying
+ * a country of origin by EAN-13 barcode value. See
+ * <a href="http://en.wikipedia.org/wiki/List_of_GS1_country_codes">
+ * http://en.wikipedia.org/wiki/List_of_GS1_country_codes</a>.
+ *
+ * @author Sean Owen
+ */
+final class EANManufacturerOrgSupport {
+
+  private final Vector ranges = new Vector();
+  private final Vector countryIdentifiers = new Vector();
+
+  String lookupCountryIdentifier(String productCode) {
+    initIfNeeded();
+    int prefix = Integer.parseInt(productCode.substring(0, 3));
+    int max = ranges.size();
+    for (int i = 0; i < max; i++) {
+      int[] range = (int[]) ranges.elementAt(i);
+      int start = range[0];
+      if (prefix < start) {
+        return null;
+      }
+      int end = range.length == 1 ? start : range[1];
+      if (prefix <= end) {
+        return (String) countryIdentifiers.elementAt(i);
+      }
+    }
+    return null;
+  }
+  
+  private void add(int[] range, String id) {
+    ranges.addElement(range);
+    countryIdentifiers.addElement(id);
+  }
+  
+  private synchronized void initIfNeeded() {
+    if (!ranges.isEmpty()) {
+      return;
+    }
+    add(new int[] {0,19},    "US/CA");
+    add(new int[] {30,39},   "US");
+    add(new int[] {60,139},  "US/CA");
+    add(new int[] {300,379}, "FR");
+    add(new int[] {380},     "BG");
+    add(new int[] {383},     "SI");
+    add(new int[] {385},     "HR");
+    add(new int[] {387},     "BA");
+    add(new int[] {400,440}, "DE");
+    add(new int[] {450,459}, "JP");
+    add(new int[] {460,469}, "RU");
+    add(new int[] {471},     "TW");
+    add(new int[] {474},     "EE");
+    add(new int[] {475},     "LV");
+    add(new int[] {476},     "AZ");
+    add(new int[] {477},     "LT");
+    add(new int[] {478},     "UZ");
+    add(new int[] {479},     "LK");
+    add(new int[] {480},     "PH");
+    add(new int[] {481},     "BY");
+    add(new int[] {482},     "UA");
+    add(new int[] {484},     "MD");
+    add(new int[] {485},     "AM");
+    add(new int[] {486},     "GE");
+    add(new int[] {487},     "KZ");
+    add(new int[] {489},     "HK");
+    add(new int[] {490,499}, "JP");    
+    add(new int[] {500,509}, "GB");    
+    add(new int[] {520},     "GR");
+    add(new int[] {528},     "LB");
+    add(new int[] {529},     "CY");
+    add(new int[] {531},     "MK");
+    add(new int[] {535},     "MT");
+    add(new int[] {539},     "IE");
+    add(new int[] {540,549}, "BE/LU");    
+    add(new int[] {560},     "PT");
+    add(new int[] {569},     "IS");
+    add(new int[] {570,579}, "DK");
+    add(new int[] {590},     "PL");
+    add(new int[] {594},     "RO");
+    add(new int[] {599},     "HU");
+    add(new int[] {600,601}, "ZA");
+    add(new int[] {603},     "GH");    
+    add(new int[] {608},     "BH");
+    add(new int[] {609},     "MU");
+    add(new int[] {611},     "MA");
+    add(new int[] {613},     "DZ");
+    add(new int[] {616},     "KE");
+    add(new int[] {618},     "CI");    
+    add(new int[] {619},     "TN");
+    add(new int[] {621},     "SY");
+    add(new int[] {622},     "EG");
+    add(new int[] {624},     "LY");
+    add(new int[] {625},     "JO");
+    add(new int[] {626},     "IR");
+    add(new int[] {627},     "KW");
+    add(new int[] {628},     "SA");
+    add(new int[] {629},     "AE");
+    add(new int[] {640,649}, "FI");
+    add(new int[] {690,695}, "CN");
+    add(new int[] {700,709}, "NO");
+    add(new int[] {729},     "IL");
+    add(new int[] {730,739}, "SE");
+    add(new int[] {740},     "GT");
+    add(new int[] {741},     "SV");
+    add(new int[] {742},     "HN");
+    add(new int[] {743},     "NI");
+    add(new int[] {744},     "CR");
+    add(new int[] {745},     "PA");
+    add(new int[] {746},     "DO");
+    add(new int[] {750},     "MX");
+    add(new int[] {754,755}, "CA");
+    add(new int[] {759},     "VE");
+    add(new int[] {760,769}, "CH");
+    add(new int[] {770},     "CO");
+    add(new int[] {773},     "UY");
+    add(new int[] {775},     "PE");
+    add(new int[] {777},     "BO");
+    add(new int[] {779},     "AR");
+    add(new int[] {780},     "CL");
+    add(new int[] {784},     "PY");
+    add(new int[] {785},     "PE");  
+    add(new int[] {786},     "EC");
+    add(new int[] {789,790}, "BR");
+    add(new int[] {800,839}, "IT");
+    add(new int[] {840,849}, "ES");
+    add(new int[] {850},     "CU");
+    add(new int[] {858},     "SK");
+    add(new int[] {859},     "CZ");
+    add(new int[] {860},     "YU");
+    add(new int[] {865},     "MN");    
+    add(new int[] {867},     "KP");
+    add(new int[] {868,869}, "TR");
+    add(new int[] {870,879}, "NL");
+    add(new int[] {880},     "KR");
+    add(new int[] {885},     "TH");
+    add(new int[] {888},     "SG");
+    add(new int[] {890},     "IN");
+    add(new int[] {893},     "VN");
+    add(new int[] {896},     "PK");    
+    add(new int[] {899},     "ID");
+    add(new int[] {900,919}, "AT");
+    add(new int[] {930,939}, "AU");
+    add(new int[] {940,949}, "AZ");
+    add(new int[] {955},     "MY");
+    add(new int[] {958},     "MO");
+  }
+
+}
index 3076941..f883566 100644 (file)
@@ -30,10 +30,8 @@ final class UPCEANExtensionSupport {
   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 {
@@ -60,9 +58,6 @@ final class UPCEANExtensionSupport {
     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];
 
@@ -78,14 +73,6 @@ final class UPCEANExtensionSupport {
         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++;
       }
index 6036157..7b4ea5e 100644 (file)
@@ -23,6 +23,7 @@ import com.google.zxing.FormatException;
 import com.google.zxing.NotFoundException;
 import com.google.zxing.ReaderException;
 import com.google.zxing.Result;
+import com.google.zxing.ResultMetadataType;
 import com.google.zxing.ResultPoint;
 import com.google.zxing.ResultPointCallback;
 import com.google.zxing.common.BitArray;
@@ -93,10 +94,12 @@ public abstract class UPCEANReader extends OneDReader {
 
   private final StringBuffer decodeRowStringBuffer;
   private final UPCEANExtensionSupport extensionReader;
+  private final EANManufacturerOrgSupport eanManSupport;
 
   protected UPCEANReader() {
     decodeRowStringBuffer = new StringBuffer(20);
     extensionReader = new UPCEANExtensionSupport();
+    eanManSupport = new EANManufacturerOrgSupport();
   }
 
   static int[] findStartGuardPattern(BitArray row) throws NotFoundException {
@@ -174,12 +177,13 @@ public abstract class UPCEANReader extends OneDReader {
 
     float left = (float) (startGuardRange[1] + startGuardRange[0]) / 2.0f;
     float right = (float) (endRange[1] + endRange[0]) / 2.0f;
+    BarcodeFormat format = getBarcodeFormat();
     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());
+        format);
 
     try {
       Result extensionResult = extensionReader.decodeRow(row, endRange[1]);
@@ -187,6 +191,14 @@ public abstract class UPCEANReader extends OneDReader {
     } catch (ReaderException re) {
       // continue
     }
+
+    if (BarcodeFormat.EAN_13.equals(format) || BarcodeFormat.UPC_A.equals(format)) {
+      String countryID = eanManSupport.lookupCountryIdentifier(resultString);
+      if (countryID != null) {
+        decodeResult.putMetadata(ResultMetadataType.POSSIBLE_COUNTRY, countryID);
+      }
+    }
+
     return decodeResult;
   }
 
diff --git a/core/test/src/com/google/zxing/oned/EANManufacturerOrgSupportTest.java b/core/test/src/com/google/zxing/oned/EANManufacturerOrgSupportTest.java
new file mode 100644 (file)
index 0000000..de5c05f
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * 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 junit.framework.TestCase;
+
+/**
+ * Tests {@link EANManufacturerOrgSupport}.
+ * 
+ * @author Sean Owen
+ */
+public final class EANManufacturerOrgSupportTest extends TestCase {
+
+  public void testLookup() {
+    EANManufacturerOrgSupport support = new EANManufacturerOrgSupport();
+    assertNull(support.lookupCountryIdentifier("472000"));
+    assertEquals("US/CA", support.lookupCountryIdentifier("000000"));
+    assertEquals("MO", support.lookupCountryIdentifier("958000"));
+    assertEquals("GB", support.lookupCountryIdentifier("500000"));
+    assertEquals("GB", support.lookupCountryIdentifier("509000"));
+  }
+
+}