2 * Copyright 2009 ZXing authors
\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
8 * http://www.apache.org/licenses/LICENSE-2.0
\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
17 using ReaderException = com.google.zxing.ReaderException;
\r
18 using DecoderResult = com.google.zxing.common.DecoderResult;
\r
19 namespace com.google.zxing.pdf417.decoder
\r
22 /// <summary> <p>This class contains the methods for decoding the PDF417 codewords.</p>
\r
25 /// <author> SITA Lab (kevin.osullivan@sita.aero)
\r
27 /// <author>www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source
\r
29 sealed class DecodedBitStreamParser
\r
32 private const int TEXT_COMPACTION_MODE_LATCH = 900;
\r
33 private const int BYTE_COMPACTION_MODE_LATCH = 901;
\r
34 private const int NUMERIC_COMPACTION_MODE_LATCH = 902;
\r
35 private const int BYTE_COMPACTION_MODE_LATCH_6 = 924;
\r
36 private const int BEGIN_MACRO_PDF417_CONTROL_BLOCK = 928;
\r
37 private const int BEGIN_MACRO_PDF417_OPTIONAL_FIELD = 923;
\r
38 private const int MACRO_PDF417_TERMINATOR = 922;
\r
39 private const int MODE_SHIFT_TO_BYTE_COMPACTION_MODE = 913;
\r
40 private const int MAX_NUMERIC_CODEWORDS = 15;
\r
42 private const int ALPHA = 0;
\r
43 private const int LOWER = 1;
\r
44 private const int MIXED = 2;
\r
45 private const int PUNCT = 3;
\r
46 private const int PUNCT_SHIFT = 4;
\r
48 private const int PL = 25;
\r
49 private const int LL = 27;
\r
50 private const int AS = 27;
\r
51 private const int ML = 28;
\r
52 private const int AL = 28;
\r
53 private const int PS = 29;
\r
54 private const int PAL = 29;
\r
56 //UPGRADE_NOTE: Final was removed from the declaration of 'PUNCT_CHARS'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
\r
57 private static readonly char[] PUNCT_CHARS = new char[]{';', '<', '>', '@', '[', (char) (92), '}', '_', (char) (96), '~', '!', (char) (13), (char) (9), ',', ':', (char) (10), '-', '.', '$', '/', (char) (34), '|', '*', '(', ')', '?', '{', '}', (char) (39)};
\r
59 //UPGRADE_NOTE: Final was removed from the declaration of 'MIXED_CHARS'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
\r
60 private static readonly char[] MIXED_CHARS = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '&', (char) (13), (char) (9), ',', ':', '#', '-', '.', '$', '/', '+', '%', '*', '=', '^'};
\r
62 // Table containing values for the exponent of 900.
\r
63 // This is used in the numeric compaction decode algorithm.
\r
64 //UPGRADE_NOTE: Final was removed from the declaration of 'EXP900'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
\r
65 private static readonly System.String[] EXP900 = new System.String[]{"000000000000000000000000000000000000000000001", "000000000000000000000000000000000000000000900", "000000000000000000000000000000000000000810000", "000000000000000000000000000000000000729000000", "000000000000000000000000000000000656100000000", "000000000000000000000000000000590490000000000", "000000000000000000000000000531441000000000000", "000000000000000000000000478296900000000000000", "000000000000000000000430467210000000000000000", "000000000000000000387420489000000000000000000", "000000000000000348678440100000000000000000000", "000000000000313810596090000000000000000000000", "000000000282429536481000000000000000000000000", "000000254186582832900000000000000000000000000", "000228767924549610000000000000000000000000000", "205891132094649000000000000000000000000000000"};
\r
67 private DecodedBitStreamParser()
\r
71 internal static DecoderResult decode(int[] codewords)
\r
73 System.Text.StringBuilder result = new System.Text.StringBuilder(100);
\r
74 // Get compaction mode
\r
76 int code = codewords[codeIndex++];
\r
77 while (codeIndex < codewords[0])
\r
82 case TEXT_COMPACTION_MODE_LATCH: {
\r
83 codeIndex = textCompaction(codewords, codeIndex, result);
\r
87 case BYTE_COMPACTION_MODE_LATCH: {
\r
88 codeIndex = byteCompaction(code, codewords, codeIndex, result);
\r
92 case NUMERIC_COMPACTION_MODE_LATCH: {
\r
93 codeIndex = numericCompaction(codewords, codeIndex, result);
\r
97 case MODE_SHIFT_TO_BYTE_COMPACTION_MODE: {
\r
98 codeIndex = byteCompaction(code, codewords, codeIndex, result);
\r
102 case BYTE_COMPACTION_MODE_LATCH_6: {
\r
103 codeIndex = byteCompaction(code, codewords, codeIndex, result);
\r
108 // Default to text compaction. During testing numerous barcodes
\r
109 // appeared to be missing the starting mode. In these cases defaulting
\r
110 // to text compaction seems to work.
\r
112 codeIndex = textCompaction(codewords, codeIndex, result);
\r
117 if (codeIndex < codewords.Length)
\r
119 code = codewords[codeIndex++];
\r
123 throw ReaderException.Instance;
\r
126 return new DecoderResult(null, result.ToString(), null, null);
\r
129 /// <summary> Text Compaction mode (see 5.4.1.5) permits all printable ASCII characters to be
\r
130 /// encoded, i.e. values 32 - 126 inclusive in accordance with ISO/IEC 646 (IRV), as
\r
131 /// well as selected control characters.
\r
134 /// <param name="codewords">The array of codewords (data + error)
\r
136 /// <param name="codeIndex">The current index into the codeword array.
\r
138 /// <param name="result"> The decoded data is appended to the result.
\r
140 /// <returns> The next index into the codeword array.
\r
142 private static int textCompaction(int[] codewords, int codeIndex, System.Text.StringBuilder result)
\r
144 // 2 character per codeword
\r
145 int[] textCompactionData = new int[codewords[0] << 1];
\r
146 // Used to hold the byte compaction value if there is a mode shift
\r
147 int[] byteCompactionData = new int[codewords[0] << 1];
\r
151 while ((codeIndex < codewords[0]) && !end)
\r
153 int code = codewords[codeIndex++];
\r
154 if (code < TEXT_COMPACTION_MODE_LATCH)
\r
156 textCompactionData[index] = code / 30;
\r
157 textCompactionData[index + 1] = code % 30;
\r
165 case TEXT_COMPACTION_MODE_LATCH: {
\r
171 case BYTE_COMPACTION_MODE_LATCH: {
\r
177 case NUMERIC_COMPACTION_MODE_LATCH: {
\r
183 case MODE_SHIFT_TO_BYTE_COMPACTION_MODE: {
\r
184 // The Mode Shift codeword 913 shall cause a temporary
\r
185 // switch from Text Compaction mode to Byte Compaction mode.
\r
186 // This switch shall be in effect for only the next codeword,
\r
187 // after which the mode shall revert to the prevailing sub-mode
\r
188 // of the Text Compaction mode. Codeword 913 is only available
\r
189 // in Text Compaction mode; its use is described in 5.4.2.4.
\r
190 textCompactionData[index] = MODE_SHIFT_TO_BYTE_COMPACTION_MODE;
\r
191 byteCompactionData[index] = code; //Integer.toHexString(code);
\r
196 case BYTE_COMPACTION_MODE_LATCH_6: {
\r
204 decodeTextCompaction(textCompactionData, byteCompactionData, index, result);
\r
208 /// <summary> The Text Compaction mode includes all the printable ASCII characters
\r
209 /// (i.e. values from 32 to 126) and three ASCII control characters: HT or tab
\r
210 /// (ASCII value 9), LF or line feed (ASCII value 10), and CR or carriage
\r
211 /// return (ASCII value 13). The Text Compaction mode also includes various latch
\r
212 /// and shift characters which are used exclusively within the mode. The Text
\r
213 /// Compaction mode encodes up to 2 characters per codeword. The compaction rules
\r
214 /// for converting data into PDF417 codewords are defined in 5.4.2.2. The sub-mode
\r
215 /// switches are defined in 5.4.2.3.
\r
218 /// <param name="textCompactionData">The text compaction data.
\r
220 /// <param name="byteCompactionData">The byte compaction data if there
\r
221 /// was a mode shift.
\r
223 /// <param name="length"> The size of the text compaction and byte compaction data.
\r
225 /// <param name="result"> The decoded data is appended to the result.
\r
227 private static void decodeTextCompaction(int[] textCompactionData, int[] byteCompactionData, int length, System.Text.StringBuilder result)
\r
229 // Beginning from an initial state of the Alpha sub-mode
\r
230 // The default compaction mode for PDF417 in effect at the start of each symbol shall always be Text
\r
231 // Compaction mode Alpha sub-mode (uppercase alphabetic). A latch codeword from another mode to the Text
\r
232 // Compaction mode shall always switch to the Text Compaction Alpha sub-mode.
\r
233 int subMode = ALPHA;
\r
234 int priorToShiftMode = ALPHA;
\r
238 int subModeCh = textCompactionData[i];
\r
239 char ch = (char) (0);
\r
244 // Alpha (uppercase alphabetic)
\r
245 if (subModeCh < 26)
\r
247 // Upper case Alpha Character
\r
248 ch = (char) ('A' + subModeCh);
\r
252 if (subModeCh == 26)
\r
256 else if (subModeCh == LL)
\r
260 else if (subModeCh == ML)
\r
264 else if (subModeCh == PS)
\r
266 // Shift to punctuation
\r
267 priorToShiftMode = subMode;
\r
268 subMode = PUNCT_SHIFT;
\r
270 else if (subModeCh == MODE_SHIFT_TO_BYTE_COMPACTION_MODE)
\r
272 result.Append((char) byteCompactionData[i]);
\r
279 // Lower (lowercase alphabetic)
\r
280 if (subModeCh < 26)
\r
282 ch = (char) ('a' + subModeCh);
\r
286 if (subModeCh == 26)
\r
290 else if (subModeCh == AL)
\r
294 else if (subModeCh == ML)
\r
298 else if (subModeCh == PS)
\r
300 // Shift to punctuation
\r
301 priorToShiftMode = subMode;
\r
302 subMode = PUNCT_SHIFT;
\r
304 else if (subModeCh == MODE_SHIFT_TO_BYTE_COMPACTION_MODE)
\r
306 result.Append((char) byteCompactionData[i]);
\r
313 // Mixed (numeric and some punctuation)
\r
314 if (subModeCh < PL)
\r
316 ch = MIXED_CHARS[subModeCh];
\r
320 if (subModeCh == PL)
\r
324 else if (subModeCh == 26)
\r
328 else if (subModeCh == AS)
\r
330 //mode_change = true;
\r
332 else if (subModeCh == AL)
\r
336 else if (subModeCh == PS)
\r
338 // Shift to punctuation
\r
339 priorToShiftMode = subMode;
\r
340 subMode = PUNCT_SHIFT;
\r
342 else if (subModeCh == MODE_SHIFT_TO_BYTE_COMPACTION_MODE)
\r
344 result.Append((char) byteCompactionData[i]);
\r
352 if (subModeCh < PS)
\r
354 ch = PUNCT_CHARS[subModeCh];
\r
358 if (subModeCh == PAL)
\r
362 else if (subModeCh == MODE_SHIFT_TO_BYTE_COMPACTION_MODE)
\r
364 result.Append((char) byteCompactionData[i]);
\r
371 // Restore sub-mode
\r
372 subMode = priorToShiftMode;
\r
373 if (subModeCh < PS)
\r
375 ch = PUNCT_CHARS[subModeCh];
\r
379 if (subModeCh == PAL)
\r
388 // Append decoded character to result
\r
395 /// <summary> Byte Compaction mode (see 5.4.3) permits all 256 possible 8-bit byte values to be encoded.
\r
396 /// This includes all ASCII characters value 0 to 127 inclusive and provides for international
\r
397 /// character set support.
\r
400 /// <param name="mode"> The byte compaction mode i.e. 901 or 924
\r
402 /// <param name="codewords">The array of codewords (data + error)
\r
404 /// <param name="codeIndex">The current index into the codeword array.
\r
406 /// <param name="result"> The decoded data is appended to the result.
\r
408 /// <returns> The next index into the codeword array.
\r
410 private static int byteCompaction(int mode, int[] codewords, int codeIndex, System.Text.StringBuilder result)
\r
412 if (mode == BYTE_COMPACTION_MODE_LATCH)
\r
414 // Total number of Byte Compaction characters to be encoded
\r
415 // is not a multiple of 6
\r
417 long value_Renamed = 0;
\r
418 char[] decodedData = new char[6];
\r
419 int[] byteCompactedCodewords = new int[6];
\r
421 while ((codeIndex < codewords[0]) && !end)
\r
423 int code = codewords[codeIndex++];
\r
424 if (code < TEXT_COMPACTION_MODE_LATCH)
\r
426 byteCompactedCodewords[count] = code;
\r
429 value_Renamed *= 900;
\r
430 value_Renamed += code;
\r
434 if ((code == TEXT_COMPACTION_MODE_LATCH) || (code == BYTE_COMPACTION_MODE_LATCH) || (code == NUMERIC_COMPACTION_MODE_LATCH) || (code == BYTE_COMPACTION_MODE_LATCH_6) || (code == BEGIN_MACRO_PDF417_CONTROL_BLOCK) || (code == BEGIN_MACRO_PDF417_OPTIONAL_FIELD) || (code == MACRO_PDF417_TERMINATOR))
\r
440 if ((count % 5 == 0) && (count > 0))
\r
442 // Decode every 5 codewords
\r
443 // Convert to Base 256
\r
444 for (int j = 0; j < 6; ++j)
\r
446 decodedData[5 - j] = (char) (value_Renamed % 256);
\r
447 value_Renamed >>= 8;
\r
449 result.Append(decodedData);
\r
453 // If Byte Compaction mode is invoked with codeword 901,
\r
454 // the final group of codewords is interpreted directly
\r
455 // as one byte per codeword, without compaction.
\r
456 for (int i = ((count / 5) * 5); i < count; i++)
\r
458 result.Append((char) byteCompactedCodewords[i]);
\r
461 else if (mode == BYTE_COMPACTION_MODE_LATCH_6)
\r
463 // Total number of Byte Compaction characters to be encoded
\r
464 // is an integer multiple of 6
\r
466 long value_Renamed = 0;
\r
468 while ((codeIndex < codewords[0]) && !end)
\r
470 int code = codewords[codeIndex++];
\r
471 if (code < TEXT_COMPACTION_MODE_LATCH)
\r
475 value_Renamed *= 900;
\r
476 value_Renamed += code;
\r
480 if ((code == TEXT_COMPACTION_MODE_LATCH) || (code == BYTE_COMPACTION_MODE_LATCH) || (code == NUMERIC_COMPACTION_MODE_LATCH) || (code == BYTE_COMPACTION_MODE_LATCH_6) || (code == BEGIN_MACRO_PDF417_CONTROL_BLOCK) || (code == BEGIN_MACRO_PDF417_OPTIONAL_FIELD) || (code == MACRO_PDF417_TERMINATOR))
\r
486 if ((count % 5 == 0) && (count > 0))
\r
488 // Decode every 5 codewords
\r
489 // Convert to Base 256
\r
490 char[] decodedData = new char[6];
\r
491 for (int j = 0; j < 6; ++j)
\r
493 decodedData[5 - j] = (char) (value_Renamed % 256);
\r
494 value_Renamed >>= 8;
\r
496 result.Append(decodedData);
\r
503 /// <summary> Numeric Compaction mode (see 5.4.4) permits efficient encoding of numeric data strings.
\r
506 /// <param name="codewords">The array of codewords (data + error)
\r
508 /// <param name="codeIndex">The current index into the codeword array.
\r
510 /// <param name="result"> The decoded data is appended to the result.
\r
512 /// <returns> The next index into the codeword array.
\r
514 private static int numericCompaction(int[] codewords, int codeIndex, System.Text.StringBuilder result)
\r
519 int[] numericCodewords = new int[MAX_NUMERIC_CODEWORDS];
\r
521 while ((codeIndex < codewords.Length) && !end)
\r
523 int code = codewords[codeIndex++];
\r
524 if (code < TEXT_COMPACTION_MODE_LATCH)
\r
526 numericCodewords[count] = code;
\r
531 if ((code == TEXT_COMPACTION_MODE_LATCH) || (code == BYTE_COMPACTION_MODE_LATCH) || (code == BYTE_COMPACTION_MODE_LATCH_6) || (code == BEGIN_MACRO_PDF417_CONTROL_BLOCK) || (code == BEGIN_MACRO_PDF417_OPTIONAL_FIELD) || (code == MACRO_PDF417_TERMINATOR))
\r
537 if ((count % MAX_NUMERIC_CODEWORDS) == 0 || code == NUMERIC_COMPACTION_MODE_LATCH)
\r
539 // Re-invoking Numeric Compaction mode (by using codeword 902
\r
540 // while in Numeric Compaction mode) serves to terminate the
\r
541 // current Numeric Compaction mode grouping as described in 5.4.4.2,
\r
542 // and then to start a new one grouping.
\r
543 System.String s = decodeBase900toBase10(numericCodewords, count);
\r
551 /// <summary> Convert a list of Numeric Compacted codewords from Base 900 to Base 10.
\r
554 /// <param name="codewords">The array of codewords
\r
556 /// <param name="count"> The number of codewords
\r
558 /// <returns> The decoded string representing the Numeric data.
\r
562 Encode the fifteen digit numeric string 000213298174000
\r
563 Prefix the numeric string with a 1 and set the initial value of
\r
564 t = 1 000 213 298 174 000
\r
565 Calculate codeword 0
\r
566 d0 = 1 000 213 298 174 000 mod 900 = 200
\r
568 t = 1 000 213 298 174 000 div 900 = 1 111 348 109 082
\r
569 Calculate codeword 1
\r
570 d1 = 1 111 348 109 082 mod 900 = 282
\r
572 t = 1 111 348 109 082 div 900 = 1 234 831 232
\r
573 Calculate codeword 2
\r
574 d2 = 1 234 831 232 mod 900 = 632
\r
576 t = 1 234 831 232 div 900 = 1 372 034
\r
577 Calculate codeword 3
\r
578 d3 = 1 372 034 mod 900 = 434
\r
580 t = 1 372 034 div 900 = 1 524
\r
581 Calculate codeword 4
\r
582 d4 = 1 524 mod 900 = 624
\r
584 t = 1 524 div 900 = 1
\r
585 Calculate codeword 5
\r
588 Codeword sequence is: 1, 624, 434, 632, 282, 200
\r
590 Decode the above codewords involves
\r
591 1 x 900 power of 5 + 624 x 900 power of 4 + 434 x 900 power of 3 +
\r
592 632 x 900 power of 2 + 282 x 900 power of 1 + 200 x 900 power of 0 = 1000213298174000
\r
594 Remove leading 1 => Result is 000213298174000
\r
596 As there are huge numbers involved here we must use fake out the maths using string
\r
597 tokens for the numbers.
\r
598 BigDecimal is not supported by J2ME.
\r
600 private static System.String decodeBase900toBase10(int[] codewords, int count)
\r
602 System.Text.StringBuilder accum = null;
\r
603 for (int i = 0; i < count; i++)
\r
605 System.Text.StringBuilder value_Renamed = multiply(EXP900[count - i - 1], codewords[i]);
\r
608 // First time in accum=0
\r
609 accum = value_Renamed;
\r
613 accum = add(accum.ToString(), value_Renamed.ToString());
\r
616 System.String result = null;
\r
617 // Remove leading '1' which was inserted to preserve
\r
619 for (int i = 0; i < accum.Length; i++)
\r
621 if (accum[i] == '1')
\r
623 //result = accum.substring(i + 1);
\r
624 result = accum.ToString().Substring(i + 1);
\r
628 if (result == null)
\r
630 // No leading 1 => just write the converted number.
\r
631 result = accum.ToString();
\r
636 /// <summary> Multiplies two String numbers
\r
639 /// <param name="value1">Any number represented as a string.
\r
641 /// <param name="value2">A number <= 999.
\r
643 /// <returns> the result of value1 * value2.
\r
645 private static System.Text.StringBuilder multiply(System.String value1, int value2)
\r
647 System.Text.StringBuilder result = new System.Text.StringBuilder(value1.Length);
\r
648 for (int i = 0; i < value1.Length; i++)
\r
650 // Put zeros into the result.
\r
651 result.Append('0');
\r
653 int hundreds = value2 / 100;
\r
654 int tens = (value2 / 10) % 10;
\r
655 int ones = value2 % 10;
\r
656 // Multiply by ones
\r
657 for (int j = 0; j < ones; j++)
\r
659 result = add(result.ToString(), value1);
\r
661 // Multiply by tens
\r
662 for (int j = 0; j < tens; j++)
\r
664 result = add(result.ToString(), (value1 + '0').Substring(1));
\r
666 // Multiply by hundreds
\r
667 for (int j = 0; j < hundreds; j++)
\r
669 result = add(result.ToString(), (value1 + "00").Substring(2));
\r
674 /// <summary> Add two numbers which are represented as strings.
\r
677 /// <param name="value1">
\r
679 /// <param name="value2">
\r
681 /// <returns> the result of value1 + value2
\r
683 private static System.Text.StringBuilder add(System.String value1, System.String value2)
\r
685 System.Text.StringBuilder temp1 = new System.Text.StringBuilder(5);
\r
686 System.Text.StringBuilder temp2 = new System.Text.StringBuilder(5);
\r
687 System.Text.StringBuilder result = new System.Text.StringBuilder(value1.Length);
\r
688 for (int i = 0; i < value1.Length; i++)
\r
690 // Put zeros into the result.
\r
691 result.Append('0');
\r
694 for (int i = value1.Length - 3; i > - 1; i -= 3)
\r
698 temp1.Append(value1[i]);
\r
699 temp1.Append(value1[i + 1]);
\r
700 temp1.Append(value1[i + 2]);
\r
703 temp2.Append(value2[i]);
\r
704 temp2.Append(value2[i + 1]);
\r
705 temp2.Append(value2[i + 2]);
\r
707 int intValue1 = System.Int32.Parse(temp1.ToString());
\r
708 int intValue2 = System.Int32.Parse(temp2.ToString());
\r
710 int sumval = (intValue1 + intValue2 + carry) % 1000;
\r
711 carry = (intValue1 + intValue2 + carry) / 1000;
\r
713 result[i + 2] = (char) ((sumval % 10) + '0');
\r
714 result[i + 1] = (char) (((sumval / 10) % 10) + '0');
\r
715 result[i] = (char) ((sumval / 100) + '0');
\r
721 private static String decodeBase900toBase10(int codewords[], int count) {
\r
722 BigInteger accum = BigInteger.valueOf(0);
\r
723 BigInteger value = null;
\r
724 for (int i = 0; i < count; i++) {
\r
725 value = BigInteger.valueOf(900).pow(count - i - 1);
\r
726 value = value.multiply(BigInteger.valueOf(codewords[i]));
\r
727 accum = accum.add(value);
\r
729 if (debug) System.out.println("Big Integer " + accum);
\r
730 String result = accum.toString().substring(1);
\r