Go ahead and enable Code 93 in android
[zxing.git] / csharp / oned / Code39Reader.cs
index ebddc97..c425024 100755 (executable)
@@ -1,4 +1,6 @@
-/*\r
+/*\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
 * 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
-    using System;\r
-    using System.Text;\r
-    using com.google.zxing.common;\r
-\r
-\r
-    public sealed class Code39Reader : AbstractOneDReader \r
-    { \r
-          private static  String ALPHABET_STRING = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. *$/+%";\r
-          private static  char[] ALPHABET = ALPHABET_STRING.ToCharArray();\r
-\r
-          /**\r
-           * These represent the encodings of characters, as patterns of wide and narrow bars.\r
-           * The 9 least-significant bits of each int correspond to the pattern of wide and narrow,\r
-           * with 1s representing "wide" and 0s representing narrow.\r
-           */\r
-          private static  int[] CHARACTER_ENCODINGS = {\r
-              0x034, 0x121, 0x061, 0x160, 0x031, 0x130, 0x070, 0x025, 0x124, 0x064, // 0-9\r
-              0x109, 0x049, 0x148, 0x019, 0x118, 0x058, 0x00D, 0x10C, 0x04C, 0x01C, // A-J\r
-              0x103, 0x043, 0x142, 0x013, 0x112, 0x052, 0x007, 0x106, 0x046, 0x016, // K-T\r
-              0x181, 0x0C1, 0x1C0, 0x091, 0x190, 0x0D0, 0x085, 0x184, 0x0C4, 0x094, // U-*\r
-              0x0A8, 0x0A2, 0x08A, 0x02A // $-%\r
-          };\r
-\r
-          private static  int ASTERISK_ENCODING = CHARACTER_ENCODINGS[39];\r
-\r
-          private  bool usingCheckDigit;\r
-          private  bool extendedMode;\r
-\r
-          /**\r
-           * Creates a reader that assumes all encoded data is data, and does not treat the \r
-           * character as a check digit. It will not decoded "extended Code 39" sequences.\r
-           */\r
-          public Code39Reader() {\r
-            usingCheckDigit = false;\r
-            extendedMode = false;\r
-          }\r
-\r
-          /**\r
-           * Creates a reader that can be configured to check the last character as a check digit.\r
-           * It will not decoded "extended Code 39" sequences.\r
-           *\r
-           * @param usingCheckDigit if true, treat the last data character as a check digit, not\r
-           * data, and verify that the checksum passes.\r
-           */\r
-          public Code39Reader(bool usingCheckDigit) {\r
-            this.usingCheckDigit = usingCheckDigit;\r
-            this.extendedMode = false;\r
-          }\r
-\r
-          /**\r
-           * Creates a reader that can be configured to check the last character as a check digit,\r
-           * or optionally attempt to decode "extended Code 39" sequences that are used to encode\r
-           * the full ASCII character set.\r
-           *\r
-           * @param usingCheckDigit if true, treat the last data character as a check digit, not\r
-           * data, and verify that the checksum passes.\r
-           * @param extendedMode if true, will attempt to decode extended Code 39 sequences in the\r
-           * text.\r
-           */\r
-          public Code39Reader(bool usingCheckDigit, bool extendedMode) {\r
-            this.usingCheckDigit = usingCheckDigit;\r
-            this.extendedMode = extendedMode;\r
-          }\r
-\r
-          public override Result decodeRow(int rowNumber, BitArray row, System.Collections.Hashtable hints) {\r
-\r
-            int[] start = findAsteriskPattern(row);\r
-            int nextStart = start[1];\r
-            int end = row.getSize();\r
-\r
-            // Read off white space\r
-            while (nextStart < end && !row.get(nextStart)) {\r
-              nextStart++;\r
-            }\r
-\r
-            StringBuilder result = new StringBuilder();\r
-            int[] counters = new int[9];\r
-            char decodedChar;\r
-            int lastStart;\r
-            do {\r
-              recordPattern(row, nextStart, counters);\r
-              int pattern = toNarrowWidePattern(counters);\r
-              decodedChar = patternToChar(pattern);\r
-              result.Append(decodedChar);\r
-              lastStart = nextStart;\r
-              for (int i = 0; i < counters.Length; i++) {\r
-                nextStart += counters[i];\r
-              }\r
-              // Read off white space\r
-              while (nextStart < end && !row.get(nextStart)) {\r
-                nextStart++;\r
-              }\r
-            } while (decodedChar != '*');\r
-\r
-            result.Remove(result.Length - 1, 1);  // remove asterisk\r
-\r
-            // Look for whitespace after pattern:\r
-            int lastPatternSize = 0;\r
-            for (int i = 0; i < counters.Length; i++) {\r
-              lastPatternSize += counters[i];\r
-            }\r
-            int whiteSpaceAfterEnd = nextStart - lastStart - lastPatternSize;\r
-            // If 50% of last pattern size, following last pattern, is not whitespace, fail\r
-            // (but if it's whitespace to the very end of the image, that's OK)\r
-            if (nextStart != end && whiteSpaceAfterEnd / 2 < lastPatternSize) {\r
-              throw new ReaderException();\r
-            }\r
-\r
-            if (usingCheckDigit) {\r
-              int max = result.Length - 1;\r
-              int total = 0;\r
-              for (int i = 0; i < max; i++) {\r
-                total += ALPHABET_STRING.IndexOf(result[i]);\r
-              }\r
-              if (total % 43 != ALPHABET_STRING.IndexOf(result[max]))\r
-              {\r
-                throw new ReaderException();\r
-              }\r
-              result.Remove(max,1);\r
-            }\r
-\r
-            String resultString = result.ToString();\r
-            if (extendedMode) {\r
-              resultString = decodeExtended(resultString);\r
-            }\r
-\r
-            if (resultString.Length == 0) {\r
-              // Almost surely a false positive\r
-              throw new ReaderException();\r
-            }\r
-\r
-            float left = (float) (start[1] + start[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_39);\r
-\r
-          }\r
-\r
-          private static int[] findAsteriskPattern(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[9];\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
-                  try {\r
-                    if (toNarrowWidePattern(counters) == ASTERISK_ENCODING) {\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};\r
-                      }\r
-                    }\r
-                  } catch (ReaderException re) {\r
-                    // no match, continue\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 toNarrowWidePattern(int[] counters) {\r
-            int numCounters = counters.Length;\r
-            int maxNarrowCounter = 0;\r
-            int wideCounters;\r
-            do {\r
-              int minCounter = int.MaxValue;\r
-              for (int i = 0; i < numCounters; i++) {\r
-                int counter = counters[i];\r
-                if (counter < minCounter && counter > maxNarrowCounter) {\r
-                  minCounter = counter;\r
-                }\r
-              }\r
-              maxNarrowCounter = minCounter;\r
-              wideCounters = 0;\r
-              int totalWideCountersWidth = 0;\r
-              int pattern = 0;\r
-              for (int i = 0; i < numCounters; i++) {\r
-                int counter = counters[i];\r
-                if (counters[i] > maxNarrowCounter) {\r
-                  pattern |= 1 << (numCounters - 1 - i);\r
-                  wideCounters++;\r
-                  totalWideCountersWidth += counter;\r
-                }\r
-              }\r
-              if (wideCounters == 3) {\r
-                // Found 3 wide counters, but are they close enough in width?\r
-                // We can perform a cheap, conservative check to see if any individual\r
-                // counter is more than 1.5 times the average:\r
-                for (int i = 0; i < numCounters && wideCounters > 0; i++) {\r
-                  int counter = counters[i];\r
-                  if (counters[i] > maxNarrowCounter) {\r
-                    wideCounters--;\r
-                    // totalWideCountersWidth = 3 * average, so this checks if counter >= 3/2 * average\r
-                    if ((counter << 1) >= totalWideCountersWidth) {\r
-                      throw new ReaderException();\r
-                    }\r
-                  }\r
-                }\r
-                return pattern;\r
-              }\r
-            } while (wideCounters > 3);\r
-            throw new ReaderException();\r
-          }\r
-\r
-          private static char patternToChar(int pattern) {\r
-            for (int i = 0; i < CHARACTER_ENCODINGS.Length; i++) {\r
-              if (CHARACTER_ENCODINGS[i] == pattern) {\r
-                return ALPHABET[i];\r
-              }\r
-            }\r
-            throw new ReaderException();\r
-          }\r
-\r
-          private static String decodeExtended(String encoded) {\r
-            int Length = encoded.Length;\r
-            StringBuilder decoded = new StringBuilder(Length);\r
-            for (int i = 0; i < Length; i++) {\r
-              char c = encoded[i];\r
-              if (c == '+' || c == '$' || c == '%' || c == '/') {\r
-                char next = encoded[i + 1];\r
-                char decodedChar = '\0';\r
-                switch (c) {\r
-                  case '+':\r
-                    // +A to +Z map to a to z\r
-                    if (next >= 'A' && next <= 'Z') {\r
-                      decodedChar = (char) (next + 32);\r
-                    } else {\r
-                      throw new ReaderException();\r
-                    }\r
-                    break;\r
-                  case '$':\r
-                    // $A to $Z map to control codes SH to SB\r
-                    if (next >= 'A' && next <= 'Z') {\r
-                      decodedChar = (char) (next - 64);\r
-                    } else {\r
-                      throw new ReaderException();\r
-                    }\r
-                    break;\r
-                  case '%':\r
-                    // %A to %E map to control codes ESC to US\r
-                    if (next >= 'A' && next <= 'E') {\r
-                      decodedChar = (char) (next - 38);\r
-                    } else if (next >= 'F' && next <= 'W') {\r
-                      decodedChar = (char) (next - 11);\r
-                    } else {\r
-                      throw new ReaderException();\r
-                    }\r
-                    break;\r
-                  case '/':\r
-                    // /A to /O map to ! to , and /Z maps to :\r
-                    if (next >= 'A' && next <= 'O') {\r
-                      decodedChar = (char) (next - 32);\r
-                    } else if (next == 'Z') {\r
-                      decodedChar = ':';\r
-                    } else {\r
-                      throw new ReaderException();\r
-                    }\r
-                    break;\r
-                }\r
-                decoded.Append(decodedChar);\r
-                // bump up i again since we read two characters\r
-                i++;\r
-              } else {\r
-                decoded.Append(c);\r
-              }\r
-            }\r
-            return decoded.ToString();\r
-          }\r
-\r
-    \r
-    }\r
+       \r
+       /// <summary> <p>Decodes Code 39 barcodes. This does not support "Full ASCII Code 39" yet.</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 Code39Reader:OneDReader\r
+       {\r
+               \r
+               private const System.String ALPHABET_STRING = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. *$/+%";\r
+               //UPGRADE_NOTE: Final was removed from the declaration of 'ALPHABET '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"\r
+               private static readonly char[] ALPHABET = ALPHABET_STRING.ToCharArray();\r
+               \r
+               /// <summary> These represent the encodings of characters, as patterns of wide and narrow bars.\r
+               /// The 9 least-significant bits of each int correspond to the pattern of wide and narrow,\r
+               /// with 1s representing "wide" and 0s representing narrow.\r
+               /// </summary>\r
+               //UPGRADE_NOTE: Final was removed from the declaration of 'CHARACTER_ENCODINGS'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"\r
+               private static readonly int[] CHARACTER_ENCODINGS = new int[]{0x034, 0x121, 0x061, 0x160, 0x031, 0x130, 0x070, 0x025, 0x124, 0x064, 0x109, 0x049, 0x148, 0x019, 0x118, 0x058, 0x00D, 0x10C, 0x04C, 0x01C, 0x103, 0x043, 0x142, 0x013, 0x112, 0x052, 0x007, 0x106, 0x046, 0x016, 0x181, 0x0C1, 0x1C0, 0x091, 0x190, 0x0D0, 0x085, 0x184, 0x0C4, 0x094, 0x0A8, 0x0A2, 0x08A, 0x02A};\r
+               \r
+               //UPGRADE_NOTE: Final was removed from the declaration of 'ASTERISK_ENCODING '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"\r
+               private static readonly int ASTERISK_ENCODING = CHARACTER_ENCODINGS[39];\r
+               \r
+               //UPGRADE_NOTE: Final was removed from the declaration of 'usingCheckDigit '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"\r
+               private bool usingCheckDigit;\r
+               //UPGRADE_NOTE: Final was removed from the declaration of 'extendedMode '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"\r
+               private bool extendedMode;\r
+               \r
+               /// <summary> Creates a reader that assumes all encoded data is data, and does not treat the final\r
+               /// character as a check digit. It will not decoded "extended Code 39" sequences.\r
+               /// </summary>\r
+               public Code39Reader()\r
+               {\r
+                       usingCheckDigit = false;\r
+                       extendedMode = false;\r
+               }\r
+               \r
+               /// <summary> Creates a reader that can be configured to check the last character as a check digit.\r
+               /// It will not decoded "extended Code 39" sequences.\r
+               /// \r
+               /// </summary>\r
+               /// <param name="usingCheckDigit">if true, treat the last data character as a check digit, not\r
+               /// data, and verify that the checksum passes.\r
+               /// </param>\r
+               public Code39Reader(bool usingCheckDigit)\r
+               {\r
+                       this.usingCheckDigit = usingCheckDigit;\r
+                       this.extendedMode = false;\r
+               }\r
+               \r
+               /// <summary> Creates a reader that can be configured to check the last character as a check digit,\r
+               /// or optionally attempt to decode "extended Code 39" sequences that are used to encode\r
+               /// the full ASCII character set.\r
+               /// \r
+               /// </summary>\r
+               /// <param name="usingCheckDigit">if true, treat the last data character as a check digit, not\r
+               /// data, and verify that the checksum passes.\r
+               /// </param>\r
+               /// <param name="extendedMode">if true, will attempt to decode extended Code 39 sequences in the\r
+               /// text.\r
+               /// </param>\r
+               public Code39Reader(bool usingCheckDigit, bool extendedMode)\r
+               {\r
+                       this.usingCheckDigit = usingCheckDigit;\r
+                       this.extendedMode = extendedMode;\r
+               }\r
+               \r
+               public override Result decodeRow(int rowNumber, BitArray row, System.Collections.Hashtable hints)\r
+               {\r
+                       \r
+                       int[] start = findAsteriskPattern(row);\r
+                       int nextStart = start[1];\r
+                       int end = row.Size;\r
+                       \r
+                       // Read off white space\r
+                       while (nextStart < end && !row.get_Renamed(nextStart))\r
+                       {\r
+                               nextStart++;\r
+                       }\r
+                       \r
+                       System.Text.StringBuilder result = new System.Text.StringBuilder(20);\r
+                       int[] counters = new int[9];\r
+                       char decodedChar;\r
+                       int lastStart;\r
+                       do \r
+                       {\r
+                               recordPattern(row, nextStart, counters);\r
+                               int pattern = toNarrowWidePattern(counters);\r
+                               if (pattern < 0)\r
+                               {\r
+                                       throw ReaderException.Instance;\r
+                               }\r
+                               decodedChar = patternToChar(pattern);\r
+                               result.Append(decodedChar);\r
+                               lastStart = nextStart;\r
+                               for (int i = 0; i < counters.Length; i++)\r
+                               {\r
+                                       nextStart += counters[i];\r
+                               }\r
+                               // Read off white space\r
+                               while (nextStart < end && !row.get_Renamed(nextStart))\r
+                               {\r
+                                       nextStart++;\r
+                               }\r
+                       }\r
+                       while (decodedChar != '*');\r
+                       result.Remove(result.Length - 1, 1); // remove asterisk\r
+                       \r
+                       // Look for whitespace after pattern:\r
+                       int lastPatternSize = 0;\r
+                       for (int i = 0; i < counters.Length; i++)\r
+                       {\r
+                               lastPatternSize += counters[i];\r
+                       }\r
+                       int whiteSpaceAfterEnd = nextStart - lastStart - lastPatternSize;\r
+                       // If 50% of last pattern size, following last pattern, is not whitespace, fail\r
+                       // (but if it's whitespace to the very end of the image, that's OK)\r
+                       if (nextStart != end && whiteSpaceAfterEnd / 2 < lastPatternSize)\r
+                       {\r
+                               throw ReaderException.Instance;\r
+                       }\r
+                       \r
+                       if (usingCheckDigit)\r
+                       {\r
+                               int max = result.Length - 1;\r
+                               int total = 0;\r
+                               for (int i = 0; i < max; i++)\r
+                               {\r
+                                       total += ALPHABET_STRING.IndexOf((System.Char) result[i]);\r
+                               }\r
+                               if (total % 43 != ALPHABET_STRING.IndexOf((System.Char) result[max]))\r
+                               {\r
+                                       throw ReaderException.Instance;\r
+                               }\r
+                               result.Remove(max, 1);\r
+                       }\r
+                       \r
+                       System.String resultString = result.ToString();\r
+                       if (extendedMode)\r
+                       {\r
+                               resultString = decodeExtended(resultString);\r
+                       }\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) (start[1] + start[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_39);\r
+               }\r
+               \r
+               private static int[] findAsteriskPattern(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[9];\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
+                                               if (toNarrowWidePattern(counters) == ASTERISK_ENCODING)\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};\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
+               // For efficiency, returns -1 on failure. Not throwing here saved as many as 700 exceptions\r
+               // per image when using some of our blackbox images.\r
+               private static int toNarrowWidePattern(int[] counters)\r
+               {\r
+                       int numCounters = counters.Length;\r
+                       int maxNarrowCounter = 0;\r
+                       int wideCounters;\r
+                       do \r
+                       {\r
+                               int minCounter = System.Int32.MaxValue;\r
+                               for (int i = 0; i < numCounters; i++)\r
+                               {\r
+                                       int counter = counters[i];\r
+                                       if (counter < minCounter && counter > maxNarrowCounter)\r
+                                       {\r
+                                               minCounter = counter;\r
+                                       }\r
+                               }\r
+                               maxNarrowCounter = minCounter;\r
+                               wideCounters = 0;\r
+                               int totalWideCountersWidth = 0;\r
+                               int pattern = 0;\r
+                               for (int i = 0; i < numCounters; i++)\r
+                               {\r
+                                       int counter = counters[i];\r
+                                       if (counters[i] > maxNarrowCounter)\r
+                                       {\r
+                                               pattern |= 1 << (numCounters - 1 - i);\r
+                                               wideCounters++;\r
+                                               totalWideCountersWidth += counter;\r
+                                       }\r
+                               }\r
+                               if (wideCounters == 3)\r
+                               {\r
+                                       // Found 3 wide counters, but are they close enough in width?\r
+                                       // We can perform a cheap, conservative check to see if any individual\r
+                                       // counter is more than 1.5 times the average:\r
+                                       for (int i = 0; i < numCounters && wideCounters > 0; i++)\r
+                                       {\r
+                                               int counter = counters[i];\r
+                                               if (counters[i] > maxNarrowCounter)\r
+                                               {\r
+                                                       wideCounters--;\r
+                                                       // totalWideCountersWidth = 3 * average, so this checks if counter >= 3/2 * average\r
+                                                       if ((counter << 1) >= totalWideCountersWidth)\r
+                                                       {\r
+                                                               return - 1;\r
+                                                       }\r
+                                               }\r
+                                       }\r
+                                       return pattern;\r
+                               }\r
+                       }\r
+                       while (wideCounters > 3);\r
+                       return - 1;\r
+               }\r
+               \r
+               private static char patternToChar(int pattern)\r
+               {\r
+                       for (int i = 0; i < CHARACTER_ENCODINGS.Length; i++)\r
+                       {\r
+                               if (CHARACTER_ENCODINGS[i] == pattern)\r
+                               {\r
+                                       return ALPHABET[i];\r
+                               }\r
+                       }\r
+                       throw ReaderException.Instance;\r
+               }\r
+               \r
+               private static System.String decodeExtended(System.String encoded)\r
+               {\r
+                       int length = encoded.Length;\r
+                       System.Text.StringBuilder decoded = new System.Text.StringBuilder(length);\r
+                       for (int i = 0; i < length; i++)\r
+                       {\r
+                               char c = encoded[i];\r
+                               if (c == '+' || c == '$' || c == '%' || c == '/')\r
+                               {\r
+                                       char next = encoded[i + 1];\r
+                                       char decodedChar = '\x0000';\r
+                                       switch (c)\r
+                                       {\r
+                                               \r
+                                               case '+': \r
+                                                       // +A to +Z map to a to z\r
+                                                       if (next >= 'A' && next <= 'Z')\r
+                                                       {\r
+                                                               decodedChar = (char) (next + 32);\r
+                                                       }\r
+                                                       else\r
+                                                       {\r
+                                                               throw ReaderException.Instance;\r
+                                                       }\r
+                                                       break;\r
+                                               \r
+                                               case '$': \r
+                                                       // $A to $Z map to control codes SH to SB\r
+                                                       if (next >= 'A' && next <= 'Z')\r
+                                                       {\r
+                                                               decodedChar = (char) (next - 64);\r
+                                                       }\r
+                                                       else\r
+                                                       {\r
+                                                               throw ReaderException.Instance;\r
+                                                       }\r
+                                                       break;\r
+                                               \r
+                                               case '%': \r
+                                                       // %A to %E map to control codes ESC to US\r
+                                                       if (next >= 'A' && next <= 'E')\r
+                                                       {\r
+                                                               decodedChar = (char) (next - 38);\r
+                                                       }\r
+                                                       else if (next >= 'F' && next <= 'W')\r
+                                                       {\r
+                                                               decodedChar = (char) (next - 11);\r
+                                                       }\r
+                                                       else\r
+                                                       {\r
+                                                               throw ReaderException.Instance;\r
+                                                       }\r
+                                                       break;\r
+                                               \r
+                                               case '/': \r
+                                                       // /A to /O map to ! to , and /Z maps to :\r
+                                                       if (next >= 'A' && next <= 'O')\r
+                                                       {\r
+                                                               decodedChar = (char) (next - 32);\r
+                                                       }\r
+                                                       else if (next == 'Z')\r
+                                                       {\r
+                                                               decodedChar = ':';\r
+                                                       }\r
+                                                       else\r
+                                                       {\r
+                                                               throw ReaderException.Instance;\r
+                                                       }\r
+                                                       break;\r
+                                               }\r
+                                       decoded.Append(decodedChar);\r
+                                       // bump up i again since we read two characters\r
+                                       i++;\r
+                               }\r
+                               else\r
+                               {\r
+                                       decoded.Append(c);\r
+                               }\r
+                       }\r
+                       return decoded.ToString();\r
+               }\r
+       }\r
 }
\ No newline at end of file