--- /dev/null
+/*\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