New C# port from Suraj Supekar
[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..e7dc192
--- /dev/null
@@ -0,0 +1,444 @@
+/*\r
+* Copyright 2008 ZXing authors\r
+*\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
+using System;\r
+using BarcodeFormat = com.google.zxing.BarcodeFormat;\r
+using ReaderException = com.google.zxing.ReaderException;\r
+using Result = com.google.zxing.Result;\r
+using ResultPoint = com.google.zxing.ResultPoint;\r
+using BitArray = com.google.zxing.common.BitArray;\r
+namespace com.google.zxing.oned\r
+{\r
+       \r
+       /// <summary> <p>Decodes Code 128 barcodes.</p>\r
+       /// \r
+       /// </summary>\r
+       /// <author>  Sean Owen\r
+       /// </author>\r
+       /// <author>www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source \r
+       /// </author>\r
+       public sealed class Code128Reader:OneDReader\r
+       {\r
+               \r
+               //UPGRADE_NOTE: Final was removed from the declaration of 'CODE_PATTERNS'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"\r
+               private static readonly int[][] CODE_PATTERNS = new int[][]{new int[]{2, 1, 2, 2, 2, 2}, new int[]{2, 2, 2, 1, 2, 2}, new int[]{2, 2, 2, 2, 2, 1}, new int[]{1, 2, 1, 2, 2, 3}, new int[]{1, 2, 1, 3, 2, 2}, new int[]{1, 3, 1, 2, 2, 2}, new int[]{1, 2, 2, 2, 1, 3}, new int[]{1, 2, 2, 3, 1, 2}, new int[]{1, 3, 2, 2, 1, 2}, new int[]{2, 2, 1, 2, 1, 3}, new int[]{2, 2, 1, 3, 1, 2}, new int[]{2, 3, 1, 2, 1, 2}, new int[]{1, 1, 2, 2, 3, 2}, new int[]{1, 2, 2, 1, 3, 2}, new int[]{1, 2, 2, 2, 3, 1}, new int[]{1, 1, 3, 2, 2, 2}, new int[]{1, 2, 3, 1, 2, 2}, new int[]{1, 2, 3, 2, 2, 1}, new int[]{2, 2, 3, 2, 1, 1}, new int[]{2, 2, 1, 1, 3, 2}, new int[]{2, 2, 1, 2, 3, 1}, new int[]{2, 1, 3, 2, 1, 2}, new int[]{2, 2, 3, 1, 1, 2}, new int[]{3, 1, 2, 1, 3, 1}, new int[]{3, 1, 1, 2, 2, 2}, new int[]{3, 2, 1, 1, 2, 2}, new int[]{3, 2, 1, 2, 2, 1}, new int[]{3, 1, 2, 2, 1, 2}, new int[]{3, 2, 2, 1, 1, 2}, new int[]{3, 2, 2, 2, 1, 1}, new int[]{2, 1, 2, 1, 2, 3}, new int[]{2, 1, 2, 3, 2, 1}, new int[]{2, 3, 2, 1, 2, 1}, new int[]{1, 1, 1, 3, 2, 3}, new int[]{1, 3, 1, 1, 2, 3}, new int[]{1, 3, 1, 3, 2, 1}, new int[]{1, 1, 2, 3, 1, 3}, new int[]{1, 3, 2, 1, 1, 3}, new int[]{1, 3, 2, 3, 1, 1}, new int[]{2, 1, 1, 3, 1, 3}, new int[]{2, 3, 1, 1, 1, 3}, new int[]{2, 3, 1, 3, 1, 1}, new int[]{1, 1, 2, 1, 3, 3}, new int[]{1, 1, 2, 3, 3, 1}, new int[]{1, 3, 2, 1, 3, 1}, new int[]{1, 1, 3, 1, 2, 3}, new int[]{1, 1, 3, 3, 2, 1}, new int[]{1, 3, 3, 1, 2, 1}, new int[]{3, 1, 3, 1, 2, 1}, new int[]{2, 1, 1, 3, 3, 1}, new int[]{2, 3, 1, 1, 3, 1}, new int[]{2, 1, 3, 1, 1, 3}, new int[]{2, 1, 3, 3, 1, 1}, new int[]{2, 1, 3, 1, 3, 1}, new int[]{3, 1, 1, 1, 2, 3}, new int[]{3, 1, 1, 3, 2, 1}, new int[]{3, 3, 1, 1, 2, 1}, new int[]{3, 1, 2, 1, 1, 3}, new int[]{3, 1, 2, 3, 1, 1}, new int[]{3, 3, 2, 1, 1, 1}, new int[]{3, 1, 4, 1, 1, 1}, new int[]{2, 2, 1, 4, 1, 1}, new int[]{4, 3, 1, 1, 1, 1}, new int[]{1, 1, 1, 2, 2, 4}, new int[]{1, 1, 1, 4, 2, 2}, new int[]{1, 2, 1, 1, 2, 4}, new int[]{1, 2, 1, 4, 2, 1}, new int[]{1, 4, 1, 1, 2, 2}, new \r
+                       int[]{1, 4, 1, 2, 2, 1}, new int[]{1, 1, 2, 2, 1, 4}, new int[]{1, 1, 2, 4, 1, 2}, new int[]{1, 2, 2, 1, 1, 4}, new int[]{1, 2, 2, 4, 1, 1}, new int[]{1, 4, 2, 1, 1, 2}, new int[]{1, 4, 2, 2, 1, 1}, new int[]{2, 4, 1, 2, 1, 1}, new int[]{2, 2, 1, 1, 1, 4}, new int[]{4, 1, 3, 1, 1, 1}, new int[]{2, 4, 1, 1, 1, 2}, new int[]{1, 3, 4, 1, 1, 1}, new int[]{1, 1, 1, 2, 4, 2}, new int[]{1, 2, 1, 1, 4, 2}, new int[]{1, 2, 1, 2, 4, 1}, new int[]{1, 1, 4, 2, 1, 2}, new int[]{1, 2, 4, 1, 1, 2}, new int[]{1, 2, 4, 2, 1, 1}, new int[]{4, 1, 1, 2, 1, 2}, new int[]{4, 2, 1, 1, 1, 2}, new int[]{4, 2, 1, 2, 1, 1}, new int[]{2, 1, 2, 1, 4, 1}, new int[]{2, 1, 4, 1, 2, 1}, new int[]{4, 1, 2, 1, 2, 1}, new int[]{1, 1, 1, 1, 4, 3}, new int[]{1, 1, 1, 3, 4, 1}, new int[]{1, 3, 1, 1, 4, 1}, new int[]{1, 1, 4, 1, 1, 3}, new int[]{1, 1, 4, 3, 1, 1}, new int[]{4, 1, 1, 1, 1, 3}, new int[]{4, 1, 1, 3, 1, 1}, new int[]{1, 1, 3, 1, 4, 1}, new int[]{1, 1, 4, 1, 3, 1}, new int[]{3, 1, 1, 1, 4, 1}, new int[]{4, 1, 1, 1, 3, 1}, new int[]{2, 1, 1, 4, 1, 2}, new int[]{2, 1, 1, 2, 1, 4}, new int[]{2, 1, 1, 2, 3, 2}, new int[]{2, 3, 3, 1, 1, 1, 2}};\r
+               \r
+               //UPGRADE_NOTE: Final was removed from the declaration of 'MAX_AVG_VARIANCE '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"\r
+               //UPGRADE_WARNING: Data types in Visual C# might be different.  Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"\r
+               private static readonly int MAX_AVG_VARIANCE = (int) (PATTERN_MATCH_RESULT_SCALE_FACTOR * 0.25f);\r
+               //UPGRADE_NOTE: Final was removed from the declaration of 'MAX_INDIVIDUAL_VARIANCE '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"\r
+               //UPGRADE_WARNING: Data types in Visual C# might be different.  Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"\r
+               private static readonly 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
+               {\r
+                       int width = row.Size;\r
+                       int rowOffset = 0;\r
+                       while (rowOffset < width)\r
+                       {\r
+                               if (row.get_Renamed(rowOffset))\r
+                               {\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
+                       {\r
+                               bool pixel = row.get_Renamed(i);\r
+                               if (pixel ^ isWhite)\r
+                               {\r
+                                       counters[counterPosition]++;\r
+                               }\r
+                               else\r
+                               {\r
+                                       if (counterPosition == patternLength - 1)\r
+                                       {\r
+                                               int bestVariance = MAX_AVG_VARIANCE;\r
+                                               int bestMatch = - 1;\r
+                                               for (int startCode = CODE_START_A; startCode <= CODE_START_C; startCode++)\r
+                                               {\r
+                                                       int variance = patternMatchVariance(counters, CODE_PATTERNS[startCode], MAX_INDIVIDUAL_VARIANCE);\r
+                                                       if (variance < bestVariance)\r
+                                                       {\r
+                                                               bestVariance = variance;\r
+                                                               bestMatch = startCode;\r
+                                                       }\r
+                                               }\r
+                                               if (bestMatch >= 0)\r
+                                               {\r
+                                                       // Look for whitespace before start pattern, >= 50% of width of start pattern\r
+                                                       if (row.isRange(System.Math.Max(0, patternStart - (i - patternStart) / 2), patternStart, false))\r
+                                                       {\r
+                                                               return new int[]{patternStart, i, bestMatch};\r
+                                                       }\r
+                                               }\r
+                                               patternStart += counters[0] + counters[1];\r
+                                               for (int y = 2; y < patternLength; y++)\r
+                                               {\r
+                                                       counters[y - 2] = counters[y];\r
+                                               }\r
+                                               counters[patternLength - 2] = 0;\r
+                                               counters[patternLength - 1] = 0;\r
+                                               counterPosition--;\r
+                                       }\r
+                                       else\r
+                                       {\r
+                                               counterPosition++;\r
+                                       }\r
+                                       counters[counterPosition] = 1;\r
+                                       isWhite = !isWhite;\r
+                               }\r
+                       }\r
+                       throw ReaderException.Instance;\r
+               }\r
+               \r
+               private static int decodeCode(BitArray row, int[] counters, int rowOffset)\r
+               {\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
+                       {\r
+                               int[] pattern = CODE_PATTERNS[d];\r
+                               int variance = patternMatchVariance(counters, pattern, MAX_INDIVIDUAL_VARIANCE);\r
+                               if (variance < bestVariance)\r
+                               {\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
+                       {\r
+                               return bestMatch;\r
+                       }\r
+                       else\r
+                       {\r
+                               throw ReaderException.Instance;\r
+                       }\r
+               }\r
+               \r
+               public override Result decodeRow(int rowNumber, BitArray row, System.Collections.Hashtable hints)\r
+               {\r
+                       \r
+                       int[] startPatternInfo = findStartPattern(row);\r
+                       int startCode = startPatternInfo[2];\r
+                       int codeSet;\r
+                       switch (startCode)\r
+                       {\r
+                               \r
+                               case CODE_START_A: \r
+                                       codeSet = CODE_CODE_A;\r
+                                       break;\r
+                               \r
+                               case CODE_START_B: \r
+                                       codeSet = CODE_CODE_B;\r
+                                       break;\r
+                               \r
+                               case CODE_START_C: \r
+                                       codeSet = CODE_CODE_C;\r
+                                       break;\r
+                               \r
+                               default: \r
+                                       throw ReaderException.Instance;\r
+                               \r
+                       }\r
+                       \r
+                       bool done = false;\r
+                       bool isNextShifted = false;\r
+                       \r
+                       System.Text.StringBuilder result = new System.Text.StringBuilder(20);\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
+                               \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
+                               {\r
+                                       lastCharacterWasPrintable = true;\r
+                               }\r
+                               \r
+                               // Add to checksum computation (if not CODE_STOP of course)\r
+                               if (code != CODE_STOP)\r
+                               {\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
+                               {\r
+                                       nextStart += counters[i];\r
+                               }\r
+                               \r
+                               // Take care of illegal start codes\r
+                               switch (code)\r
+                               {\r
+                                       \r
+                                       case CODE_START_A: \r
+                                       case CODE_START_B: \r
+                                       case CODE_START_C: \r
+                                               throw ReaderException.Instance;\r
+                                       }\r
+                               \r
+                               switch (codeSet)\r
+                               {\r
+                                       \r
+                                       \r
+                                       case CODE_CODE_A: \r
+                                               if (code < 64)\r
+                                               {\r
+                                                       result.Append((char) (' ' + code));\r
+                                               }\r
+                                               else if (code < 96)\r
+                                               {\r
+                                                       result.Append((char) (code - 64));\r
+                                               }\r
+                                               else\r
+                                               {\r
+                                                       // Don't let CODE_STOP, which always appears, affect whether whether we think the last\r
+                                                       // code was printable or not.\r
+                                                       if (code != CODE_STOP)\r
+                                                       {\r
+                                                               lastCharacterWasPrintable = false;\r
+                                                       }\r
+                                                       switch (code)\r
+                                                       {\r
+                                                               \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
+                                                               \r
+                                                               case CODE_SHIFT: \r
+                                                                       isNextShifted = true;\r
+                                                                       codeSet = CODE_CODE_B;\r
+                                                                       break;\r
+                                                               \r
+                                                               case CODE_CODE_B: \r
+                                                                       codeSet = CODE_CODE_B;\r
+                                                                       break;\r
+                                                               \r
+                                                               case CODE_CODE_C: \r
+                                                                       codeSet = CODE_CODE_C;\r
+                                                                       break;\r
+                                                               \r
+                                                               case CODE_STOP: \r
+                                                                       done = true;\r
+                                                                       break;\r
+                                                               }\r
+                                               }\r
+                                               break;\r
+                                       \r
+                                       case CODE_CODE_B: \r
+                                               if (code < 96)\r
+                                               {\r
+                                                       result.Append((char) (' ' + code));\r
+                                               }\r
+                                               else\r
+                                               {\r
+                                                       if (code != CODE_STOP)\r
+                                                       {\r
+                                                               lastCharacterWasPrintable = false;\r
+                                                       }\r
+                                                       switch (code)\r
+                                                       {\r
+                                                               \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
+                                                               \r
+                                                               case CODE_SHIFT: \r
+                                                                       isNextShifted = true;\r
+                                                                       codeSet = CODE_CODE_C;\r
+                                                                       break;\r
+                                                               \r
+                                                               case CODE_CODE_A: \r
+                                                                       codeSet = CODE_CODE_A;\r
+                                                                       break;\r
+                                                               \r
+                                                               case CODE_CODE_C: \r
+                                                                       codeSet = CODE_CODE_C;\r
+                                                                       break;\r
+                                                               \r
+                                                               case CODE_STOP: \r
+                                                                       done = true;\r
+                                                                       break;\r
+                                                               }\r
+                                               }\r
+                                               break;\r
+                                       \r
+                                       case CODE_CODE_C: \r
+                                               if (code < 100)\r
+                                               {\r
+                                                       if (code < 10)\r
+                                                       {\r
+                                                               result.Append('0');\r
+                                                       }\r
+                                                       result.Append(code);\r
+                                               }\r
+                                               else\r
+                                               {\r
+                                                       if (code != CODE_STOP)\r
+                                                       {\r
+                                                               lastCharacterWasPrintable = false;\r
+                                                       }\r
+                                                       switch (code)\r
+                                                       {\r
+                                                               \r
+                                                               case CODE_FNC_1: \r
+                                                                       // do nothing?\r
+                                                                       break;\r
+                                                               \r
+                                                               case CODE_CODE_A: \r
+                                                                       codeSet = CODE_CODE_A;\r
+                                                                       break;\r
+                                                               \r
+                                                               case CODE_CODE_B: \r
+                                                                       codeSet = CODE_CODE_B;\r
+                                                                       break;\r
+                                                               \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
+                               {\r
+                                       switch (codeSet)\r
+                                       {\r
+                                               \r
+                                               case CODE_CODE_A: \r
+                                                       codeSet = CODE_CODE_C;\r
+                                                       break;\r
+                                               \r
+                                               case CODE_CODE_B: \r
+                                                       codeSet = CODE_CODE_A;\r
+                                                       break;\r
+                                               \r
+                                               case CODE_CODE_C: \r
+                                                       codeSet = CODE_CODE_B;\r
+                                                       break;\r
+                                               }\r
+                               }\r
+                       }\r
+                       \r
+                       // Check for ample whitespace following pattern, but, to do this we first need to remember that\r
+                       // we fudged decoding CODE_STOP since it actually has 7 bars, not 6. There is a black bar left\r
+                       // to read off. Would be slightly better to properly read. Here we just skip it:\r
+                       int width = row.Size;\r
+                       while (nextStart < width && row.get_Renamed(nextStart))\r
+                       {\r
+                               nextStart++;\r
+                       }\r
+                       if (!row.isRange(nextStart, System.Math.Min(width, nextStart + (nextStart - lastStart) / 2), false))\r
+                       {\r
+                               throw ReaderException.Instance;\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
+                       {\r
+                               throw ReaderException.Instance;\r
+                       }\r
+                       \r
+                       // Need to pull out the check digits from string\r
+                       int resultLength = result.Length;\r
+                       // Only bother if the result had at least one character, and if the checksum digit happened to\r
+                       // be a printable character. If it was just interpreted as a control code, nothing to remove.\r
+                       if (resultLength > 0 && lastCharacterWasPrintable)\r
+                       {\r
+                               if (codeSet == CODE_CODE_C)\r
+                               {\r
+                                       result.Remove(resultLength - 2, resultLength - (resultLength - 2));\r
+                               }\r
+                               else\r
+                               {\r
+                                       result.Remove(resultLength - 1, resultLength - (resultLength - 1));\r
+                               }\r
+                       }\r
+                       \r
+                       System.String resultString = result.ToString();\r
+                       \r
+                       if (resultString.Length == 0)\r
+                       {\r
+                               // Almost surely a false positive\r
+                               throw ReaderException.Instance;\r
+                       }\r
+                       \r
+                       //UPGRADE_WARNING: Data types in Visual C# might be different.  Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"\r
+                       float left = (float) (startPatternInfo[1] + startPatternInfo[0]) / 2.0f;\r
+                       //UPGRADE_WARNING: Data types in Visual C# might be different.  Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"\r
+                       float right = (float) (nextStart + lastStart) / 2.0f;\r
+                       //UPGRADE_WARNING: Data types in Visual C# might be different.  Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"\r
+                       return new Result(resultString, null, new ResultPoint[]{new ResultPoint(left, (float) rowNumber), new ResultPoint(right, (float) rowNumber)}, BarcodeFormat.CODE_128);\r
+               }\r
+       }\r
+}
\ No newline at end of file