/** * See ISO 16022:2007, 5.4.1 */ private static bool decodeECISegment(BitSource bits, ECIStringBuilder result) { if (bits.available() < 8) { return(false); } int c1 = bits.readBits(8); if (c1 <= 127) { return(result.AppendECI(c1 - 1)); } return(true); //currently we only support character set ECIs /*} else { * if (bits.available() < 8) { * throw FormatException.getFormatInstance(); * } * int c2 = bits.readBits(8); * if (c1 >= 128 && c1 <= 191) { * } else { * if (bits.available() < 8) { * throw FormatException.getFormatInstance(); * } * int c3 = bits.readBits(8); * } * }*/ }
/// <summary> /// See ISO 16022:2006, 5.2.9 and Annex B, B.2 /// </summary> private static bool decodeBase256Segment(BitSource bits, ECIStringBuilder result, IList <byte[]> byteSegments) { // Figure out how long the Base 256 Segment is. int codewordPosition = 1 + bits.ByteOffset; // position is 1-indexed int d1 = unrandomize255State(bits.readBits(8), codewordPosition++); int count; if (d1 == 0) { // Read the remainder of the symbol count = bits.available() / 8; } else if (d1 < 250) { count = d1; } else { count = 250 * (d1 - 249) + unrandomize255State(bits.readBits(8), codewordPosition++); } // We're seeing NegativeArraySizeException errors from users. if (count < 0) { return(false); } byte[] bytes = new byte[count]; for (int i = 0; i < count; i++) { // Have seen this particular error in the wild, such as at // http://www.bcgen.com/demo/IDAutomationStreamingDataMatrix.aspx?MODE=3&D=Fred&PFMT=3&PT=F&X=0.3&O=0&LM=0.2 if (bits.available() < 8) { return(false); } bytes[i] = (byte)unrandomize255State(bits.readBits(8), codewordPosition++); } byteSegments.Add(bytes); try { #if (NETFX_CORE || PORTABLE || NETSTANDARD1_0 || NETSTANDARD1_1 || NETSTANDARD1_2) result.Append(Encoding.GetEncoding(StringUtils.ISO88591).GetString(bytes, 0, bytes.Length)); #else result.Append(Encoding.GetEncoding(StringUtils.ISO88591).GetString(bytes)); #endif } catch (Exception uee) { throw new InvalidOperationException("Platform does not support required encoding: " + uee); } return(true); }
/// <summary> /// See ISO 16022:2006, 5.2.8 and Annex C Table C.3 /// </summary> private static bool decodeEdifactSegment(BitSource bits, ECIStringBuilder result) { do { // If there is only two or less bytes left then it will be encoded as ASCII if (bits.available() <= 16) { return(true); } for (int i = 0; i < 4; i++) { int edifactValue = bits.readBits(6); // Check for the unlatch character if (edifactValue == 0x1F) { // 011111 // Read rest of byte, which should be 0, and stop int bitsLeft = 8 - bits.BitOffset; if (bitsLeft != 8) { bits.readBits(bitsLeft); } return(true); } if ((edifactValue & 0x20) == 0) { // no 1 in the leading (6th) bit edifactValue |= 0x40; // Add a leading 01 to the 6 bit binary value } result.Append((char)edifactValue); } } while (bits.available() > 0); return(true); }
internal static DecoderResult decode(byte[] bytes) { BitSource bits = new BitSource(bytes); ECIStringBuilder result = new ECIStringBuilder(100); StringBuilder resultTrailer = new StringBuilder(0); List <byte[]> byteSegments = new List <byte[]>(1); Mode mode = Mode.ASCII_ENCODE; List <int> fnc1Positions = new List <int>(); // Would be replaceable by looking directly at 'bytes', if we're sure to not having to account for multi byte values. int symbologyModifier; bool isECIencoded = false; do { if (mode == Mode.ASCII_ENCODE) { if (!decodeAsciiSegment(bits, result, resultTrailer, fnc1Positions, out mode)) { return(null); } } else { switch (mode) { case Mode.C40_ENCODE: if (!decodeC40Segment(bits, result, fnc1Positions)) { return(null); } break; case Mode.TEXT_ENCODE: if (!decodeTextSegment(bits, result, fnc1Positions)) { return(null); } break; case Mode.ANSIX12_ENCODE: if (!decodeAnsiX12Segment(bits, result)) { return(null); } break; case Mode.EDIFACT_ENCODE: if (!decodeEdifactSegment(bits, result)) { return(null); } break; case Mode.BASE256_ENCODE: if (!decodeBase256Segment(bits, result, byteSegments)) { return(null); } break; case Mode.ECI_ENCODE: decodeECISegment(bits, result); isECIencoded = true; // ECI detection only, atm continue decoding as ASCII break; default: return(null); } mode = Mode.ASCII_ENCODE; } } while (mode != Mode.PAD_ENCODE && bits.available() > 0); if (resultTrailer.Length > 0) { result.Append(resultTrailer.ToString()); } if (isECIencoded) { // Examples for this numbers can be found in this documentation of a hardware barcode scanner: // https://honeywellaidc.force.com/supportppr/s/article/List-of-barcode-symbology-AIM-Identifiers if (fnc1Positions.Contains(0) || fnc1Positions.Contains(4)) { symbologyModifier = 5; } else if (fnc1Positions.Contains(1) || fnc1Positions.Contains(5)) { symbologyModifier = 6; } else { symbologyModifier = 4; } } else { if (fnc1Positions.Contains(0) || fnc1Positions.Contains(4)) { symbologyModifier = 2; } else if (fnc1Positions.Contains(1) || fnc1Positions.Contains(5)) { symbologyModifier = 3; } else { symbologyModifier = 1; } } return(new DecoderResult(bytes, result.ToString(), byteSegments.Count == 0 ? null : byteSegments, null, symbologyModifier)); }
/// <summary> /// See ISO 16022:2006, 5.2.7 /// </summary> private static bool decodeAnsiX12Segment(BitSource bits, ECIStringBuilder result) { // Three ANSI X12 values are encoded in a 16-bit value as // (1600 * C1) + (40 * C2) + C3 + 1 int[] cValues = new int[3]; do { // If there is only one byte left then it will be encoded as ASCII if (bits.available() == 8) { return(true); } int firstByte = bits.readBits(8); if (firstByte == 254) { // Unlatch codeword return(true); } parseTwoBytes(firstByte, bits.readBits(8), cValues); for (int i = 0; i < 3; i++) { int cValue = cValues[i]; switch (cValue) { case 0: // X12 segment terminator <CR> result.Append('\r'); break; case 1: // X12 segment separator * result.Append('*'); break; case 2: // X12 sub-element separator > result.Append('>'); break; case 3: // space result.Append(' '); break; default: if (cValue < 14) { // 0 - 9 result.Append((char)(cValue + 44)); } else if (cValue < 40) { // A - Z result.Append((char)(cValue + 51)); } else { return(false); } break; } } } while (bits.available() > 0); return(true); }
/// <summary> /// See ISO 16022:2006, 5.2.6 and Annex C, Table C.2 /// </summary> private static bool decodeTextSegment(BitSource bits, ECIStringBuilder result, List <int> fnc1positions) { // Three Text values are encoded in a 16-bit value as // (1600 * C1) + (40 * C2) + C3 + 1 // TODO(bbrown): The Upper Shift with Text doesn't work in the 4 value scenario all the time bool upperShift = false; int[] cValues = new int[3]; int shift = 0; do { // If there is only one byte left then it will be encoded as ASCII if (bits.available() == 8) { return(true); } int firstByte = bits.readBits(8); if (firstByte == 254) { // Unlatch codeword return(true); } parseTwoBytes(firstByte, bits.readBits(8), cValues); for (int i = 0; i < 3; i++) { int cValue = cValues[i]; switch (shift) { case 0: if (cValue < 3) { shift = cValue + 1; } else if (cValue < TEXT_BASIC_SET_CHARS.Length) { char textChar = TEXT_BASIC_SET_CHARS[cValue]; if (upperShift) { result.Append((char)(textChar + 128)); upperShift = false; } else { result.Append(textChar); } } else { return(false); } break; case 1: if (upperShift) { result.Append((char)(cValue + 128)); upperShift = false; } else { result.Append((char)cValue); } shift = 0; break; case 2: // Shift 2 for Text is the same encoding as C40 if (cValue < TEXT_SHIFT2_SET_CHARS.Length) { char textChar = TEXT_SHIFT2_SET_CHARS[cValue]; if (upperShift) { result.Append((char)(textChar + 128)); upperShift = false; } else { result.Append(textChar); } } else { switch (cValue) { case 27: // FNC1 fnc1positions.Add(result.Length); result.Append((char)29); // translate as ASCII 29 break; case 30: // Upper Shift upperShift = true; break; default: return(false); } } shift = 0; break; case 3: if (cValue < TEXT_SHIFT3_SET_CHARS.Length) { char textChar = TEXT_SHIFT3_SET_CHARS[cValue]; if (upperShift) { result.Append((char)(textChar + 128)); upperShift = false; } else { result.Append(textChar); } shift = 0; } else { return(false); } break; default: return(false); } } } while (bits.available() > 0); return(true); }
/// <summary> /// See ISO 16022:2006, 5.2.3 and Annex C, Table C.2 /// </summary> private static bool decodeAsciiSegment(BitSource bits, ECIStringBuilder result, StringBuilder resultTrailer, List <int> fnc1positions, out Mode mode) { bool upperShift = false; mode = Mode.ASCII_ENCODE; do { int oneByte = bits.readBits(8); if (oneByte == 0) { return(false); } else if (oneByte <= 128) { // ASCII data (ASCII value + 1) if (upperShift) { oneByte += 128; //upperShift = false; } result.Append((char)(oneByte - 1)); mode = Mode.ASCII_ENCODE; return(true); } else if (oneByte == 129) { // Pad mode = Mode.PAD_ENCODE; return(true); } else if (oneByte <= 229) { // 2-digit data 00-99 (Numeric Value + 130) int value = oneByte - 130; if (value < 10) { // pad with '0' for single digit values result.Append('0'); } result.Append(value); } else { switch (oneByte) { case 230: // Latch to C40 encodation mode = Mode.C40_ENCODE; return(true); case 231: // Latch to Base 256 encodation mode = Mode.BASE256_ENCODE; return(true); case 232: // FNC1 fnc1positions.Add(result.Length); result.Append((char)29); // translate as ASCII 29 break; case 233: // Structured Append case 234: // Reader Programming // Ignore these symbols for now //throw ReaderException.getInstance(); break; case 235: // Upper Shift (shift to Extended ASCII) upperShift = true; break; case 236: // 05 Macro result.Append("[)>\u001E05\u001D"); resultTrailer.Insert(0, "\u001E\u0004"); break; case 237: // 06 Macro result.Append("[)>\u001E06\u001D"); resultTrailer.Insert(0, "\u001E\u0004"); break; case 238: // Latch to ANSI X12 encodation mode = Mode.ANSIX12_ENCODE; return(true); case 239: // Latch to Text encodation mode = Mode.TEXT_ENCODE; return(true); case 240: // Latch to EDIFACT encodation mode = Mode.EDIFACT_ENCODE; return(true); case 241: // ECI Character mode = Mode.ECI_ENCODE; return(true); default: // Not to be used in ASCII encodation // but work around encoders that end with 254, latch back to ASCII if (oneByte != 254 || bits.available() != 0) { return(false); } break; } } } while (bits.available() > 0); mode = Mode.ASCII_ENCODE; return(true); }