Slovenian translation
[zxing.git] / csharp / oned / Code39Reader.cs
1 /*\r
2 * Copyright 2008 ZXing authors\r
3 *\r
4 * Licensed under the Apache License, Version 2.0 (the "License");\r
5 * you may not use this file except in compliance with the License.\r
6 * You may obtain a copy of the License at\r
7 *\r
8 *      http://www.apache.org/licenses/LICENSE-2.0\r
9 *\r
10 * Unless required by applicable law or agreed to in writing, software\r
11 * distributed under the License is distributed on an "AS IS" BASIS,\r
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
13 * See the License for the specific language governing permissions and\r
14 * limitations under the License.\r
15 */\r
16 using System;\r
17 using BarcodeFormat = com.google.zxing.BarcodeFormat;\r
18 using ReaderException = com.google.zxing.ReaderException;\r
19 using Result = com.google.zxing.Result;\r
20 using ResultPoint = com.google.zxing.ResultPoint;\r
21 using BitArray = com.google.zxing.common.BitArray;\r
22 namespace com.google.zxing.oned\r
23 {\r
24         \r
25         /// <summary> <p>Decodes Code 39 barcodes. This does not support "Full ASCII Code 39" yet.</p>\r
26         /// \r
27         /// </summary>\r
28         /// <author>  Sean Owen\r
29         /// </author>\r
30         /// <author>www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source \r
31         /// </author>\r
32         public sealed class Code39Reader:OneDReader\r
33         {\r
34                 \r
35                 private const System.String ALPHABET_STRING = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. *$/+%";\r
36                 //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
37                 private static readonly char[] ALPHABET = ALPHABET_STRING.ToCharArray();\r
38                 \r
39                 /// <summary> These represent the encodings of characters, as patterns of wide and narrow bars.\r
40                 /// The 9 least-significant bits of each int correspond to the pattern of wide and narrow,\r
41                 /// with 1s representing "wide" and 0s representing narrow.\r
42                 /// </summary>\r
43                 //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
44                 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
45                 \r
46                 //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
47                 private static readonly int ASTERISK_ENCODING = CHARACTER_ENCODINGS[39];\r
48                 \r
49                 //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
50                 private bool usingCheckDigit;\r
51                 //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
52                 private bool extendedMode;\r
53                 \r
54                 /// <summary> Creates a reader that assumes all encoded data is data, and does not treat the final\r
55                 /// character as a check digit. It will not decoded "extended Code 39" sequences.\r
56                 /// </summary>\r
57                 public Code39Reader()\r
58                 {\r
59                         usingCheckDigit = false;\r
60                         extendedMode = false;\r
61                 }\r
62                 \r
63                 /// <summary> Creates a reader that can be configured to check the last character as a check digit.\r
64                 /// It will not decoded "extended Code 39" sequences.\r
65                 /// \r
66                 /// </summary>\r
67                 /// <param name="usingCheckDigit">if true, treat the last data character as a check digit, not\r
68                 /// data, and verify that the checksum passes.\r
69                 /// </param>\r
70                 public Code39Reader(bool usingCheckDigit)\r
71                 {\r
72                         this.usingCheckDigit = usingCheckDigit;\r
73                         this.extendedMode = false;\r
74                 }\r
75                 \r
76                 /// <summary> Creates a reader that can be configured to check the last character as a check digit,\r
77                 /// or optionally attempt to decode "extended Code 39" sequences that are used to encode\r
78                 /// the full ASCII character set.\r
79                 /// \r
80                 /// </summary>\r
81                 /// <param name="usingCheckDigit">if true, treat the last data character as a check digit, not\r
82                 /// data, and verify that the checksum passes.\r
83                 /// </param>\r
84                 /// <param name="extendedMode">if true, will attempt to decode extended Code 39 sequences in the\r
85                 /// text.\r
86                 /// </param>\r
87                 public Code39Reader(bool usingCheckDigit, bool extendedMode)\r
88                 {\r
89                         this.usingCheckDigit = usingCheckDigit;\r
90                         this.extendedMode = extendedMode;\r
91                 }\r
92                 \r
93                 public override Result decodeRow(int rowNumber, BitArray row, System.Collections.Hashtable hints)\r
94                 {\r
95                         \r
96                         int[] start = findAsteriskPattern(row);\r
97                         int nextStart = start[1];\r
98                         int end = row.Size;\r
99                         \r
100                         // Read off white space\r
101                         while (nextStart < end && !row.get_Renamed(nextStart))\r
102                         {\r
103                                 nextStart++;\r
104                         }\r
105                         \r
106                         System.Text.StringBuilder result = new System.Text.StringBuilder(20);\r
107                         int[] counters = new int[9];\r
108                         char decodedChar;\r
109                         int lastStart;\r
110                         do \r
111                         {\r
112                                 recordPattern(row, nextStart, counters);\r
113                                 int pattern = toNarrowWidePattern(counters);\r
114                                 if (pattern < 0)\r
115                                 {\r
116                                         throw ReaderException.Instance;\r
117                                 }\r
118                                 decodedChar = patternToChar(pattern);\r
119                                 result.Append(decodedChar);\r
120                                 lastStart = nextStart;\r
121                                 for (int i = 0; i < counters.Length; i++)\r
122                                 {\r
123                                         nextStart += counters[i];\r
124                                 }\r
125                                 // Read off white space\r
126                                 while (nextStart < end && !row.get_Renamed(nextStart))\r
127                                 {\r
128                                         nextStart++;\r
129                                 }\r
130                         }\r
131                         while (decodedChar != '*');\r
132                         result.Remove(result.Length - 1, 1); // remove asterisk\r
133                         \r
134                         // Look for whitespace after pattern:\r
135                         int lastPatternSize = 0;\r
136                         for (int i = 0; i < counters.Length; i++)\r
137                         {\r
138                                 lastPatternSize += counters[i];\r
139                         }\r
140                         int whiteSpaceAfterEnd = nextStart - lastStart - lastPatternSize;\r
141                         // If 50% of last pattern size, following last pattern, is not whitespace, fail\r
142                         // (but if it's whitespace to the very end of the image, that's OK)\r
143                         if (nextStart != end && whiteSpaceAfterEnd / 2 < lastPatternSize)\r
144                         {\r
145                                 throw ReaderException.Instance;\r
146                         }\r
147                         \r
148                         if (usingCheckDigit)\r
149                         {\r
150                                 int max = result.Length - 1;\r
151                                 int total = 0;\r
152                                 for (int i = 0; i < max; i++)\r
153                                 {\r
154                                         total += ALPHABET_STRING.IndexOf((System.Char) result[i]);\r
155                                 }\r
156                                 if (total % 43 != ALPHABET_STRING.IndexOf((System.Char) result[max]))\r
157                                 {\r
158                                         throw ReaderException.Instance;\r
159                                 }\r
160                                 result.Remove(max, 1);\r
161                         }\r
162                         \r
163                         System.String resultString = result.ToString();\r
164                         if (extendedMode)\r
165                         {\r
166                                 resultString = decodeExtended(resultString);\r
167                         }\r
168                         \r
169                         if (resultString.Length == 0)\r
170                         {\r
171                                 // Almost surely a false positive\r
172                                 throw ReaderException.Instance;\r
173                         }\r
174                         \r
175                         //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
176                         float left = (float) (start[1] + start[0]) / 2.0f;\r
177                         //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
178                         float right = (float) (nextStart + lastStart) / 2.0f;\r
179                         //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
180                         return new Result(resultString, null, new ResultPoint[]{new ResultPoint(left, (float) rowNumber), new ResultPoint(right, (float) rowNumber)}, BarcodeFormat.CODE_39);\r
181                 }\r
182                 \r
183                 private static int[] findAsteriskPattern(BitArray row)\r
184                 {\r
185                         int width = row.Size;\r
186                         int rowOffset = 0;\r
187                         while (rowOffset < width)\r
188                         {\r
189                                 if (row.get_Renamed(rowOffset))\r
190                                 {\r
191                                         break;\r
192                                 }\r
193                                 rowOffset++;\r
194                         }\r
195                         \r
196                         int counterPosition = 0;\r
197                         int[] counters = new int[9];\r
198                         int patternStart = rowOffset;\r
199                         bool isWhite = false;\r
200                         int patternLength = counters.Length;\r
201                         \r
202                         for (int i = rowOffset; i < width; i++)\r
203                         {\r
204                                 bool pixel = row.get_Renamed(i);\r
205                                 if (pixel ^ isWhite)\r
206                                 {\r
207                                         counters[counterPosition]++;\r
208                                 }\r
209                                 else\r
210                                 {\r
211                                         if (counterPosition == patternLength - 1)\r
212                                         {\r
213                                                 if (toNarrowWidePattern(counters) == ASTERISK_ENCODING)\r
214                                                 {\r
215                                                         // Look for whitespace before start pattern, >= 50% of width of start pattern\r
216                                                         if (row.isRange(System.Math.Max(0, patternStart - (i - patternStart) / 2), patternStart, false))\r
217                                                         {\r
218                                                                 return new int[]{patternStart, i};\r
219                                                         }\r
220                                                 }\r
221                                                 patternStart += counters[0] + counters[1];\r
222                                                 for (int y = 2; y < patternLength; y++)\r
223                                                 {\r
224                                                         counters[y - 2] = counters[y];\r
225                                                 }\r
226                                                 counters[patternLength - 2] = 0;\r
227                                                 counters[patternLength - 1] = 0;\r
228                                                 counterPosition--;\r
229                                         }\r
230                                         else\r
231                                         {\r
232                                                 counterPosition++;\r
233                                         }\r
234                                         counters[counterPosition] = 1;\r
235                                         isWhite = !isWhite;\r
236                                 }\r
237                         }\r
238                         throw ReaderException.Instance;\r
239                 }\r
240                 \r
241                 // For efficiency, returns -1 on failure. Not throwing here saved as many as 700 exceptions\r
242                 // per image when using some of our blackbox images.\r
243                 private static int toNarrowWidePattern(int[] counters)\r
244                 {\r
245                         int numCounters = counters.Length;\r
246                         int maxNarrowCounter = 0;\r
247                         int wideCounters;\r
248                         do \r
249                         {\r
250                                 int minCounter = System.Int32.MaxValue;\r
251                                 for (int i = 0; i < numCounters; i++)\r
252                                 {\r
253                                         int counter = counters[i];\r
254                                         if (counter < minCounter && counter > maxNarrowCounter)\r
255                                         {\r
256                                                 minCounter = counter;\r
257                                         }\r
258                                 }\r
259                                 maxNarrowCounter = minCounter;\r
260                                 wideCounters = 0;\r
261                                 int totalWideCountersWidth = 0;\r
262                                 int pattern = 0;\r
263                                 for (int i = 0; i < numCounters; i++)\r
264                                 {\r
265                                         int counter = counters[i];\r
266                                         if (counters[i] > maxNarrowCounter)\r
267                                         {\r
268                                                 pattern |= 1 << (numCounters - 1 - i);\r
269                                                 wideCounters++;\r
270                                                 totalWideCountersWidth += counter;\r
271                                         }\r
272                                 }\r
273                                 if (wideCounters == 3)\r
274                                 {\r
275                                         // Found 3 wide counters, but are they close enough in width?\r
276                                         // We can perform a cheap, conservative check to see if any individual\r
277                                         // counter is more than 1.5 times the average:\r
278                                         for (int i = 0; i < numCounters && wideCounters > 0; i++)\r
279                                         {\r
280                                                 int counter = counters[i];\r
281                                                 if (counters[i] > maxNarrowCounter)\r
282                                                 {\r
283                                                         wideCounters--;\r
284                                                         // totalWideCountersWidth = 3 * average, so this checks if counter >= 3/2 * average\r
285                                                         if ((counter << 1) >= totalWideCountersWidth)\r
286                                                         {\r
287                                                                 return - 1;\r
288                                                         }\r
289                                                 }\r
290                                         }\r
291                                         return pattern;\r
292                                 }\r
293                         }\r
294                         while (wideCounters > 3);\r
295                         return - 1;\r
296                 }\r
297                 \r
298                 private static char patternToChar(int pattern)\r
299                 {\r
300                         for (int i = 0; i < CHARACTER_ENCODINGS.Length; i++)\r
301                         {\r
302                                 if (CHARACTER_ENCODINGS[i] == pattern)\r
303                                 {\r
304                                         return ALPHABET[i];\r
305                                 }\r
306                         }\r
307                         throw ReaderException.Instance;\r
308                 }\r
309                 \r
310                 private static System.String decodeExtended(System.String encoded)\r
311                 {\r
312                         int length = encoded.Length;\r
313                         System.Text.StringBuilder decoded = new System.Text.StringBuilder(length);\r
314                         for (int i = 0; i < length; i++)\r
315                         {\r
316                                 char c = encoded[i];\r
317                                 if (c == '+' || c == '$' || c == '%' || c == '/')\r
318                                 {\r
319                                         char next = encoded[i + 1];\r
320                                         char decodedChar = '\x0000';\r
321                                         switch (c)\r
322                                         {\r
323                                                 \r
324                                                 case '+': \r
325                                                         // +A to +Z map to a to z\r
326                                                         if (next >= 'A' && next <= 'Z')\r
327                                                         {\r
328                                                                 decodedChar = (char) (next + 32);\r
329                                                         }\r
330                                                         else\r
331                                                         {\r
332                                                                 throw ReaderException.Instance;\r
333                                                         }\r
334                                                         break;\r
335                                                 \r
336                                                 case '$': \r
337                                                         // $A to $Z map to control codes SH to SB\r
338                                                         if (next >= 'A' && next <= 'Z')\r
339                                                         {\r
340                                                                 decodedChar = (char) (next - 64);\r
341                                                         }\r
342                                                         else\r
343                                                         {\r
344                                                                 throw ReaderException.Instance;\r
345                                                         }\r
346                                                         break;\r
347                                                 \r
348                                                 case '%': \r
349                                                         // %A to %E map to control codes ESC to US\r
350                                                         if (next >= 'A' && next <= 'E')\r
351                                                         {\r
352                                                                 decodedChar = (char) (next - 38);\r
353                                                         }\r
354                                                         else if (next >= 'F' && next <= 'W')\r
355                                                         {\r
356                                                                 decodedChar = (char) (next - 11);\r
357                                                         }\r
358                                                         else\r
359                                                         {\r
360                                                                 throw ReaderException.Instance;\r
361                                                         }\r
362                                                         break;\r
363                                                 \r
364                                                 case '/': \r
365                                                         // /A to /O map to ! to , and /Z maps to :\r
366                                                         if (next >= 'A' && next <= 'O')\r
367                                                         {\r
368                                                                 decodedChar = (char) (next - 32);\r
369                                                         }\r
370                                                         else if (next == 'Z')\r
371                                                         {\r
372                                                                 decodedChar = ':';\r
373                                                         }\r
374                                                         else\r
375                                                         {\r
376                                                                 throw ReaderException.Instance;\r
377                                                         }\r
378                                                         break;\r
379                                                 }\r
380                                         decoded.Append(decodedChar);\r
381                                         // bump up i again since we read two characters\r
382                                         i++;\r
383                                 }\r
384                                 else\r
385                                 {\r
386                                         decoded.Append(c);\r
387                                 }\r
388                         }\r
389                         return decoded.ToString();\r
390                 }\r
391         }\r
392 }