diff --git a/src/common/eci_string_builder.rs b/src/common/eci_string_builder.rs index 547013e..924d434 100644 --- a/src/common/eci_string_builder.rs +++ b/src/common/eci_string_builder.rs @@ -166,6 +166,12 @@ impl ECIStringBuilder { pub fn is_empty(&self) -> bool { return self.current_bytes.is_empty() && self.result.is_empty(); } + + pub fn build_result(mut self) -> Self { + self.encodeCurrentBytesIfAny(); + + self + } } impl fmt::Display for ECIStringBuilder { diff --git a/src/datamatrix/decoder/DecodedBitStreamParserTestCase.java b/src/datamatrix/decoder/DecodedBitStreamParserTestCase.java deleted file mode 100644 index a0bbc58..0000000 --- a/src/datamatrix/decoder/DecodedBitStreamParserTestCase.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2008 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing.datamatrix.decoder; - -import org.junit.Assert; -import org.junit.Test; - -/** - * @author bbrown@google.com (Brian Brown) - */ -public final class DecodedBitStreamParserTestCase extends Assert { - - @Test - public void testAsciiStandardDecode() throws Exception { - // ASCII characters 0-127 are encoded as the value + 1 - byte[] bytes = {(byte) ('a' + 1), (byte) ('b' + 1), (byte) ('c' + 1), - (byte) ('A' + 1), (byte) ('B' + 1), (byte) ('C' + 1)}; - String decodedString = DecodedBitStreamParser.decode(bytes).getText(); - assertEquals("abcABC", decodedString); - } - - @Test - public void testAsciiDoubleDigitDecode() throws Exception { - // ASCII double digit (00 - 99) Numeric Value + 130 - byte[] bytes = {(byte) 130 , (byte) (1 + 130), - (byte) (98 + 130), (byte) (99 + 130)}; - String decodedString = DecodedBitStreamParser.decode(bytes).getText(); - assertEquals("00019899", decodedString); - } - - // TODO(bbrown): Add test cases for each encoding type - // TODO(bbrown): Add test cases for switching encoding types -} \ No newline at end of file diff --git a/src/datamatrix/decoder/bit_matrix_parser.rs b/src/datamatrix/decoder/bit_matrix_parser.rs index 9f88587..b0641b3 100644 --- a/src/datamatrix/decoder/bit_matrix_parser.rs +++ b/src/datamatrix/decoder/bit_matrix_parser.rs @@ -78,20 +78,20 @@ impl BitMatrixParser { * @return bytes encoded within the Data Matrix Code * @throws FormatException if the exact number of bytes expected is not read */ - pub fn readCodewords(&self) -> Result, Exceptions> { - let result = vec![0u8; self.version.getTotalCodewords() as usize]; - let resultOffset = 0; + pub fn readCodewords(&mut self) -> Result, Exceptions> { + let mut result = vec![0u8; self.version.getTotalCodewords() as usize]; + let mut resultOffset = 0; - let row = 4; - let column = 0_usize; + let mut row = 4; + let mut column = 0_usize; let numRows = self.mappingBitMatrix.getHeight() as usize; let numColumns = self.mappingBitMatrix.getWidth() as usize; - let corner1Read = false; - let corner2Read = false; - let corner3Read = false; - let corner4Read = false; + let mut corner1Read = false; + let mut corner2Read = false; + let mut corner3Read = false; + let mut corner4Read = false; // Read all of the codewords loop { @@ -193,7 +193,9 @@ impl BitMatrixParser { * @param numColumns Number of columns in the mapping matrix * @return value of the given bit in the mapping matrix */ - fn readModule(&self, row: usize, column: usize, numRows: usize, numColumns: usize) -> bool { + fn readModule(&mut self, row: usize, column: usize, numRows: usize, numColumns: usize) -> bool { + let mut row = row; + let mut column = column; // Adjust the row and column indices based on boundary wrapping if row < 0 { row += numRows; @@ -222,7 +224,7 @@ impl BitMatrixParser { * @param numColumns Number of columns in the mapping matrix * @return byte from the utah shape */ - fn readUtah(&self, row: usize, column: usize, numRows: usize, numColumns: usize) -> u32 { + fn readUtah(&mut self, row: usize, column: usize, numRows: usize, numColumns: usize) -> u32 { let mut currentByte = 0; if self.readModule(row - 2, column - 2, numRows, numColumns) { currentByte |= 1; @@ -267,7 +269,7 @@ impl BitMatrixParser { * @param numColumns Number of columns in the mapping matrix * @return byte from the Corner condition 1 */ - fn readCorner1(&self, numRows: usize, numColumns: usize) -> u32 { + fn readCorner1(&mut self, numRows: usize, numColumns: usize) -> u32 { let mut currentByte = 0; if self.readModule(numRows - 1, 0, numRows, numColumns) { currentByte |= 1; @@ -312,7 +314,7 @@ impl BitMatrixParser { * @param numColumns Number of columns in the mapping matrix * @return byte from the Corner condition 2 */ - fn readCorner2(&self, numRows: usize, numColumns: usize) -> u32 { + fn readCorner2(&mut self, numRows: usize, numColumns: usize) -> u32 { let mut currentByte = 0; if self.readModule(numRows - 3, 0, numRows, numColumns) { currentByte |= 1; @@ -357,7 +359,7 @@ impl BitMatrixParser { * @param numColumns Number of columns in the mapping matrix * @return byte from the Corner condition 3 */ - fn readCorner3(&self, numRows: usize, numColumns: usize) -> u32 { + fn readCorner3(&mut self, numRows: usize, numColumns: usize) -> u32 { let mut currentByte = 0; if self.readModule(numRows - 1, 0, numRows, numColumns) { currentByte |= 1; @@ -402,7 +404,7 @@ impl BitMatrixParser { * @param numColumns Number of columns in the mapping matrix * @return byte from the Corner condition 4 */ - fn readCorner4(&self, numRows: usize, numColumns: usize) -> u32 { + fn readCorner4(&mut self, numRows: usize, numColumns: usize) -> u32 { let mut currentByte = 0; if self.readModule(numRows - 3, 0, numRows, numColumns) { currentByte |= 1; @@ -467,7 +469,8 @@ impl BitMatrixParser { let sizeDataRegionRow = numDataRegionsRow * dataRegionSizeRows; let sizeDataRegionColumn = numDataRegionsColumn * dataRegionSizeColumns; - let bitMatrixWithoutAlignment = BitMatrix::new(sizeDataRegionColumn, sizeDataRegionRow)?; + let mut bitMatrixWithoutAlignment = + BitMatrix::new(sizeDataRegionColumn, sizeDataRegionRow)?; for dataRegionRow in 0..numDataRegionsRow { // for (int dataRegionRow = 0; dataRegionRow < numDataRegionsRow; ++dataRegionRow) { let dataRegionRowOffset = dataRegionRow * dataRegionSizeRows; diff --git a/src/datamatrix/decoder/decoded_bit_stream_parser.rs b/src/datamatrix/decoder/decoded_bit_stream_parser.rs index 0e67ac6..017903c 100644 --- a/src/datamatrix/decoder/decoded_bit_stream_parser.rs +++ b/src/datamatrix/decoder/decoded_bit_stream_parser.rs @@ -16,8 +16,10 @@ use encoding::Encoding; -use crate::{Exceptions, common::{BitSource, ECIStringBuilder, DecoderRXingResult}}; - +use crate::{ + common::{BitSource, DecoderRXingResult, ECIStringBuilder}, + Exceptions, +}; /** *

Data Matrix Codes can encode text as bits in one of several modes, and can use multiple modes @@ -29,8 +31,8 @@ use crate::{Exceptions, common::{BitSource, ECIStringBuilder, DecoderRXingResult * @author Sean Owen */ - #[derive(Debug,PartialEq, Eq,Clone, Copy)] - enum Mode { +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +enum Mode { PAD_ENCODE, // Not really a mode ASCII_ENCODE, C40_ENCODE, @@ -38,527 +40,636 @@ use crate::{Exceptions, common::{BitSource, ECIStringBuilder, DecoderRXingResult ANSIX12_ENCODE, EDIFACT_ENCODE, BASE256_ENCODE, - ECI_ENCODE - } + ECI_ENCODE, +} - /** - * See ISO 16022:2006, Annex C Table C.1 - * The C40 Basic Character Set (*'s used for placeholders for the shift values) - */ - const C40_BASIC_SET_CHARS : [char;40]= [ - '*', '*', '*', ' ', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', - 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' - ]; +/** + * See ISO 16022:2006, Annex C Table C.1 + * The C40 Basic Character Set (*'s used for placeholders for the shift values) + */ +const C40_BASIC_SET_CHARS: [char; 40] = [ + '*', '*', '*', ' ', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', + 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', +]; - const C40_SHIFT2_SET_CHARS :[char;27] = [ - '!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', - '/', ':', ';', '<', '=', '>', '?', '@', '[', '\\', ']', '^', '_' - ]; +const C40_SHIFT2_SET_CHARS: [char; 27] = [ + '!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/', ':', ';', '<', '=', + '>', '?', '@', '[', '\\', ']', '^', '_', +]; - /** - * See ISO 16022:2006, Annex C Table C.2 - * The Text Basic Character Set (*'s used for placeholders for the shift values) - */ - const TEXT_BASIC_SET_CHARS : [char;40]= - ['*', '*', '*', ' ', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', - 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' - ]; +/** + * See ISO 16022:2006, Annex C Table C.2 + * The Text Basic Character Set (*'s used for placeholders for the shift values) + */ +const TEXT_BASIC_SET_CHARS: [char; 40] = [ + '*', '*', '*', ' ', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', + 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', + 'y', 'z', +]; - // Shift 2 for Text is the same encoding as C40 - const TEXT_SHIFT2_SET_CHARS : [char;27]= C40_SHIFT2_SET_CHARS; +// Shift 2 for Text is the same encoding as C40 +const TEXT_SHIFT2_SET_CHARS: [char; 27] = C40_SHIFT2_SET_CHARS; - const TEXT_SHIFT3_SET_CHARS : [char;32]= [ - '`', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', - 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '{', '|', '}', '~', 127 as char - ]; +const TEXT_SHIFT3_SET_CHARS: [char; 32] = [ + '`', + 'A', + 'B', + 'C', + 'D', + 'E', + 'F', + 'G', + 'H', + 'I', + 'J', + 'K', + 'L', + 'M', + 'N', + 'O', + 'P', + 'Q', + 'R', + 'S', + 'T', + 'U', + 'V', + 'W', + 'X', + 'Y', + 'Z', + '{', + '|', + '}', + '~', + 127 as char, +]; - pub fn decode( bytes: &[u8]) -> Result { - let bits = BitSource::new(bytes); - let result = ECIStringBuilder::with_capacity(100); - let resultTrailer = String::new(); - let byteSegments = Vec::new();//new ArrayList<>(1); - let mode = Mode::ASCII_ENCODE; +pub fn decode(bytes: &[u8]) -> Result { + let mut bits = BitSource::new(bytes.to_vec()); + let mut result = ECIStringBuilder::with_capacity(100); + let mut resultTrailer = String::new(); + let mut byteSegments = Vec::new(); //new ArrayList<>(1); + let mut mode = Mode::ASCII_ENCODE; // Could look directly at 'bytes', if we're sure of not having to account for multi byte values - let fnc1Positions = Vec::new(); + let mut fnc1Positions = Vec::new(); let symbologyModifier; - let isECIencoded = false; + let mut isECIencoded = false; loop { - if mode == Mode::ASCII_ENCODE { - mode = decodeAsciiSegment(bits, result, resultTrailer, fnc1Positions); - } else { - match mode { - Mode::C40_ENCODE=> - decodeC40Segment(bits, result, fnc1Positions), - Mode::TEXT_ENCODE=> - decodeTextSegment(bits, result, fnc1Positions), - Mode::ANSIX12_ENCODE=> - decodeAnsiX12Segment(bits, result), - Mode::EDIFACT_ENCODE=> - decodeEdifactSegment(bits, result), - Mode::BASE256_ENCODE=> - decodeBase256Segment(bits, result, byteSegments), - Mode::ECI_ENCODE=>{ - decodeECISegment(bits, result); - isECIencoded = true; // ECI detection only, atm continue decoding as ASCII - }, - _=> - return Err(Exceptions::FormatException("".to_owned())), + if mode == Mode::ASCII_ENCODE { + mode = decodeAsciiSegment( + &mut bits, + &mut result, + &mut resultTrailer, + &mut fnc1Positions, + )?; + } else { + match mode { + Mode::C40_ENCODE => decodeC40Segment(&mut bits, &mut result, &mut fnc1Positions)?, + Mode::TEXT_ENCODE => decodeTextSegment(&mut bits, &mut result, &mut fnc1Positions)?, + Mode::ANSIX12_ENCODE => decodeAnsiX12Segment(&mut bits, &mut result)?, + Mode::EDIFACT_ENCODE => decodeEdifactSegment(&mut bits, &mut result)?, + Mode::BASE256_ENCODE => { + decodeBase256Segment(&mut bits, &mut result, &mut byteSegments)? + } + Mode::ECI_ENCODE => { + decodeECISegment(&mut bits, &mut result)?; + isECIencoded = true; // ECI detection only, atm continue decoding as ASCII + } + _ => return Err(Exceptions::FormatException("".to_owned())), + }; + mode = Mode::ASCII_ENCODE; } - mode = Mode::ASCII_ENCODE; - } - if ! (mode != Mode::PAD_ENCODE && bits.available() > 0) { break } - } //while (mode != Mode.PAD_ENCODE && bits.available() > 0); - if (resultTrailer.length() > 0) { - result.appendCharacters(resultTrailer); - } - 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 DecoderRXingResult(bytes, - result.toString(), - byteSegments.isEmpty() ? null : byteSegments, - null, - symbologyModifier); - } - - /** - * See ISO 16022:2006, 5.2.3 and Annex C, Table C.2 - */ - fn decodeAsciiSegment( bits:&BitSource, - result:&ECIStringBuilder, - resultTrailer:&String, - fnc1positions:&[usize]) -> Result { - boolean upperShift = false; - do { - int oneByte = bits.readBits(8); - if (oneByte == 0) { - throw FormatException.getFormatInstance(); - } else if (oneByte <= 128) { // ASCII data (ASCII value + 1) - if (upperShift) { - oneByte += 128; - //upperShift = false; - } - result.append((char) (oneByte - 1)); - return Mode.ASCII_ENCODE; - } else if (oneByte == 129) { // Pad - return Mode.PAD_ENCODE; - } 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 - return Mode.C40_ENCODE; - case 231: // Latch to Base 256 encodation - return Mode.BASE256_ENCODE; - case 232: // FNC1 - fnc1positions.add(result.length()); - result.append((char) 29); // translate as ASCII 29 + if !(mode != Mode::PAD_ENCODE && bits.available() > 0) { break; - case 233: // Structured Append - case 234: // Reader Programming + } + } //while (mode != Mode.PAD_ENCODE && bits.available() > 0); + if resultTrailer.len() > 0 { + result.appendCharacters(&resultTrailer); + } + 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; + } + } + + Ok(DecoderRXingResult::with_symbology( + bytes.to_vec(), + result.build_result().to_string(), + byteSegments, + String::new(), + symbologyModifier, + )) + + // return new DecoderRXingResult(bytes, + // result.toString(), + // byteSegments.isEmpty() ? null : byteSegments, + // null, + // symbologyModifier); +} + +/** + * See ISO 16022:2006, 5.2.3 and Annex C, Table C.2 + */ +fn decodeAsciiSegment( + bits: &mut BitSource, + result: &mut ECIStringBuilder, + resultTrailer: &mut String, + fnc1positions: &mut Vec, +) -> Result { + let mut upperShift = false; + loop { + let mut oneByte = bits.readBits(8)?; + if oneByte == 0 { + return Err(Exceptions::FormatException("".to_owned())); + } else if oneByte <= 128 { + // ASCII data (ASCII value + 1) + if upperShift { + oneByte += 128; + //upperShift = false; + } + result.append_char(char::from_u32(oneByte - 1).unwrap()); + return Ok(Mode::ASCII_ENCODE); + } else if oneByte == 129 { + // Pad + return Ok(Mode::PAD_ENCODE); + } else if oneByte <= 229 { + // 2-digit data 00-99 (Numeric Value + 130) + let value = oneByte - 130; + if value < 10 { + // pad with '0' for single digit values + result.append_char('0'); + } + //result.append_char(char::from_u32(value).unwrap()); + result.append_string(&format!("{}", value)); + } else { + match oneByte { + 230=> // Latch to C40 encodation + return Ok(Mode::C40_ENCODE), + 231=> // Latch to Base 256 encodation + return Ok(Mode::BASE256_ENCODE), + 232=> {// FNC1 + fnc1positions.push(result.len()); + result.append_char( 29 as char); // translate as ASCII 29 + }, + 233| // Structured Append + 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 - return Mode.ANSIX12_ENCODE; - case 239: // Latch to Text encodation - return Mode.TEXT_ENCODE; - case 240: // Latch to EDIFACT encodation - return Mode.EDIFACT_ENCODE; - case 241: // ECI Character - return Mode.ECI_ENCODE; - default: + {}, + 235=> // Upper Shift (shift to Extended ASCII) + upperShift = true, + + 236=> {// 05 Macro + result.append_string("[)>\u{001E}05\u{001D}"); + resultTrailer.replace_range(0..0, "\u{001E}\u{0004}"); + // resultTrailer.insert(0, "\u{001E}\u{0004}"); + }, + 237=>{ // 06 Macro + result.append_string("[)>\u{001E}06\u{001D}"); + resultTrailer.replace_range(0..0, "\u{001E}\u{0004}"); + // resultTrailer.insert(0, "\u{001E}\u{0004}"); + }, + 238=> // Latch to ANSI X12 encodation + return Ok(Mode::ANSIX12_ENCODE), + 239=> // Latch to Text encodation + return Ok(Mode::TEXT_ENCODE), + 240=> // Latch to EDIFACT encodation + return Ok(Mode::EDIFACT_ENCODE), + 241=> // ECI Character + return Ok(Mode::ECI_ENCODE), + _=>{ // 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) { - throw FormatException.getFormatInstance(); - } + if oneByte != 254 || bits.available() != 0 { + return Err(Exceptions::FormatException("".to_owned())) + }}, + } + } + if !(bits.available() > 0) { break; } - } - } while (bits.available() > 0); - return Mode.ASCII_ENCODE; - } + } //while (bits.available() > 0); + Ok(Mode::ASCII_ENCODE) +} - /** - * See ISO 16022:2006, 5.2.5 and Annex C, Table C.1 - */ - fn decodeC40Segment( bits:&BitSource, result:&ECIStringBuilder, fnc1positions:&[usize]) - -> Result<(),Exceptions> { +/** + * See ISO 16022:2006, 5.2.5 and Annex C, Table C.1 + */ +fn decodeC40Segment( + bits: &mut BitSource, + result: &mut ECIStringBuilder, + fnc1positions: &mut Vec, +) -> Result<(), Exceptions> { // Three C40 values are encoded in a 16-bit value as // (1600 * C1) + (40 * C2) + C3 + 1 // TODO(bbrown): The Upper Shift with C40 doesn't work in the 4 value scenario all the time - boolean upperShift = false; + let mut upperShift = false; - int[] cValues = new int[3]; - int shift = 0; + let mut cValues = [0; 3]; + let mut shift = 0; - do { - // If there is only one byte left then it will be encoded as ASCII - if (bits.available() == 8) { - return; - } - int firstByte = bits.readBits(8); - if (firstByte == 254) { // Unlatch codeword - return; - } - - 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 < C40_BASIC_SET_CHARS.length) { - char c40char = C40_BASIC_SET_CHARS[cValue]; - if (upperShift) { - result.append((char) (c40char + 128)); - upperShift = false; - } else { - result.append(c40char); - } - } else { - throw FormatException.getFormatInstance(); - } - break; - case 1: - if (upperShift) { - result.append((char) (cValue + 128)); - upperShift = false; - } else { - result.append((char) cValue); - } - shift = 0; - break; - case 2: - if (cValue < C40_SHIFT2_SET_CHARS.length) { - char c40char = C40_SHIFT2_SET_CHARS[cValue]; - if (upperShift) { - result.append((char) (c40char + 128)); - upperShift = false; - } else { - result.append(c40char); - } - } 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: - throw FormatException.getFormatInstance(); - } - } - shift = 0; - break; - case 3: - if (upperShift) { - result.append((char) (cValue + 224)); - upperShift = false; - } else { - result.append((char) (cValue + 96)); - } - shift = 0; - break; - default: - throw FormatException.getFormatInstance(); + loop { + // If there is only one byte left then it will be encoded as ASCII + if bits.available() == 8 { + return Ok(()); + } + let firstByte = bits.readBits(8)?; + if firstByte == 254 { + // Unlatch codeword + return Ok(()); } - } - } while (bits.available() > 0); - } - /** - * See ISO 16022:2006, 5.2.6 and Annex C, Table C.2 - */ - fn decodeTextSegment( bits:&BitSource, result:&ECIStringBuilder, fnc1positions:&[usize]) - -> Result<(),Exceptions> { + parseTwoBytes(firstByte, bits.readBits(8)?, &mut cValues); + + for i in 0..3 { + // for (int i = 0; i < 3; i++) { + let cValue = cValues[i]; + match shift { + 0 => { + if cValue < 3 { + shift = cValue + 1; + } else if cValue < C40_BASIC_SET_CHARS.len() as u32 { + let c40char = C40_BASIC_SET_CHARS[cValue as usize]; + if upperShift { + result.append_char(char::from_u32(c40char as u32 + 128).unwrap()); + upperShift = false; + } else { + result.append_char(c40char); + } + } else { + return Err(Exceptions::FormatException("".to_owned())); + } + } + 1 => { + if upperShift { + result.append_char(char::from_u32((cValue + 128) as u32).unwrap()); + upperShift = false; + } else { + result.append_char(char::from_u32(cValue as u32).unwrap()); + } + shift = 0; + } + 2 => { + if cValue < C40_SHIFT2_SET_CHARS.len() as u32 { + let c40char = C40_SHIFT2_SET_CHARS[cValue as usize]; + if upperShift { + result.append_char(char::from_u32(c40char as u32 + 128).unwrap()); + upperShift = false; + } else { + result.append_char(c40char); + } + } else { + match cValue { + 27 => { + // FNC1 + fnc1positions.push(result.len()); + result.append_char(29 as char); // translate as ASCII 29 + } + 30 => + // Upper Shift + { + upperShift = true + } + + _ => return Err(Exceptions::FormatException("".to_owned())), + } + } + shift = 0; + } + 3 => { + if upperShift { + result.append_char(char::from_u32(cValue as u32 + 224).unwrap()); + upperShift = false; + } else { + result.append_char(char::from_u32(cValue as u32 + 96).unwrap()); + } + shift = 0; + } + + _ => return Err(Exceptions::FormatException("".to_owned())), + } + } + if !(bits.available() > 0) { + break; + } + } //while (bits.available() > 0); + Ok(()) +} + +/** + * See ISO 16022:2006, 5.2.6 and Annex C, Table C.2 + */ +fn decodeTextSegment( + bits: &mut BitSource, + result: &mut ECIStringBuilder, + fnc1positions: &mut Vec, +) -> Result<(), Exceptions> { // 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 - let upperShift = false; + let mut upperShift = false; - let cValues = [0;3];//new int[3]; - let shift = 0; + let mut cValues = [0; 3]; //new int[3]; + let mut shift = 0; loop { - // If there is only one byte left then it will be encoded as ASCII - if bits.available() == 8 { - return Ok(()) - } - let firstByte = bits.readBits(8)?; - if firstByte == 254 { // Unlatch codeword - return Ok(()) - } - - parseTwoBytes(firstByte, bits.readBits(8)?, &cValues); - - for cValue in cValues { - // for (int i = 0; i < 3; i++) { - // int cValue = cValues[i]; - match shift { - 0=> - if cValue < 3 { - shift = cValue + 1; - } else if cValue < TEXT_BASIC_SET_CHARS.len() { - let textChar = TEXT_BASIC_SET_CHARS[cValue]; - if upperShift { - result.append_char( (textChar + 128)); - upperShift = false; - } else { - result.append(textChar); - } - } else { - return Err(Exceptions::FormatException("".to_owned())); - }, - 1=> - {if upperShift { - result.append_char( (cValue + 128)); - upperShift = false; - } else { - result.append_char( cValue); - } - shift = 0;}, - - 2=> - {// Shift 2 for Text is the same encoding as C40 - if cValue < TEXT_SHIFT2_SET_CHARS.len() { - let textChar = TEXT_SHIFT2_SET_CHARS[cValue]; - if upperShift { - result.append_char( (textChar + 128)); - upperShift = false; - } else { - result.append_char(textChar); - } - } else { - match cValue { - 27=>{ // FNC1 - fnc1positions.push(result.length()); - result.append_char( 29); // translate as ASCII 29 - }, - 30=> // Upper Shift - upperShift = true, - - _=> - return Err(Exceptions::FormatException("".to_owned())), - } - } - shift = 0;}, - 3=> - if cValue < TEXT_SHIFT3_SET_CHARS.len() { - let textChar = TEXT_SHIFT3_SET_CHARS[cValue]; - if upperShift { - result.append_char( (textChar + 128)); - upperShift = false; - } else { - result.append_char(textChar); - } - shift = 0; - } else { - return Err(Exceptions::FormatException("".to_owned())); - }, - - _=> - return Err(Exceptions::FormatException("".to_owned())), + // If there is only one byte left then it will be encoded as ASCII + if bits.available() == 8 { + return Ok(()); + } + let firstByte = bits.readBits(8)?; + if firstByte == 254 { + // Unlatch codeword + return Ok(()); + } + + parseTwoBytes(firstByte, bits.readBits(8)?, &mut cValues); + + for cValue in cValues { + // for (int i = 0; i < 3; i++) { + // int cValue = cValues[i]; + match shift { + 0 => { + if cValue < 3 { + shift = cValue + 1; + } else if cValue < TEXT_BASIC_SET_CHARS.len() as u32 { + let textChar = TEXT_BASIC_SET_CHARS[cValue as usize]; + if upperShift { + result + .append_char(char::from_u32(textChar as u32 + 128 as u32).unwrap()); + upperShift = false; + } else { + result.append_char(textChar); + } + } else { + return Err(Exceptions::FormatException("".to_owned())); + } + } + 1 => { + if upperShift { + result.append_char(char::from_u32(cValue + 128).unwrap()); + upperShift = false; + } else { + result.append_char(char::from_u32(cValue).unwrap()); + } + shift = 0; + } + + 2 => { + // Shift 2 for Text is the same encoding as C40 + if cValue < TEXT_SHIFT2_SET_CHARS.len() as u32 { + let textChar = TEXT_SHIFT2_SET_CHARS[cValue as usize]; + if upperShift { + result.append_char(char::from_u32(textChar as u32 + 128).unwrap()); + upperShift = false; + } else { + result.append_char(textChar); + } + } else { + match cValue { + 27 => { + // FNC1 + fnc1positions.push(result.len()); + result.append_char(29 as char); // translate as ASCII 29 + } + 30 => + // Upper Shift + { + upperShift = true + } + + _ => return Err(Exceptions::FormatException("".to_owned())), + } + } + shift = 0; + } + 3 => { + if cValue < TEXT_SHIFT3_SET_CHARS.len() as u32 { + let textChar = TEXT_SHIFT3_SET_CHARS[cValue as usize]; + if upperShift { + result.append_char(char::from_u32(textChar as u32 + 128).unwrap()); + upperShift = false; + } else { + result.append_char(textChar); + } + shift = 0; + } else { + return Err(Exceptions::FormatException("".to_owned())); + } + } + + _ => return Err(Exceptions::FormatException("".to_owned())), + } + } + if !(bits.available() > 0) { + break; } - } - if !(bits.available() > 0){break} } //while (bits.available() > 0); Ok(()) - } +} - /** - * See ISO 16022:2006, 5.2.7 - */ - fn decodeAnsiX12Segment( bits:&BitSource, - result:&ECIStringBuilder) -> Result<(),Exceptions> { +/** + * See ISO 16022:2006, 5.2.7 + */ +fn decodeAnsiX12Segment( + bits: &mut BitSource, + result: &mut ECIStringBuilder, +) -> Result<(), Exceptions> { // Three ANSI X12 values are encoded in a 16-bit value as // (1600 * C1) + (40 * C2) + C3 + 1 - let cValues = [0;3];//new int[3]; + let mut cValues = [0; 3]; //new int[3]; loop { - // If there is only one byte left then it will be encoded as ASCII - if bits.available() == 8 { - return Ok(()) - } - let firstByte = bits.readBits(8)?; - if firstByte == 254 { // Unlatch codeword - return Ok(()) - } - - parseTwoBytes(firstByte, bits.readBits(8)?, &cValues); - - for cValue in cValues { - // for (int i = 0; i < 3; i++) { - // int cValue = cValues[i]; - match cValue { - 0=> // X12 segment terminator - result.append_char('\r'), - - 1=> // X12 segment separator * - result.append_char('*'), - - 2=> // X12 sub-element separator > - result.append_char('>'), - - 3=> // space - result.append_char(' '), - - _=> - if cValue < 14 { // 0 - 9 - result.append_char( char::from_u32(cValue + 44).unwrap()); - } else if cValue < 40 { // A - Z - result.append_char( char::from_u32(cValue + 51).unwrap()); - } else { - return Err(Exceptions::FormatException("".to_owned())) - }, + // If there is only one byte left then it will be encoded as ASCII + if bits.available() == 8 { + return Ok(()); + } + let firstByte = bits.readBits(8)?; + if firstByte == 254 { + // Unlatch codeword + return Ok(()); + } + + parseTwoBytes(firstByte, bits.readBits(8)?, &mut cValues); + + for cValue in cValues { + // for (int i = 0; i < 3; i++) { + // int cValue = cValues[i]; + match cValue { + 0 => + // X12 segment terminator + { + result.append_char('\r') + } + + 1 => + // X12 segment separator * + { + result.append_char('*') + } + + 2 => + // X12 sub-element separator > + { + result.append_char('>') + } + + 3 => + // space + { + result.append_char(' ') + } + + _ => { + if cValue < 14 { + // 0 - 9 + result.append_char(char::from_u32(cValue + 44).unwrap()); + } else if cValue < 40 { + // A - Z + result.append_char(char::from_u32(cValue + 51).unwrap()); + } else { + return Err(Exceptions::FormatException("".to_owned())); + } + } + } + } + if !(bits.available() > 0) { + break; } - } - if ! (bits.available() > 0) { break } } //while (bits.available() > 0); Ok(()) - } +} - fn parseTwoBytes( firstByte:u32, secondByte:u32, result:&[u32]) { - let fullBitValue = (firstByte << 8) + secondByte - 1; - let temp = fullBitValue / 1600; +fn parseTwoBytes(firstByte: u32, secondByte: u32, result: &mut [u32]) { + let mut fullBitValue = (firstByte << 8) + secondByte - 1; + let mut temp = fullBitValue / 1600; result[0] = temp; fullBitValue -= temp * 1600; temp = fullBitValue / 40; result[1] = temp; result[2] = fullBitValue - temp * 40; - } +} - /** - * See ISO 16022:2006, 5.2.8 and Annex C Table C.3 - */ - fn decodeEdifactSegment( bits:&BitSource, result:&ECIStringBuilder) -> Result<(),Exceptions>{ +/** + * See ISO 16022:2006, 5.2.8 and Annex C Table C.3 + */ +fn decodeEdifactSegment( + bits: &mut BitSource, + result: &mut ECIStringBuilder, +) -> Result<(), Exceptions> { loop { - // If there is only two or less bytes left then it will be encoded as ASCII - if bits.available() <= 16 { - return Ok(()); - } - - for i in 0..4 { - // for (int i = 0; i < 4; i++) { - let edifactValue = bits.readBits(6)?; - - // Check for the unlatch character - if edifactValue == 0x1F { // 011111 - // Read rest of byte, which should be 0, and stop - let bitsLeft = 8 - bits.getBitOffset(); - if bitsLeft != 8 { - bits.readBits(bitsLeft); - } - return Ok(()); + // If there is only two or less bytes left then it will be encoded as ASCII + if bits.available() <= 16 { + return Ok(()); } - 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( char::from_u32(edifactValue).unwrap()); - } + for _i in 0..4 { + // for (int i = 0; i < 4; i++) { + let mut edifactValue = bits.readBits(6)?; - if ! (bits.available() > 0) { break } - } + // Check for the unlatch character + if edifactValue == 0x1F { + // 011111 + // Read rest of byte, which should be 0, and stop + let bitsLeft = 8 - bits.getBitOffset(); + if bitsLeft != 8 { + bits.readBits(bitsLeft); + } + return Ok(()); + } + + 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(char::from_u32(edifactValue).unwrap()); + } + + if !(bits.available() > 0) { + break; + } + } Ok(()) - } +} - /** - * See ISO 16022:2006, 5.2.9 and Annex B, B.2 - */ - fn decodeBase256Segment( bits:&BitSource, - result:&ECIStringBuilder, - byteSegments:&Vec>) - -> Result<(),Exceptions> { +/** + * See ISO 16022:2006, 5.2.9 and Annex B, B.2 + */ +fn decodeBase256Segment( + bits: &mut BitSource, + result: &mut ECIStringBuilder, + byteSegments: &mut Vec>, +) -> Result<(), Exceptions> { // Figure out how long the Base 256 Segment is. - let codewordPosition = 1 + bits.getByteOffset(); // position is 1-indexed + let mut codewordPosition = 1 + bits.getByteOffset(); // position is 1-indexed let d1 = unrandomize255State(bits.readBits(8)?, codewordPosition); - codewordPosition +=1; + codewordPosition += 1; let count; - if d1 == 0 { // Read the remainder of the symbol - count = bits.available() as u32 / 8; + if d1 == 0 { + // Read the remainder of the symbol + count = bits.available() as u32 / 8; } else if d1 < 250 { - count = d1; + count = d1; } else { - count = 250 * (d1 - 249) + unrandomize255State(bits.readBits(8)?, codewordPosition); - codewordPosition +=1; + count = 250 * (d1 - 249) + unrandomize255State(bits.readBits(8)?, codewordPosition); + codewordPosition += 1; } // We're seeing NegativeArraySizeException errors from users. - if (count < 0) { - return Err(Exceptions::FormatException("".to_owned())) + if count < 0 { + return Err(Exceptions::FormatException("".to_owned())); } - let bytes = vec![0u8;count as usize]; + let mut bytes = vec![0u8; count as usize]; for i in 0..count as usize { - // 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 Err(Exceptions::FormatException("".to_owned())) - } - bytes[i] = unrandomize255State(bits.readBits(8)?, codewordPosition) as u8; - codewordPosition+=1; + // 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 Err(Exceptions::FormatException("".to_owned())); + } + bytes[i] = unrandomize255State(bits.readBits(8)?, codewordPosition) as u8; + codewordPosition += 1; } + result.append_string( + &encoding::all::ISO_8859_1 + .decode(&bytes, encoding::DecoderTrap::Strict) + .expect("decode"), + ); byteSegments.push(bytes); - result.append_string(&encoding::all::ISO_8859_1.decode(&bytes, encoding::DecoderTrap::Strict).expect("decode")); - - Ok(()) - } + // result.append_string(&encoding::all::ISO_8859_1.decode(&bytes, encoding::DecoderTrap::Strict).expect("decode")); - /** - * See ISO 16022:2007, 5.4.1 - */ - fn decodeECISegment( bits:&BitSource, - result:&ECIStringBuilder) - -> Result<(),Exceptions> { + Ok(()) +} + +/** + * See ISO 16022:2007, 5.4.1 + */ +fn decodeECISegment(bits: &mut BitSource, result: &mut ECIStringBuilder) -> Result<(), Exceptions> { if bits.available() < 8 { - return Err(Exceptions::FormatException("".to_owned())) + return Err(Exceptions::FormatException("".to_owned())); } let c1 = bits.readBits(8)?; if c1 <= 127 { - result.appendECI(c1 - 1)?; + result.appendECI(c1 - 1)?; } Ok(()) @@ -576,17 +687,57 @@ use crate::{Exceptions, common::{BitSource, ECIStringBuilder, DecoderRXingResult int c3 = bits.readBits(8); } }*/ - } +} - - /** - * See ISO 16022:2006, Annex B, B.2 - */ - fn unrandomize255State( randomizedBase256Codeword:u32, - base256CodewordPosition:usize) -> u32{ +/** + * See ISO 16022:2006, Annex B, B.2 + */ +fn unrandomize255State(randomizedBase256Codeword: u32, base256CodewordPosition: usize) -> u32 { let pseudoRandomNumber = ((149 * base256CodewordPosition as u32) % 255) + 1; let tempVariable = randomizedBase256Codeword - pseudoRandomNumber; - - if tempVariable >= 0 {tempVariable} else {tempVariable + 256} - } + if tempVariable >= 0 { + tempVariable + } else { + tempVariable + 256 + } +} + +#[cfg(test)] +mod tests { + use crate::datamatrix::decoder::decoded_bit_stream_parser; + + #[test] + fn testAsciiStandardDecode() { + // ASCII characters 0-127 are encoded as the value + 1 + let bytes = [ + (b'a' + 1), + (b'b' + 1), + (b'c' + 1), + (b'A' + 1), + (b'B' + 1), + (b'C' + 1), + ]; + let decodedString = String::from( + decoded_bit_stream_parser::decode(&bytes) + .expect("decode") + .getText(), + ); + assert_eq!("abcABC", decodedString); + } + + #[test] + fn testAsciiDoubleDigitDecode() { + // ASCII double digit (00 - 99) Numeric Value + 130 + let bytes = [130, (1 + 130), (98 + 130), (99 + 130)]; + let decodedString = String::from( + decoded_bit_stream_parser::decode(&bytes) + .expect("decode") + .getText(), + ); + assert_eq!("00019899", decodedString); + } + + // TODO(bbrown): Add test cases for each encoding type + // TODO(bbrown): Add test cases for switching encoding types +} diff --git a/src/datamatrix/decoder/decoder.rs b/src/datamatrix/decoder/decoder.rs index bb3be0e..8856aec 100644 --- a/src/datamatrix/decoder/decoder.rs +++ b/src/datamatrix/decoder/decoder.rs @@ -14,10 +14,15 @@ * limitations under the License. */ -use crate::{common::{reedsolomon::{ReedSolomonDecoder, get_predefined_genericgf, PredefinedGenericGF}, DecoderRXingResult, BitMatrix}, Exceptions}; - -use super::{DataBlock, BitMatrixParser, decoded_bit_stream_parser}; +use crate::{ + common::{ + reedsolomon::{get_predefined_genericgf, PredefinedGenericGF, ReedSolomonDecoder}, + BitMatrix, DecoderRXingResult, + }, + Exceptions, +}; +use super::{decoded_bit_stream_parser, BitMatrixParser, DataBlock}; /** *

The main class which implements Data Matrix Code decoding -- as opposed to locating and extracting @@ -28,104 +33,110 @@ use super::{DataBlock, BitMatrixParser, decoded_bit_stream_parser}; pub struct Decoder(ReedSolomonDecoder); impl Decoder { - - pub fn new() -> Self { - - Self(ReedSolomonDecoder::new(get_predefined_genericgf(PredefinedGenericGF::DataMatrixField256))) - // rsDecoder = new ReedSolomonDecoder(GenericGF.DATA_MATRIX_FIELD_256); - } - - /** - *

Convenience method that can decode a Data Matrix Code represented as a 2D array of booleans. - * "true" is taken to mean a black module.

- * - * @param image booleans representing white/black Data Matrix Code modules - * @return text and bytes encoded within the Data Matrix Code - * @throws FormatException if the Data Matrix Code cannot be decoded - * @throws ChecksumException if error correction fails - */ - pub fn decode_bools(&self, image:&Vec>) -> Result { - self.decode(&BitMatrix::parse_bools(&image)) - } - - /** - *

Decodes a Data Matrix Code represented as a {@link BitMatrix}. A 1 or "true" is taken - * to mean a black module.

- * - * @param bits booleans representing white/black Data Matrix Code modules - * @return text and bytes encoded within the Data Matrix Code - * @throws FormatException if the Data Matrix Code cannot be decoded - * @throws ChecksumException if error correction fails - */ - pub fn decode(&self, bits:&BitMatrix) -> Result { - - // Construct a parser and read version, error-correction level - let parser = BitMatrixParser::new(bits)?; - let version = parser.getVersion(); - - - // Read codewords - let codewords = parser.readCodewords()?; - // Separate into data blocks - let dataBlocks = DataBlock::getDataBlocks(&codewords, version)?; - - // Count total number of data bytes - let totalBytes = 0; - for db in dataBlocks { - totalBytes += db.getNumDataCodewords(); - } - let resultBytes = vec![0u8;totalBytes as usize]; - - let dataBlocksCount = dataBlocks.len(); - // Error-correct and copy data blocks together into a stream of bytes - for j in 0..dataBlocksCount { - // for (int j = 0; j < dataBlocksCount; j++) { - let dataBlock = dataBlocks[j]; - let codewordBytes = dataBlock.getCodewords(); - let numDataCodewords = dataBlock.getNumDataCodewords() as usize; - self.correctErrors(codewordBytes, numDataCodewords as u32); - for i in 0..numDataCodewords { - // for (int i = 0; i < numDataCodewords; i++) { - // De-interlace data blocks. - resultBytes[i * dataBlocksCount + j] = codewordBytes[i]; - } + pub fn new() -> Self { + Self(ReedSolomonDecoder::new(get_predefined_genericgf( + PredefinedGenericGF::DataMatrixField256, + ))) + // rsDecoder = new ReedSolomonDecoder(GenericGF.DATA_MATRIX_FIELD_256); } - // Decode the contents of that stream of bytes - decoded_bit_stream_parser::decode(&resultBytes) - } - - /** - *

Given data and error-correction codewords received, possibly corrupted by errors, attempts to - * correct the errors in-place using Reed-Solomon error correction.

- * - * @param codewordBytes data and error correction codewords - * @param numDataCodewords number of codewords that are data bytes - * @throws ChecksumException if error correction fails - */ - fn correctErrors(&self, codewordBytes:&[u8], numDataCodewords:u32) -> Result<(),Exceptions> { - let numCodewords = codewordBytes.len(); - // First read into an array of ints - // let codewordsInts = vec![0i32;numCodewords]; - // for i in 0..numCodewords { - // // for (int i = 0; i < numCodewords; i++) { - // codewordsInts[i] = codewordBytes[i]; - // } - let codewordsInts : Vec = codewordBytes.iter().map(|x| *x as i32).collect(); - - //try { - self.0.decode(&mut codewordsInts, codewordBytes.len() as i32- numDataCodewords as i32)?; - //} catch (ReedSolomonException ignored) { - //throw ChecksumException.getChecksumInstance(); - //} - // Copy back into array of bytes -- only need to worry about the bytes that were data - // We don't care about errors in the error-correction codewords - for i in 0..numDataCodewords as usize { - // for (int i = 0; i < numDataCodewords; i++) { - codewordBytes[i] = codewordsInts[i] as u8; + /** + *

Convenience method that can decode a Data Matrix Code represented as a 2D array of booleans. + * "true" is taken to mean a black module.

+ * + * @param image booleans representing white/black Data Matrix Code modules + * @return text and bytes encoded within the Data Matrix Code + * @throws FormatException if the Data Matrix Code cannot be decoded + * @throws ChecksumException if error correction fails + */ + pub fn decode_bools(&self, image: &Vec>) -> Result { + self.decode(&BitMatrix::parse_bools(&image)) } - // codewordsInts.into_iter().take(numDataCodewords as usize).map(|x| x as u8).collect::>() - Ok(()) - } + /** + *

Decodes a Data Matrix Code represented as a {@link BitMatrix}. A 1 or "true" is taken + * to mean a black module.

+ * + * @param bits booleans representing white/black Data Matrix Code modules + * @return text and bytes encoded within the Data Matrix Code + * @throws FormatException if the Data Matrix Code cannot be decoded + * @throws ChecksumException if error correction fails + */ + pub fn decode(&self, bits: &BitMatrix) -> Result { + // Construct a parser and read version, error-correction level + let mut parser = BitMatrixParser::new(bits)?; + + // Read codewords + let codewords = parser.readCodewords()?; + + let version = parser.getVersion(); + + // Separate into data blocks + let dataBlocks = DataBlock::getDataBlocks(&codewords, &version)?; + + // Count total number of data bytes + let mut totalBytes = 0; + for db in &dataBlocks { + totalBytes += db.getNumDataCodewords(); + } + let mut resultBytes = vec![0u8; totalBytes as usize]; + + let dataBlocksCount = dataBlocks.len(); + // Error-correct and copy data blocks together into a stream of bytes + for j in 0..dataBlocksCount { + // for (int j = 0; j < dataBlocksCount; j++) { + let dataBlock = &dataBlocks[j]; + let mut codewordBytes = dataBlock.getCodewords().to_vec(); + let numDataCodewords = dataBlock.getNumDataCodewords() as usize; + self.correctErrors(&mut codewordBytes, numDataCodewords as u32)?; + for i in 0..numDataCodewords { + // for (int i = 0; i < numDataCodewords; i++) { + // De-interlace data blocks. + resultBytes[i * dataBlocksCount + j] = codewordBytes[i]; + } + } + + // Decode the contents of that stream of bytes + decoded_bit_stream_parser::decode(&resultBytes) + } + + /** + *

Given data and error-correction codewords received, possibly corrupted by errors, attempts to + * correct the errors in-place using Reed-Solomon error correction.

+ * + * @param codewordBytes data and error correction codewords + * @param numDataCodewords number of codewords that are data bytes + * @throws ChecksumException if error correction fails + */ + fn correctErrors( + &self, + codewordBytes: &mut [u8], + numDataCodewords: u32, + ) -> Result<(), Exceptions> { + let _numCodewords = codewordBytes.len(); + // First read into an array of ints + // let codewordsInts = vec![0i32;numCodewords]; + // for i in 0..numCodewords { + // // for (int i = 0; i < numCodewords; i++) { + // codewordsInts[i] = codewordBytes[i]; + // } + let mut codewordsInts: Vec = codewordBytes.iter().map(|x| *x as i32).collect(); + + //try { + self.0.decode( + &mut codewordsInts, + codewordBytes.len() as i32 - numDataCodewords as i32, + )?; + //} catch (ReedSolomonException ignored) { + //throw ChecksumException.getChecksumInstance(); + //} + // Copy back into array of bytes -- only need to worry about the bytes that were data + // We don't care about errors in the error-correction codewords + for i in 0..numDataCodewords as usize { + // for (int i = 0; i < numDataCodewords; i++) { + codewordBytes[i] = codewordsInts[i] as u8; + } + // codewordsInts.into_iter().take(numDataCodewords as usize).map(|x| x as u8).collect::>() + Ok(()) + } } diff --git a/src/datamatrix/decoder/mod.rs b/src/datamatrix/decoder/mod.rs index 4cb3261..de1da0d 100644 --- a/src/datamatrix/decoder/mod.rs +++ b/src/datamatrix/decoder/mod.rs @@ -1,12 +1,11 @@ - -mod version; +mod bit_matrix_parser; mod data_block; mod decoder; -mod bit_matrix_parser; +mod version; -pub use version::*; +pub use bit_matrix_parser::*; pub use data_block::*; pub use decoder::*; -pub use bit_matrix_parser::*; +pub use version::*; -pub mod decoded_bit_stream_parser; \ No newline at end of file +pub mod decoded_bit_stream_parser; diff --git a/src/datamatrix/encoder/edifact_encoder.rs b/src/datamatrix/encoder/edifact_encoder.rs index 37d8b94..d49a070 100644 --- a/src/datamatrix/encoder/edifact_encoder.rs +++ b/src/datamatrix/encoder/edifact_encoder.rs @@ -132,7 +132,7 @@ impl EdifactEncoder { } else if c >= '@' && c <= '^' { sb.push((c as u8 - 64) as char); } else { - high_level_encoder::illegalCharacter(c); + high_level_encoder::illegalCharacter(c).expect(""); } }