Committed C# port from Mohamad
[zxing.git] / csharp / oned / Code128Reader.cs
diff --git a/csharp/oned/Code128Reader.cs b/csharp/oned/Code128Reader.cs
new file mode 100755 (executable)
index 0000000..9834a85
--- /dev/null
@@ -0,0 +1,459 @@
+/*\r
+* Licensed under the Apache License, Version 2.0 (the "License");\r
+* you may not use this file except in compliance with the License.\r
+* You may obtain a copy of the License at\r
+*\r
+*      http://www.apache.org/licenses/LICENSE-2.0\r
+*\r
+* Unless required by applicable law or agreed to in writing, software\r
+* distributed under the License is distributed on an "AS IS" BASIS,\r
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+* See the License for the specific language governing permissions and\r
+* limitations under the License.\r
+*/\r
+namespace com.google.zxing.oned\r
+{ \r
+    using System;\r
+    using System.Text;\r
+    using com.google.zxing.common;\r
+\r
+\r
+    public sealed class Code128Reader : AbstractOneDReader\r
+    { \r
+            private static  int[][] CODE_PATTERNS = new int[][]{\r
+              new int[]{2, 1, 2, 2, 2, 2}, // 0\r
+              new int[]{2, 2, 2, 1, 2, 2},\r
+              new int[]{2, 2, 2, 2, 2, 1},\r
+              new int[]{1, 2, 1, 2, 2, 3},\r
+              new int[]{1, 2, 1, 3, 2, 2},\r
+              new int[]{1, 3, 1, 2, 2, 2}, // 5\r
+              new int[]{1, 2, 2, 2, 1, 3},\r
+              new int[]{1, 2, 2, 3, 1, 2},\r
+              new int[]{1, 3, 2, 2, 1, 2},\r
+              new int[]{2, 2, 1, 2, 1, 3},\r
+              new int[]{2, 2, 1, 3, 1, 2}, // 10\r
+              new int[]{2, 3, 1, 2, 1, 2},\r
+              new int[]{1, 1, 2, 2, 3, 2},\r
+              new int[]{1, 2, 2, 1, 3, 2},\r
+              new int[]{1, 2, 2, 2, 3, 1},\r
+              new int[]{1, 1, 3, 2, 2, 2}, // 15\r
+              new int[]{1, 2, 3, 1, 2, 2},\r
+              new int[]{1, 2, 3, 2, 2, 1},\r
+              new int[]{2, 2, 3, 2, 1, 1},\r
+              new int[]{2, 2, 1, 1, 3, 2},\r
+              new int[]{2, 2, 1, 2, 3, 1}, // 20\r
+              new int[]{2, 1, 3, 2, 1, 2},\r
+              new int[]{2, 2, 3, 1, 1, 2},\r
+              new int[]{3, 1, 2, 1, 3, 1},\r
+              new int[]{3, 1, 1, 2, 2, 2},\r
+              new int[]{3, 2, 1, 1, 2, 2}, // 25\r
+              new int[]{3, 2, 1, 2, 2, 1},\r
+              new int[]{3, 1, 2, 2, 1, 2},\r
+              new int[]{3, 2, 2, 1, 1, 2},\r
+              new int[]{3, 2, 2, 2, 1, 1},\r
+              new int[]{2, 1, 2, 1, 2, 3}, // 30\r
+              new int[]{2, 1, 2, 3, 2, 1},\r
+              new int[]{2, 3, 2, 1, 2, 1},\r
+              new int[]{1, 1, 1, 3, 2, 3},\r
+              new int[]{1, 3, 1, 1, 2, 3},\r
+              new int[]{1, 3, 1, 3, 2, 1}, // 35\r
+              new int[]{1, 1, 2, 3, 1, 3},\r
+              new int[]{1, 3, 2, 1, 1, 3},\r
+              new int[]{1, 3, 2, 3, 1, 1},\r
+              new int[]{2, 1, 1, 3, 1, 3},\r
+              new int[]{2, 3, 1, 1, 1, 3}, // 40\r
+              new int[]{2, 3, 1, 3, 1, 1},\r
+              new int[]{1, 1, 2, 1, 3, 3},\r
+              new int[]{1, 1, 2, 3, 3, 1},\r
+              new int[]{1, 3, 2, 1, 3, 1},\r
+              new int[]{1, 1, 3, 1, 2, 3}, // 45\r
+              new int[]{1, 1, 3, 3, 2, 1},\r
+              new int[]{1, 3, 3, 1, 2, 1},\r
+              new int[]{3, 1, 3, 1, 2, 1},\r
+              new int[]{2, 1, 1, 3, 3, 1},\r
+              new int[]{2, 3, 1, 1, 3, 1}, // 50\r
+              new int[]{2, 1, 3, 1, 1, 3},\r
+              new int[]{2, 1, 3, 3, 1, 1},\r
+              new int[]{2, 1, 3, 1, 3, 1},\r
+              new int[]{3, 1, 1, 1, 2, 3},\r
+              new int[]{3, 1, 1, 3, 2, 1}, // 55\r
+              new int[]{3, 3, 1, 1, 2, 1},\r
+              new int[]{3, 1, 2, 1, 1, 3},\r
+              new int[]{3, 1, 2, 3, 1, 1},\r
+              new int[]{3, 3, 2, 1, 1, 1},\r
+              new int[]{3, 1, 4, 1, 1, 1}, // 60\r
+              new int[]{2, 2, 1, 4, 1, 1},\r
+              new int[]{4, 3, 1, 1, 1, 1},\r
+              new int[]{1, 1, 1, 2, 2, 4},\r
+              new int[]{1, 1, 1, 4, 2, 2},\r
+              new int[] {1, 2, 1, 1, 2, 4}, // 65\r
+              new int[]{1, 2, 1, 4, 2, 1},\r
+              new int[]{1, 4, 1, 1, 2, 2},\r
+              new int[]{1, 4, 1, 2, 2, 1},\r
+              new int[]{1, 1, 2, 2, 1, 4},\r
+              new int[]{1, 1, 2, 4, 1, 2}, // 70\r
+              new int[]{1, 2, 2, 1, 1, 4},\r
+              new int[]{1, 2, 2, 4, 1, 1},\r
+              new int[]{1, 4, 2, 1, 1, 2},\r
+              new int[]{1, 4, 2, 2, 1, 1},\r
+              new int[]{2, 4, 1, 2, 1, 1}, // 75\r
+              new int[]{2, 2, 1, 1, 1, 4},\r
+              new int[]{4, 1, 3, 1, 1, 1},\r
+              new int[]{2, 4, 1, 1, 1, 2},\r
+              new int[]{1, 3, 4, 1, 1, 1},\r
+              new int[]{1, 1, 1, 2, 4, 2}, // 80\r
+              new int[]{1, 2, 1, 1, 4, 2},\r
+              new int[]{1, 2, 1, 2, 4, 1},\r
+              new int[]{1, 1, 4, 2, 1, 2},\r
+              new int[]{1, 2, 4, 1, 1, 2},\r
+              new int[]{1, 2, 4, 2, 1, 1}, // 85\r
+              new int[]{4, 1, 1, 2, 1, 2},\r
+              new int[]{4, 2, 1, 1, 1, 2},\r
+              new int[]{4, 2, 1, 2, 1, 1},\r
+              new int[]{2, 1, 2, 1, 4, 1},\r
+              new int[]{2, 1, 4, 1, 2, 1}, // 90\r
+              new int[]{4, 1, 2, 1, 2, 1},\r
+              new int[]{1, 1, 1, 1, 4, 3},\r
+              new int[]{1, 1, 1, 3, 4, 1},\r
+              new int[]{1, 3, 1, 1, 4, 1},\r
+              new int[]{1, 1, 4, 1, 1, 3}, // 95\r
+              new int[]{1, 1, 4, 3, 1, 1},\r
+              new int[]{4, 1, 1, 1, 1, 3},\r
+              new int[]{4, 1, 1, 3, 1, 1},\r
+              new int[]{1, 1, 3, 1, 4, 1},\r
+              new int[]{1, 1, 4, 1, 3, 1}, // 100\r
+              new int[]{3, 1, 1, 1, 4, 1},\r
+              new int[]{4, 1, 1, 1, 3, 1},\r
+              new int[]{2, 1, 1, 4, 1, 2},\r
+              new int[]{2, 1, 1, 2, 1, 4},\r
+              new int[]{2, 1, 1, 2, 3, 2}, // 105\r
+              new int[]{2, 3, 3, 1, 1, 1, 2}\r
+          };\r
+\r
+      private static  int MAX_AVG_VARIANCE = (int) (PATTERN_MATCH_RESULT_SCALE_FACTOR * 0.25f);\r
+      private static  int MAX_INDIVIDUAL_VARIANCE = (int) (PATTERN_MATCH_RESULT_SCALE_FACTOR * 0.7f);\r
+\r
+      private const int CODE_SHIFT = 98;\r
+\r
+      private const int CODE_CODE_C = 99;\r
+      private const int CODE_CODE_B = 100;\r
+      private const int CODE_CODE_A = 101;\r
+\r
+      private const int CODE_FNC_1 = 102;\r
+      private const int CODE_FNC_2 = 97;\r
+      private const int CODE_FNC_3 = 96;\r
+      private const int CODE_FNC_4_A = 101;\r
+      private const int CODE_FNC_4_B = 100;\r
+\r
+      private const  int CODE_START_A = 103;\r
+      private const int CODE_START_B = 104;\r
+      private const int CODE_START_C = 105;\r
+      private const int CODE_STOP = 106;\r
+\r
+      private static int[] findStartPattern(BitArray row) {\r
+        int width = row.getSize();\r
+        int rowOffset = 0;\r
+        while (rowOffset < width) {\r
+          if (row.get(rowOffset)) {\r
+            break;\r
+          }\r
+          rowOffset++;\r
+        }\r
+\r
+        int counterPosition = 0;\r
+        int[] counters = new int[6];\r
+        int patternStart = rowOffset;\r
+        bool isWhite = false;\r
+        int patternLength = counters.Length;\r
+\r
+        for (int i = rowOffset; i < width; i++) {\r
+          bool pixel = row.get(i);\r
+          if ((!pixel && isWhite) || (pixel && !isWhite)) {\r
+            counters[counterPosition]++;\r
+          } else {\r
+            if (counterPosition == patternLength - 1) {\r
+              int bestVariance = MAX_AVG_VARIANCE;\r
+              int bestMatch = -1;\r
+              for (int startCode = CODE_START_A; startCode <= CODE_START_C; startCode++) {\r
+                int variance = patternMatchVariance(counters, CODE_PATTERNS[startCode], MAX_INDIVIDUAL_VARIANCE);\r
+                if (variance < bestVariance) {\r
+                  bestVariance = variance;\r
+                  bestMatch = startCode;\r
+                }\r
+              }\r
+              if (bestMatch >= 0) {\r
+                // Look for whitespace before start pattern, >= 50% of width of start pattern            \r
+                if (row.isRange(Math.Max(0, patternStart - (i - patternStart) / 2), patternStart, false)) {\r
+                  return new int[]{patternStart, i, bestMatch};\r
+                }\r
+              }\r
+              patternStart += counters[0] + counters[1];\r
+              for (int y = 2; y < patternLength; y++) {\r
+                counters[y - 2] = counters[y];\r
+              }\r
+              counters[patternLength - 2] = 0;\r
+              counters[patternLength - 1] = 0;\r
+              counterPosition--;\r
+            } else {\r
+              counterPosition++;\r
+            }\r
+            counters[counterPosition] = 1;\r
+            isWhite = !isWhite;\r
+          }\r
+        }\r
+        throw new ReaderException();\r
+      }\r
+\r
+      private static int decodeCode(BitArray row, int[] counters, int rowOffset) {\r
+        recordPattern(row, rowOffset, counters);\r
+        int bestVariance = MAX_AVG_VARIANCE; // worst variance we'll accept\r
+        int bestMatch = -1;\r
+        for (int d = 0; d < CODE_PATTERNS.Length; d++) {\r
+          int[] pattern = CODE_PATTERNS[d];\r
+          int variance = patternMatchVariance(counters, pattern, MAX_INDIVIDUAL_VARIANCE);\r
+          if (variance < bestVariance) {\r
+            bestVariance = variance;\r
+            bestMatch = d;\r
+          }\r
+        }\r
+        // TODO We're overlooking the fact that the STOP pattern has 7 values, not 6\r
+        if (bestMatch >= 0) {\r
+          return bestMatch;\r
+        } else {\r
+          throw new ReaderException();\r
+        }\r
+      }\r
+\r
+      public override Result decodeRow(int rowNumber, BitArray row, System.Collections.Hashtable hints) {\r
+\r
+        int[] startPatternInfo = findStartPattern(row);\r
+        int startCode = startPatternInfo[2];\r
+        int codeSet;\r
+        switch (startCode) {\r
+          case CODE_START_A:\r
+            codeSet = CODE_CODE_A;\r
+            break;\r
+          case CODE_START_B:\r
+            codeSet = CODE_CODE_B;\r
+            break;\r
+          case CODE_START_C:\r
+            codeSet = CODE_CODE_C;\r
+            break;\r
+          default:\r
+            throw new ReaderException();\r
+        }\r
+\r
+        bool done = false;\r
+        bool isNextShifted = false;\r
+\r
+        StringBuilder result = new StringBuilder();\r
+        int lastStart = startPatternInfo[0];\r
+        int nextStart = startPatternInfo[1];\r
+        int[] counters = new int[6];\r
+\r
+        int lastCode = 0;\r
+        int code = 0;\r
+        int checksumTotal = startCode;\r
+        int multiplier = 0;\r
+        bool lastCharacterWasPrintable = true;\r
+\r
+        while (!done) {\r
+\r
+          bool unshift = isNextShifted;\r
+          isNextShifted = false;\r
+\r
+          // Save off last code\r
+          lastCode = code;\r
+\r
+          // Decode another code from image\r
+          code = decodeCode(row, counters, nextStart);\r
+\r
+          // Remember whether the last code was printable or not (excluding CODE_STOP)\r
+          if (code != CODE_STOP) {\r
+            lastCharacterWasPrintable = true;\r
+          }\r
+\r
+          // Add to checksum computation (if not CODE_STOP of course)\r
+          if (code != CODE_STOP) {\r
+            multiplier++;\r
+            checksumTotal += multiplier * code;\r
+          }\r
+\r
+          // Advance to where the next code will to start\r
+          lastStart = nextStart;\r
+          for (int i = 0; i < counters.Length; i++) {\r
+            nextStart += counters[i];\r
+          }\r
+\r
+          // Take care of illegal start codes\r
+          switch (code) {\r
+            case CODE_START_A:\r
+            case CODE_START_B:\r
+            case CODE_START_C:\r
+              throw new ReaderException();\r
+          }\r
+\r
+          switch (codeSet) {\r
+\r
+            case CODE_CODE_A:\r
+              if (code < 64) {\r
+                result.Append((char) (' ' + code));\r
+              } else if (code < 96) {\r
+                result.Append((char) (code - 64));\r
+              } else {\r
+                // Don't let CODE_STOP, which always appears, affect whether whether we think the last code\r
+                // was printable or not\r
+                if (code != CODE_STOP) {\r
+                  lastCharacterWasPrintable = false;\r
+                }\r
+                switch (code) {\r
+                  case CODE_FNC_1:\r
+                  case CODE_FNC_2:\r
+                  case CODE_FNC_3:\r
+                  case CODE_FNC_4_A:\r
+                    // do nothing?\r
+                    break;\r
+                  case CODE_SHIFT:\r
+                    isNextShifted = true;\r
+                    codeSet = CODE_CODE_B;\r
+                    break;\r
+                  case CODE_CODE_B:\r
+                    codeSet = CODE_CODE_B;\r
+                    break;\r
+                  case CODE_CODE_C:\r
+                    codeSet = CODE_CODE_C;\r
+                    break;\r
+                  case CODE_STOP:\r
+                    done = true;\r
+                    break;\r
+                }\r
+              }\r
+              break;\r
+            case CODE_CODE_B:\r
+              if (code < 96) {\r
+                result.Append((char) (' ' + code));\r
+              } else {\r
+                if (code != CODE_STOP) {\r
+                  lastCharacterWasPrintable = false;\r
+                }\r
+                switch (code) {\r
+                  case CODE_FNC_1:\r
+                  case CODE_FNC_2:\r
+                  case CODE_FNC_3:\r
+                  case CODE_FNC_4_B:\r
+                    // do nothing?\r
+                    break;\r
+                  case CODE_SHIFT:\r
+                    isNextShifted = true;\r
+                    codeSet = CODE_CODE_C;\r
+                    break;\r
+                  case CODE_CODE_A:\r
+                    codeSet = CODE_CODE_A;\r
+                    break;\r
+                  case CODE_CODE_C:\r
+                    codeSet = CODE_CODE_C;\r
+                    break;\r
+                  case CODE_STOP:\r
+                    done = true;\r
+                    break;\r
+                }\r
+              }\r
+              break;\r
+            case CODE_CODE_C:\r
+              if (code < 100) {\r
+                if (code < 10) {\r
+                  result.Append('0');\r
+                }\r
+                result.Append(code);\r
+              } else {\r
+                if (code != CODE_STOP) {\r
+                  lastCharacterWasPrintable = false;\r
+                }\r
+                switch (code) {\r
+                  case CODE_FNC_1:\r
+                    // do nothing?\r
+                    break;\r
+                  case CODE_CODE_A:\r
+                    codeSet = CODE_CODE_A;\r
+                    break;\r
+                  case CODE_CODE_B:\r
+                    codeSet = CODE_CODE_B;\r
+                    break;\r
+                  case CODE_STOP:\r
+                    done = true;\r
+                    break;\r
+                }\r
+              }\r
+              break;\r
+          }\r
+\r
+          // Unshift back to another code set if we were shifted\r
+          if (unshift) {\r
+            switch (codeSet) {\r
+              case CODE_CODE_A:\r
+                codeSet = CODE_CODE_C;\r
+                break;\r
+              case CODE_CODE_B:\r
+                codeSet = CODE_CODE_A;\r
+                break;\r
+              case CODE_CODE_C:\r
+                codeSet = CODE_CODE_B;\r
+                break;\r
+            }\r
+          }\r
+\r
+        }\r
+\r
+        // Check for ample whitespice following pattern, but, to do this we first need to remember that we\r
+        // fudged decoding CODE_STOP since it actually has 7 bars, not 6. There is a black bar left to read off.\r
+        // Would be slightly better to properly read. Here we just skip it:\r
+        while (row.get(nextStart)) {\r
+          nextStart++;\r
+        }\r
+        if (!row.isRange(nextStart, Math.Min(row.getSize(), nextStart + (nextStart - lastStart) / 2), false)) {\r
+          throw new ReaderException();\r
+        }\r
+\r
+        // Pull out from sum the value of the penultimate check code\r
+        checksumTotal -= multiplier * lastCode;\r
+        // lastCode is the checksum then:\r
+        if (checksumTotal % 103 != lastCode) {\r
+          throw new ReaderException();\r
+        }\r
+\r
+        // Need to pull out the check digits from string\r
+        int resultLength = result.Length;\r
+        // Only bother if, well, the result had at least one character, and if the checksum digit happened\r
+        // to be a printable character. If it was just interpreted as a control code, nothing to remove\r
+        if (resultLength > 0 && lastCharacterWasPrintable) {\r
+          if (codeSet == CODE_CODE_C) {\r
+              result.Remove(resultLength - 2, resultLength);\r
+          } else {\r
+              result.Remove(resultLength - 1, resultLength);\r
+          }\r
+        }\r
+\r
+        String resultString = result.ToString();\r
+\r
+        if (resultString.Length == 0) {\r
+          // Almost surely a false positive\r
+          throw new ReaderException();\r
+        }\r
+\r
+        float left = (float) (startPatternInfo[1] + startPatternInfo[0]) / 2.0f;\r
+        float right = (float) (nextStart + lastStart) / 2.0f;\r
+        return new Result(\r
+            resultString,\r
+            null,\r
+            new ResultPoint[]{\r
+                new GenericResultPoint(left, (float) rowNumber),\r
+                new GenericResultPoint(right, (float) rowNumber)},\r
+            BarcodeFormat.CODE_128);\r
+\r
+      }\r
+    \r
+    }\r
+\r
+\r
+\r
+}
\ No newline at end of file