diff --git a/src/datamatrix/encoder/minimal_encoder.rs b/src/datamatrix/encoder/minimal_encoder.rs index 3cdf885..33d0fa5 100755 --- a/src/datamatrix/encoder/minimal_encoder.rs +++ b/src/datamatrix/encoder/minimal_encoder.rs @@ -14,15 +14,18 @@ * limitations under the License. */ -use std::{rc::Rc, fmt}; +use std::{fmt, rc::Rc}; -use encoding::{self,EncodingRef}; +use encoding::{self, EncodingRef}; -use crate::{common::{MinimalECIInput, ECIInput}, Exceptions}; +use crate::{ + common::{ECIInput, MinimalECIInput}, + Exceptions, +}; -use super::{SymbolShapeHint, high_level_encoder}; +use super::{high_level_encoder, SymbolShapeHint}; -const ISO_8859_1_ENCODER : EncodingRef = encoding::all::ISO_8859_1; +const ISO_8859_1_ENCODER: EncodingRef = encoding::all::ISO_8859_1; /** * Encoder that encodes minimally @@ -31,7 +34,7 @@ const ISO_8859_1_ENCODER : EncodingRef = encoding::all::ISO_8859_1; * * Uses Dijkstra to produce mathematically minimal encodings that are in some cases smaller than the results produced * by the algorithm described in annex S in the specification ISO/IEC 16022:200(E). The biggest improvment of this - * algorithm over that one is the case when the algorithm enters the most inefficient mode, the B256 Mode:: The + * algorithm over that one is the case when the algorithm enters the most inefficient mode, the B256 Mode:: The * algorithm from the specification algorithm will exit this mode only if it encounters digits so that arbitrarily * inefficient results can be produced if the postfix contains no digits. * @@ -46,7 +49,7 @@ const ISO_8859_1_ENCODER : EncodingRef = encoding::all::ISO_8859_1; * contains many * characters from ISO-8859-2 (Latin 2) and few from ISO-8859-3 (Latin 3)). * In a second stage this stream of ECIs and bytes is minimally encoded using the various Data Matrix encoding modes. * While both stages encode mathematically minimally it is not ensured that the result is mathematically minimal since - * the size growth for inserting an ECI in the first stage can only be approximated as the first stage does not know + * the size growth for inserting an ECI in the first stage can only be approximated as the first stage does not know * in which mode the ECI will occur in the second stage (may, or may not require an extra latch to ASCII depending on * the current mode). The reason for this shortcoming are difficulties in implementing it in a straightforward and * readable manner. @@ -60,235 +63,354 @@ const ISO_8859_1_ENCODER : EncodingRef = encoding::all::ISO_8859_1; * @author Alex Geller */ - #[derive(Debug,Copy,Clone,PartialEq, Eq)] - enum Mode { +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +enum Mode { ASCII, C40, TEXT, X12, EDF, - B256 - } + B256, +} - impl Mode { +impl Mode { pub fn ordinal(&self) -> usize { - match self { - Mode::ASCII => 0, - Mode::C40 => 1, - Mode::TEXT => 2, - Mode::X12 => 3, - Mode::EDF => 4, - Mode::B256 => 5, + match self { + Mode::ASCII => 0, + Mode::C40 => 1, + Mode::TEXT => 2, + Mode::X12 => 3, + Mode::EDF => 4, + Mode::B256 => 5, + } } - } - } +} - const C40_SHIFT2_CHARS :[char;27] = ['!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/', - ':', ';', '<', '=', '>', '?', '@', '[', '\\', ']', '^', '_' ]; +const C40_SHIFT2_CHARS: [char; 27] = [ + '!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/', ':', ';', '<', '=', + '>', '?', '@', '[', '\\', ']', '^', '_', +]; - - pub fn isExtendedASCII( ch:char, fnc1:Option) -> bool{ +pub fn isExtendedASCII(ch: char, fnc1: Option) -> bool { let is_fnc1 = if let Some(fnc1) = fnc1 { - ch != fnc1 - }else { - true + ch != fnc1 + } else { + true }; is_fnc1 && ch as u8 >= 128 && ch as u8 <= 255 // return ch != fnc1 && ch as u8 >= 128 && ch as u8 <= 255; - } +} - fn isInC40Shift1Set( ch:char) -> bool{ - ch as u8 <= 31 - } +fn isInC40Shift1Set(ch: char) -> bool { + ch as u8 <= 31 +} - fn isInC40Shift2Set( ch:char, fnc1:Option) -> bool{ +fn isInC40Shift2Set(ch: char, fnc1: Option) -> bool { for c40Shift2Char in C40_SHIFT2_CHARS { - // for (char c40Shift2Char : C40_SHIFT2_CHARS) { - if c40Shift2Char == ch { - return true; - } + // for (char c40Shift2Char : C40_SHIFT2_CHARS) { + if c40Shift2Char == ch { + return true; + } } if let Some(fnc1) = fnc1 { - ch == fnc1 - }else { - false + ch == fnc1 + } else { + false } // return ch as u8 as i32 == fnc1; - } +} - fn isInTextShift1Set( ch:char) -> bool{ - isInC40Shift1Set(ch) - } +fn isInTextShift1Set(ch: char) -> bool { + isInC40Shift1Set(ch) +} - fn isInTextShift2Set( ch:char, fnc1:Option) -> bool{ - isInC40Shift2Set(ch, fnc1) - } +fn isInTextShift2Set(ch: char, fnc1: Option) -> bool { + isInC40Shift2Set(ch, fnc1) +} - /** - * Performs message encoding of a DataMatrix message - * - * @param msg the message - * @return the encoded message (the char values range from 0 to 255) - */ - pub fn encodeHighLevel( msg:&str) -> Result{ - encodeHighLevelWithDetails(msg, None, None, SymbolShapeHint::FORCE_NONE) - } +/** + * Performs message encoding of a DataMatrix message + * + * @param msg the message + * @return the encoded message (the char values range from 0 to 255) + */ +pub fn encodeHighLevel(msg: &str) -> Result { + encodeHighLevelWithDetails(msg, None, None, SymbolShapeHint::FORCE_NONE) +} - /** - * Performs message encoding of a DataMatrix message - * - * @param msg the message - * @param priorityCharset The preferred {@link Charset}. When the value of the argument is null, the algorithm - * chooses charsets that leads to a minimal representation. Otherwise the algorithm will use the priority - * charset to encode any character in the input that can be encoded by it if the charset is among the - * supported charsets. - * @param fnc1 denotes the character in the input that represents the FNC1 character or -1 if this is not a GS1 - * bar code. If the value is not -1 then a FNC1 is also prepended. - * @param shape requested shape. - * @return the encoded message (the char values range from 0 to 255) - */ - pub fn encodeHighLevelWithDetails( msg:&str, priorityCharset:Option, fnc1:Option, shape:SymbolShapeHint)->Result { +/** + * Performs message encoding of a DataMatrix message + * + * @param msg the message + * @param priorityCharset The preferred {@link Charset}. When the value of the argument is null, the algorithm + * chooses charsets that leads to a minimal representation. Otherwise the algorithm will use the priority + * charset to encode any character in the input that can be encoded by it if the charset is among the + * supported charsets. + * @param fnc1 denotes the character in the input that represents the FNC1 character or -1 if this is not a GS1 + * bar code. If the value is not -1 then a FNC1 is also prepended. + * @param shape requested shape. + * @return the encoded message (the char values range from 0 to 255) + */ +pub fn encodeHighLevelWithDetails( + msg: &str, + priorityCharset: Option, + fnc1: Option, + shape: SymbolShapeHint, +) -> Result { let mut msg = msg; - let macroId = 0; - if msg.starts_with(high_level_encoder::MACRO_05_HEADER) && msg.ends_with(high_level_encoder::MACRO_TRAILER) { - macroId = 5; - // msg = msg.substring(high_level_encoder::MACRO_05_HEADER.len(), msg.len() - 2); - msg = &msg[high_level_encoder::MACRO_05_HEADER.len()..(msg.len() - 2)]; - } else if msg.starts_with(high_level_encoder::MACRO_06_HEADER) && msg.ends_with(high_level_encoder::MACRO_TRAILER) { - macroId = 6; - // msg = msg.substring(high_level_encoder::MACRO_06_HEADER.len(), msg.len() - 2); - msg = &msg[high_level_encoder::MACRO_06_HEADER.len()..(msg.len() - 2)]; + let mut macroId = 0; + if msg.starts_with(high_level_encoder::MACRO_05_HEADER) + && msg.ends_with(high_level_encoder::MACRO_TRAILER) + { + macroId = 5; + // msg = msg.substring(high_level_encoder::MACRO_05_HEADER.len(), msg.len() - 2); + msg = &msg[high_level_encoder::MACRO_05_HEADER.len()..(msg.len() - 2)]; + } else if msg.starts_with(high_level_encoder::MACRO_06_HEADER) + && msg.ends_with(high_level_encoder::MACRO_TRAILER) + { + macroId = 6; + // msg = msg.substring(high_level_encoder::MACRO_06_HEADER.len(), msg.len() - 2); + msg = &msg[high_level_encoder::MACRO_06_HEADER.len()..(msg.len() - 2)]; } - Ok(ISO_8859_1_ENCODER.decode(&encode(msg, priorityCharset, fnc1, shape, macroId)?, encoding::DecoderTrap::Strict).expect("should decode").to_owned()) + Ok(ISO_8859_1_ENCODER + .decode( + &encode(msg, priorityCharset, fnc1, shape, macroId)?, + encoding::DecoderTrap::Strict, + ) + .expect("should decode") + .to_owned()) // return new String(encode(msg, priorityCharset, fnc1, shape, macroId), StandardCharsets.ISO_8859_1); - } +} - /** - * Encodes input minimally and returns an array of the codewords - * - * @param input The string to encode - * @param priorityCharset The preferred {@link Charset}. When the value of the argument is null, the algorithm - * chooses charsets that leads to a minimal representation. Otherwise the algorithm will use the priority - * charset to encode any character in the input that can be encoded by it if the charset is among the - * supported charsets. - * @param fnc1 denotes the character in the input that represents the FNC1 character or -1 if this is not a GS1 - * bar code. If the value is not -1 then a FNC1 is also prepended. - * @param shape requested shape. - * @param macroId Prepends the specified macro function in case that a value of 5 or 6 is specified. - * @return An array of bytes representing the codewords of a minimal encoding. - */ - fn encode( input:&str, priorityCharset:Option, fnc1:Option, shape:SymbolShapeHint, macroId:i32) -> Result,Exceptions> { - Ok(encodeMinimally( &Input::new(input, priorityCharset, fnc1, shape, macroId))?.getBytes().to_vec()) - } +/** + * Encodes input minimally and returns an array of the codewords + * + * @param input The string to encode + * @param priorityCharset The preferred {@link Charset}. When the value of the argument is null, the algorithm + * chooses charsets that leads to a minimal representation. Otherwise the algorithm will use the priority + * charset to encode any character in the input that can be encoded by it if the charset is among the + * supported charsets. + * @param fnc1 denotes the character in the input that represents the FNC1 character or -1 if this is not a GS1 + * bar code. If the value is not -1 then a FNC1 is also prepended. + * @param shape requested shape. + * @param macroId Prepends the specified macro function in case that a value of 5 or 6 is specified. + * @return An array of bytes representing the codewords of a minimal encoding. + */ +fn encode( + input: &str, + priorityCharset: Option, + fnc1: Option, + shape: SymbolShapeHint, + macroId: i32, +) -> Result, Exceptions> { + Ok(encodeMinimally(Rc::new(Input::new( + input, + priorityCharset, + fnc1, + shape, + macroId, + )))? + .getBytes() + .to_vec()) +} - fn addEdge( edges:&Vec>>>, edge:Rc) { +fn addEdge(edges: &mut Vec>>>, edge: Rc) -> Result<(), Exceptions> { let vertexIndex = (edge.fromPosition + edge.characterLength) as usize; - if edges[vertexIndex][edge.getEndMode().ordinal()].is_none() || - edges[vertexIndex][edge.getEndMode().ordinal()].as_ref().unwrap().cachedTotalSize > edge.cachedTotalSize { - edges[vertexIndex][edge.getEndMode().ordinal()] = Some(edge.clone()); + if edges[vertexIndex][edge.getEndMode()?.ordinal()].is_none() + || edges[vertexIndex][edge.getEndMode()?.ordinal()] + .as_ref() + .unwrap() + .cachedTotalSize + > edge.cachedTotalSize + { + edges[vertexIndex][edge.getEndMode()?.ordinal()] = Some(edge.clone()); } - } + Ok(()) +} - /** @return the number of words in which the string starting at from can be encoded in c40 or text Mode:: - * The number of characters encoded is returned in characterLength. - * The number of characters encoded is also minimal in the sense that the algorithm stops as soon - * as a character encoding fills a C40 word competely (three C40 values). An exception is at the - * end of the string where two C40 values are allowed (according to the spec the third c40 value - * is filled with 0 (Shift 1) in this case). - */ - fn getNumberOfC40Words( input:&Input, from:u32, c40:bool, characterLength:&[u32]) -> Result{ - let thirdsCount = 0; +/** @return the number of words in which the string starting at from can be encoded in c40 or text Mode:: + * The number of characters encoded is returned in characterLength. + * The number of characters encoded is also minimal in the sense that the algorithm stops as soon + * as a character encoding fills a C40 word competely (three C40 values). An exception is at the + * end of the string where two C40 values are allowed (according to the spec the third c40 value + * is filled with 0 (Shift 1) in this case). + */ +fn getNumberOfC40Words( + input: Rc, + from: u32, + c40: bool, + characterLength: &mut [u32], +) -> Result { + let mut thirdsCount = 0; for i in (from as usize)..input.length() { - // for (int i = from; i < input.length(); i++) { - if input.isECI(i as u32)? { - characterLength[0] = 0; - return Ok(0); - } - let ci = input.charAt(i)?; - if c40 && high_level_encoder::isNativeC40(ci) || !c40 && high_level_encoder::isNativeText(ci) { - thirdsCount+=1; //native - } else if !isExtendedASCII(ci, Some(input.getFNC1Character())) { - thirdsCount += 2; //shift - } else { - let asciiValue = ci as u8 & 0xff; - if asciiValue >= 128 && (c40 && high_level_encoder::isNativeC40( (asciiValue - 128) as char) || - !c40 && high_level_encoder::isNativeText( (asciiValue - 128) as char)) { - thirdsCount += 3; // shift, Upper shift - } else { - thirdsCount += 4; // shift, Upper shift, shift + // for (int i = from; i < input.length(); i++) { + if input.isECI(i as u32)? { + characterLength[0] = 0; + return Ok(0); + } + let ci = input.charAt(i)?; + if c40 && high_level_encoder::isNativeC40(ci) + || !c40 && high_level_encoder::isNativeText(ci) + { + thirdsCount += 1; //native + } else if !isExtendedASCII(ci, Some(input.getFNC1Character())) { + thirdsCount += 2; //shift + } else { + let asciiValue = ci as u8 & 0xff; + if asciiValue >= 128 + && (c40 && high_level_encoder::isNativeC40((asciiValue - 128) as char) + || !c40 && high_level_encoder::isNativeText((asciiValue - 128) as char)) + { + thirdsCount += 3; // shift, Upper shift + } else { + thirdsCount += 4; // shift, Upper shift, shift + } } - } - if thirdsCount % 3 == 0 || ((thirdsCount - 2) % 3 == 0 && i + 1 == input.length()) { - characterLength[0] = i as u32 - from + 1; - // return (int) Math.ceil(((double) thirdsCount) / 3.0); - return Ok((( thirdsCount as f64) / 3.0).ceil() as u32); - } + if thirdsCount % 3 == 0 || ((thirdsCount - 2) % 3 == 0 && i + 1 == input.length()) { + characterLength[0] = i as u32 - from + 1; + // return (int) Math.ceil(((double) thirdsCount) / 3.0); + return Ok(((thirdsCount as f64) / 3.0).ceil() as u32); + } } characterLength[0] = 0; - + Ok(0) - } - - fn addEdges( input:&Input, edges:&Vec>>>, from:u32, previous:Option>) -> Result<(),Exceptions> { +} +fn addEdges( + input: Rc, + edges: &mut Vec>>>, + from: u32, + previous: Option>, +) -> Result<(), Exceptions> { if input.isECI(from)? { - addEdge(edges, Rc::new(Edge::new(input, Mode::ASCII, from, 1, previous.clone()))); - return Ok(()); + addEdge( + edges, + Rc::new(Edge::new(input, Mode::ASCII, from, 1, previous.clone())?), + )?; + return Ok(()); } let ch = input.charAt(from as usize)?; - if previous.is_none() || previous.as_ref().unwrap().getEndMode() != Mode::EDF { //not possible to unlatch a full EDF edge to something - //else - if high_level_encoder::isDigit(ch) && input.haveNCharacters(from as usize, 2) && - high_level_encoder::isDigit(input.charAt(from as usize + 1)?) { - // two digits ASCII encoded - addEdge(edges, Rc::new(Edge::new(input, Mode::ASCII, from, 2, previous.clone()))); - } else { - // one ASCII encoded character or an extended character via Upper Shift - addEdge(edges, Rc::new(Edge::new(input, Mode::ASCII, from, 1, previous.clone()))); - } - - let modes = [Mode::C40, Mode::TEXT]; - for mode in modes { - // for (Mode mode : modes) { - let characterLength = [0u32;1]; - if getNumberOfC40Words(input, from, mode == Mode::C40, &characterLength)? > 0 { - addEdge(edges, Rc::new(Edge::new(input, mode, from, characterLength[0], previous.clone()))); + if previous.is_none() || previous.as_ref().unwrap().getEndMode()? != Mode::EDF { + //not possible to unlatch a full EDF edge to something + //else + if high_level_encoder::isDigit(ch) + && input.haveNCharacters(from as usize, 2) + && high_level_encoder::isDigit(input.charAt(from as usize + 1)?) + { + // two digits ASCII encoded + addEdge( + edges, + Rc::new(Edge::new( + input.clone(), + Mode::ASCII, + from, + 2, + previous.clone(), + )?), + )?; + } else { + // one ASCII encoded character or an extended character via Upper Shift + addEdge( + edges, + Rc::new(Edge::new( + input.clone(), + Mode::ASCII, + from, + 1, + previous.clone(), + )?), + )?; } - } - - if input.haveNCharacters(from as usize,3) && - high_level_encoder::isNativeX12(input.charAt(from as usize)?) && - high_level_encoder::isNativeX12(input.charAt(from as usize+ 1)?) && - high_level_encoder::isNativeX12(input.charAt(from as usize + 2)?) { - addEdge(edges, Rc::new(Edge::new(input, Mode::X12, from, 3, previous.clone()))); - } - addEdge(edges, Rc::new(Edge::new(input, Mode::B256, from, 1, previous.clone()))); + let modes = [Mode::C40, Mode::TEXT]; + for mode in modes { + // for (Mode mode : modes) { + let mut characterLength = [0u32; 1]; + if getNumberOfC40Words(input.clone(), from, mode == Mode::C40, &mut characterLength)? + > 0 + { + addEdge( + edges, + Rc::new(Edge::new( + input.clone(), + mode, + from, + characterLength[0], + previous.clone(), + )?), + )?; + } + } + + if input.haveNCharacters(from as usize, 3) + && high_level_encoder::isNativeX12(input.charAt(from as usize)?) + && high_level_encoder::isNativeX12(input.charAt(from as usize + 1)?) + && high_level_encoder::isNativeX12(input.charAt(from as usize + 2)?) + { + addEdge( + edges, + Rc::new(Edge::new( + input.clone(), + Mode::X12, + from, + 3, + previous.clone(), + )?), + )?; + } + + addEdge( + edges, + Rc::new(Edge::new( + input.clone(), + Mode::B256, + from, + 1, + previous.clone(), + )?), + )?; } //We create 4 EDF edges, with 1, 2 3 or 4 characters length. The fourth normally doesn't have a latch to ASCII //unless it is 2 characters away from the end of the input. - let i = 0u32; + let mut i = 0u32; while i < 3 { - // for (i = 0; i < 3; i++) { - let pos = from + i; - if input.haveNCharacters(pos as usize,1) && high_level_encoder::isNativeEDIFACT(input.charAt(pos as usize)?) { - addEdge(edges, Rc::new(Edge::new(input, Mode::EDF, from, i + 1, previous.clone()))); - } else { - break; - } - i+=1; + // for (i = 0; i < 3; i++) { + let pos = from + i; + if input.haveNCharacters(pos as usize, 1) + && high_level_encoder::isNativeEDIFACT(input.charAt(pos as usize)?) + { + addEdge( + edges, + Rc::new(Edge::new( + input.clone(), + Mode::EDF, + from, + i + 1, + previous.clone(), + )?), + )?; + } else { + break; + } + i += 1; } - if i == 3 && input.haveNCharacters(from as usize, 4) && high_level_encoder::isNativeEDIFACT(input.charAt(from as usize + 3)?) { - addEdge(edges, Rc::new( Edge::new(input, Mode::EDF, from, 4, previous.clone()))); + if i == 3 + && input.haveNCharacters(from as usize, 4) + && high_level_encoder::isNativeEDIFACT(input.charAt(from as usize + 3)?) + { + addEdge( + edges, + Rc::new(Edge::new(input, Mode::EDF, from, 4, previous.clone())?), + )?; } Ok(()) - } - - fn encodeMinimally( input:&Input) -> Result{ +} +fn encodeMinimally(input: Rc) -> Result { // @SuppressWarnings("checkstyle:lineLength") /* The minimal encoding is computed by Dijkstra. The acyclic graph is modeled as follows: * A vertex represents a combination of a position in the input and an encoding mode where position 0 @@ -296,16 +418,16 @@ const ISO_8859_1_ENCODER : EncodingRef = encoding::all::ISO_8859_1; * Likewise the end vertices are located after the last character at position input.length(). * For any position there might be up to six vertices, one for each of the encoding types ASCII, C40, TEXT, X12, * EDF and B256. - * + * * As an example consider the input string "ABC123" then at position 0 there is only one vertex with the default * ASCII encodation. At position 3 there might be vertices for the types ASCII, C40, X12, EDF and B256. * * An edge leading to such a vertex encodes one or more of the characters left of the position that the vertex * represents. It encodes the characters in the encoding mode of the vertex that it ends on. In other words, - * all edges leading to a particular vertex encode the same characters (the length of the suffix can vary) using the same + * all edges leading to a particular vertex encode the same characters (the length of the suffix can vary) using the same * encoding Mode:: * As an example consider the input string "ABC123" and the vertex (4,EDF). Possible edges leading to this vertex - * are: + * are: * (0,ASCII) --EDF(ABC1)--> (4,EDF) * (1,ASCII) --EDF(BC1)--> (4,EDF) * (1,B256) --EDF(BC1)--> (4,EDF) @@ -425,11 +547,11 @@ const ISO_8859_1_ENCODER : EncodingRef = encoding::all::ISO_8859_1; * (0,ASCII) B256(A) (3) --> (1,B256) B256(B) (3) --> (2,B256) EDF(CDE) (6) --> (5,EDF) * (0,ASCII) B256(A) (3) --> (1,B256) B256(B) (3) --> (2,B256) EDF(CDEF) (6) --> (6,EDF) * - * Edge "(2,ASCII) ASCII(C) (3) --> (3,ASCII)" is minimal for the vertex (3,ASCII) so that edges "(2,EDF) ASCII(C) (5) --> (3,ASCII)" + * Edge "(2,ASCII) ASCII(C) (3) --> (3,ASCII)" is minimal for the vertex (3,ASCII) so that edges "(2,EDF) ASCII(C) (5) --> (3,ASCII)" * and "(2,B256) ASCII(C) (4) --> (3,ASCII)" can be removed. - * Edge "(0,ASCII) EDF(ABC) (4) --> (3,EDF)" is minimal for the vertex (3,EDF) so that edges "(1,ASCII) EDF(BC) (5) --> (3,EDF)" + * Edge "(0,ASCII) EDF(ABC) (4) --> (3,EDF)" is minimal for the vertex (3,EDF) so that edges "(1,ASCII) EDF(BC) (5) --> (3,EDF)" * and "(1,B256) EDF(BC) (6) --> (3,EDF)" can be removed. - * Edge "(2,B256) B256(C) (4) --> (3,B256)" is minimal for the vertex (3,B256) so that edges "(2,ASCII) B256(C) (5) --> (3,B256)" + * Edge "(2,B256) B256(C) (4) --> (3,B256)" is minimal for the vertex (3,B256) so that edges "(2,ASCII) B256(C) (5) --> (3,B256)" * and "(2,EDF) B256(C) (6) --> (3,B256)" can be removed. * * This continues for vertices 3 thru 7 @@ -479,164 +601,229 @@ const ISO_8859_1_ENCODER : EncodingRef = encoding::all::ISO_8859_1; // Array that represents vertices. There is a vertex for every character and Mode:: // The last dimension in the array below encodes the 6 modes ASCII, C40, TEXT, X12, EDF and B256 // let edges = new Edge[inputLength + 1][6]; - let edges = vec![vec![None;6];inputLength + 1]; - addEdges(input, &edges, 0, None); + let mut edges = vec![vec![None; 6]; inputLength + 1]; + addEdges(input.clone(), &mut edges, 0, None)?; for i in 1..=inputLength { - // for (int i = 1; i <= inputLength; i++) { - for j in 0..6 { - // for (int j = 0; j < 6; j++) { - if edges[i][j].is_some() && i < inputLength { - addEdges(input, &edges, i as u32, edges[i][j]); + // for (int i = 1; i <= inputLength; i++) { + for j in 0..6 { + // for (int j = 0; j < 6; j++) { + if edges[i][j].is_some() && i < inputLength { + let edge = edges[i][j].clone(); + addEdges(input.clone(), &mut edges, i as u32, edge)?; + } + } + //optimize memory by removing edges that have been passed. + for j in 0..6 { + // for (int j = 0; j < 6; j++) { + edges[i - 1][j] = None; } - } - //optimize memory by removing edges that have been passed. - for j in 0..6 { - // for (int j = 0; j < 6; j++) { - edges[i - 1][j] = None; - } } - let minimalJ:i32 = -1; - let minimalSize = i32::MAX; + let mut minimalJ: i32 = -1; + let mut minimalSize = i32::MAX; for j in 0..6 { - // for (int j = 0; j < 6; j++) { - if edges[inputLength][j].is_some() { - let edge = edges[inputLength][j].as_ref().unwrap(); - let size = if j >= 1 && j <= 3 {edge.cachedTotalSize + 1} else {edge.cachedTotalSize}; //C40, TEXT and X12 need an - // extra unlatch at the end - if (size as i32) < minimalSize { - minimalSize = size as i32; - minimalJ = j as i32; + // for (int j = 0; j < 6; j++) { + if edges[inputLength][j].is_some() { + let edge = edges[inputLength][j].as_ref().unwrap(); + let size = if j >= 1 && j <= 3 { + edge.cachedTotalSize + 1 + } else { + edge.cachedTotalSize + }; //C40, TEXT and X12 need an + // extra unlatch at the end + if (size as i32) < minimalSize { + minimalSize = size as i32; + minimalJ = j as i32; + } } - } } if minimalJ < 0 { - return Err(Exceptions::RuntimeException(format!("Internal error: failed to encode \"{}\"",input))); + return Err(Exceptions::RuntimeException(format!( + "Internal error: failed to encode \"{}\"", + input + ))); } - Ok(RXingResult::new(edges[inputLength][minimalJ as usize].clone().unwrap())) - } + RXingResult::new(edges[inputLength][minimalJ as usize].clone()) +} - const allCodewordCapacities : [u32;28] = [3, 5, 8, 10, 12, 16, 18, 22, 30, 32, 36, 44, 49, 62, 86, 114, - 144, 174, 204, 280, 368, 456, 576, 696, 816, 1050, 1304, 1558]; -const squareCodewordCapacities : [u32;24]= [3, 5, 8, 12, 18, 22, 30, 36, 44, 62, 86, 114, 144, 174, 204, - 280, 368, 456, 576, 696, 816, 1050, 1304, 1558]; -const rectangularCodewordCapacities :[u32;6]= [5, 10, 16, 33, 32, 49]; +const ALL_CODEWORD_CAPACITIES: [u32; 28] = [ + 3, 5, 8, 10, 12, 16, 18, 22, 30, 32, 36, 44, 49, 62, 86, 114, 144, 174, 204, 280, 368, 456, + 576, 696, 816, 1050, 1304, 1558, +]; +const SQUARE_CODEWORD_CAPACITIES: [u32; 24] = [ + 3, 5, 8, 12, 18, 22, 30, 36, 44, 62, 86, 114, 144, 174, 204, 280, 368, 456, 576, 696, 816, + 1050, 1304, 1558, +]; +const RECTANGULAR_CODEWORD_CAPACITIES: [u32; 6] = [5, 10, 16, 33, 32, 49]; - struct Edge { - input:Input, - mode:Mode, //the mode at the start of this edge. - fromPosition:u32, - characterLength:u32, - previous:Rc, - cachedTotalSize:u32, - } - impl Edge{ +struct Edge { + input: Rc, + mode: Mode, //the mode at the start of this edge. + fromPosition: u32, + characterLength: u32, + previous: Option>, + cachedTotalSize: u32, +} +impl Edge { + fn new( + input: Rc, + mode: Mode, + fromPosition: u32, + characterLength: u32, + previous: Option>, + ) -> Result { + // this.input = input; + // this.mode = mode; + // this.fromPosition = fromPosition; + // this.characterLength = characterLength; + // this.previous = previous; + assert!(fromPosition + characterLength <= input.length() as u32); - - fn new( input:&Input, mode:Mode, fromPosition:u32, characterLength:u32, previous:Option>) -> Self{ - this.input = input; - this.mode = mode; - this.fromPosition = fromPosition; - this.characterLength = characterLength; - this.previous = previous; - assert fromPosition + characterLength <= input.length(); + let mut size = if let Some(previous) = previous.clone() { + previous.cachedTotalSize + } else { + 0 + }; - int size = previous != null ? previous.cachedTotalSize : 0; + let previousMode = Self::getPreviousMode(previous.clone())?; - Mode previousMode = getPreviousMode(); + /* + * Switching modes + * ASCII -> C40: latch 230 + * ASCII -> TEXT: latch 239 + * ASCII -> X12: latch 238 + * ASCII -> EDF: latch 240 + * ASCII -> B256: latch 231 + * C40 -> ASCII: word(c1,c2,c3), 254 + * TEXT -> ASCII: word(c1,c2,c3), 254 + * X12 -> ASCII: word(c1,c2,c3), 254 + * EDIFACT -> ASCII: Unlatch character,0,0,0 or c1,Unlatch character,0,0 or c1,c2,Unlatch character,0 or + * c1,c2,c3,Unlatch character + * B256 -> ASCII: without latch after n bytes + */ + match mode { + Mode::ASCII => { + size += 1; + if input.isECI(fromPosition).expect("bool") + || isExtendedASCII( + input + .charAt(fromPosition as usize) + .expect("char must exist)"), + Some(input.getFNC1Character()), + ) + { + size += 1; + } + if previousMode == Mode::C40 + || previousMode == Mode::TEXT + || previousMode == Mode::X12 + { + size += 1; // unlatch 254 to ASCII + } + } + Mode::B256 => { + size += 1; + if previousMode != Mode::B256 { + size += 1; //byte count + } else if Self::getB256Size(mode, previous.clone()) == 250 { + size += 1; //extra byte count + } + if previousMode == Mode::ASCII { + size += 1; //latch to B256 + } else if previousMode == Mode::C40 + || previousMode == Mode::TEXT + || previousMode == Mode::X12 + { + size += 2; //unlatch to ASCII, latch to B256 + } + } + Mode::C40 | Mode::TEXT | Mode::X12 => { + if mode == Mode::X12 { + size += 2; + } else { + let mut charLen = [0u32; 1]; + size += getNumberOfC40Words( + input.clone(), + fromPosition, + mode == Mode::C40, + &mut charLen, + ) + .expect("works") + * 2; + } - /* - * Switching modes - * ASCII -> C40: latch 230 - * ASCII -> TEXT: latch 239 - * ASCII -> X12: latch 238 - * ASCII -> EDF: latch 240 - * ASCII -> B256: latch 231 - * C40 -> ASCII: word(c1,c2,c3), 254 - * TEXT -> ASCII: word(c1,c2,c3), 254 - * X12 -> ASCII: word(c1,c2,c3), 254 - * EDIFACT -> ASCII: Unlatch character,0,0,0 or c1,Unlatch character,0,0 or c1,c2,Unlatch character,0 or - * c1,c2,c3,Unlatch character - * B256 -> ASCII: without latch after n bytes - */ - switch (mode) { - case ASCII: - size++; - if (input.isECI(fromPosition) || isExtendedASCII(input.charAt(fromPosition), input.getFNC1Character())) { - size++; - } - if (previousMode == Mode::C40 || - previousMode == Mode::TEXT || - previousMode == Mode::X12) { - size++; // unlatch 254 to ASCII - } - break; - case B256: - size++; - if (previousMode != Mode::B256) { - size++; //byte count - } else if (getB256Size() == 250) { - size++; //extra byte count - } - if (previousMode == Mode::ASCII) { - size++; //latch to B256 - } else if (previousMode == Mode::C40 || - previousMode == Mode::TEXT || - previousMode == Mode::X12) { - size += 2; //unlatch to ASCII, latch to B256 - } - break; - case C40: - case TEXT: - case X12: - if (mode == Mode::X12) { - size += 2; - } else { - int[] charLen = new int[1]; - size += getNumberOfC40Words(input, fromPosition, mode == Mode::C40, charLen) * 2; - } - - if (previousMode == Mode::ASCII || previousMode == Mode::B256) { - size++; //additional byte for latch from ASCII to this mode - } else if (previousMode != mode && (previousMode == Mode::C40 || - previousMode == Mode::TEXT || - previousMode == Mode::X12)) { - size += 2; //unlatch 254 to ASCII followed by latch to this mode - } - break; - case EDF: - size += 3; - if (previousMode == Mode::ASCII || previousMode == Mode::B256) { - size++; //additional byte for latch from ASCII to this mode - } else if (previousMode == Mode::C40 || - previousMode == Mode::TEXT || - previousMode == Mode::X12) { - size += 2; //unlatch 254 to ASCII followed by latch to this mode - } - break; - } - cachedTotalSize = size; + if previousMode == Mode::ASCII || previousMode == Mode::B256 { + size += 1; //additional byte for latch from ASCII to this mode + } else if previousMode != mode + && (previousMode == Mode::C40 + || previousMode == Mode::TEXT + || previousMode == Mode::X12) + { + size += 2; //unlatch 254 to ASCII followed by latch to this mode + } + } + Mode::EDF => { + size += 3; + if previousMode == Mode::ASCII || previousMode == Mode::B256 { + size += 1; //additional byte for latch from ASCII to this mode + } else if previousMode == Mode::C40 + || previousMode == Mode::TEXT + || previousMode == Mode::X12 + { + size += 2; //unlatch 254 to ASCII followed by latch to this mode + } + } + _ => {} + } + Ok(Self { + input, + mode, + fromPosition, + characterLength, + previous, + cachedTotalSize: size, + }) + // cachedTotalSize = size; } // does not count beyond 250 - pub fn getB256Size(&self) -> u32{ - int cnt = 0; - Edge current = this; - while (current != null && current.mode == Mode::B256 && cnt <= 250) { - cnt++; - current = current.previous; - } - return cnt; + pub fn getB256Size(mode: Mode, previous: Option>) -> u32 { + if mode != Mode::B256 { + return 0; + } + let mut cnt = 1; + let mut current = previous; + while current.is_some() && current.as_ref().unwrap().mode == Mode::B256 && cnt <= 250 { + cnt += 1; + current = current.clone().as_ref().unwrap().previous.clone(); + } + // let cnt = 0; + // Edge current = this; + // while (current != null && current.mode == Mode::B256 && cnt <= 250) { + // cnt+=1; + // current = current.previous; + // } + cnt } - pub fn getPreviousStartMode(&self) -> Mode{ - return previous == null ? Mode::ASCII : previous.mode; + pub fn getPreviousStartMode(previous: Option>) -> Mode { + if let Some(prev) = previous { + prev.mode + } else { + Mode::ASCII + } + // if previous.is_none() { Mode::ASCII} else {previous.as_ref().unwrap().mode} } - pub fn getPreviousMode(&self) -> Mode{ - return previous == null ? Mode::ASCII : previous.getEndMode(); + pub fn getPreviousMode(previous: Option>) -> Result { + if let Some(prev) = previous { + prev.getEndMode() + } else { + Ok(Mode::ASCII) + } + // return previous == null ? Mode::ASCII : previous.getEndMode(); } /** Returns Mode::ASCII in case that: @@ -645,477 +832,639 @@ const rectangularCodewordCapacities :[u32;6]= [5, 10, 16, 33, 32, 49]; * - Mode is C40, TEXT or X12 and the remaining characters can be encoded in at most 1 ASCII byte. * Returns mode in all other cases. * */ - pub fn getEndMode(&self)->Mode { - if (mode == Mode::EDF) { - if (characterLength < 4) { - return Mode::ASCII; + pub fn getEndMode(&self) -> Result { + let mode = self.mode; + if mode == Mode::EDF { + if self.characterLength < 4 { + return Ok(Mode::ASCII); + } + let lastASCII = Self::getLastASCII(&self)?; // see 5.2.8.2 EDIFACT encodation Rules + if lastASCII > 0 + && self.getCodewordsRemaining(self.cachedTotalSize + lastASCII) <= 2 - lastASCII + { + return Ok(Mode::ASCII); + } } - int lastASCII = getLastASCII(); // see 5.2.8.2 EDIFACT encodation Rules - if (lastASCII > 0 && getCodewordsRemaining(cachedTotalSize + lastASCII) <= 2 - lastASCII) { - return Mode::ASCII; + if mode == Mode::C40 || mode == Mode::TEXT || mode == Mode::X12 { + // see 5.2.5.2 C40 encodation rules and 5.2.7.2 ANSI X12 encodation rules + if self.fromPosition + self.characterLength >= self.input.length() as u32 + && self.getCodewordsRemaining(self.cachedTotalSize) == 0 + { + return Ok(Mode::ASCII); + } + let lastASCII = Self::getLastASCII(&self)?; + if lastASCII == 1 && self.getCodewordsRemaining(self.cachedTotalSize + 1) == 0 { + return Ok(Mode::ASCII); + } } - } - if (mode == Mode::C40 || - mode == Mode::TEXT || - mode == Mode::X12) { - // see 5.2.5.2 C40 encodation rules and 5.2.7.2 ANSI X12 encodation rules - if (fromPosition + characterLength >= input.length() && getCodewordsRemaining(cachedTotalSize) == 0) { - return Mode::ASCII; - } - int lastASCII = getLastASCII(); - if (lastASCII == 1 && getCodewordsRemaining(cachedTotalSize + 1) == 0) { - return Mode::ASCII; - } - } - return mode; + Ok(mode) } - pub fn getMode(&self) -> Mode{ - return mode; + pub fn getMode(&self) -> Mode { + self.mode } /** Peeks ahead and returns 1 if the postfix consists of exactly two digits, 2 if the postfix consists of exactly - * two consecutive digits and a non extended character or of 4 digits. + * two consecutive digits and a non extended character or of 4 digits. * Returns 0 in any other case **/ - pub fn getLastASCII(&self) -> u32{ - int length = input.length(); - int from = fromPosition + characterLength; - if (length - from > 4 || from >= length) { - return 0; - } - if (length - from == 1) { - if (isExtendedASCII(input.charAt(from), input.getFNC1Character())) { - return 0; + pub fn getLastASCII(&self) -> Result { + let length = self.input.length() as u32; + let from = self.fromPosition + self.characterLength; + if length - from > 4 || from >= length { + return Ok(0); } - return 1; - } - if (length - from == 2) { - if (isExtendedASCII(input.charAt(from), input.getFNC1Character()) || isExtendedASCII(input.charAt(from + 1), - input.getFNC1Character())) { - return 0; + if length - from == 1 { + if isExtendedASCII( + self.input.charAt(from as usize)?, + Some(self.input.getFNC1Character()), + ) { + return Ok(0); + } + return Ok(1); } - if (high_level_encoder::isDigit(input.charAt(from)) && high_level_encoder::isDigit(input.charAt(from + 1))) { - return 1; + if length - from == 2 { + if isExtendedASCII( + self.input.charAt(from as usize)?, + Some(self.input.getFNC1Character()), + ) || isExtendedASCII( + self.input.charAt(from as usize + 1)?, + Some(self.input.getFNC1Character()), + ) { + return Ok(0); + } + if high_level_encoder::isDigit(self.input.charAt(from as usize)?) + && high_level_encoder::isDigit(self.input.charAt(from as usize + 1)?) + { + return Ok(1); + } + return Ok(2); } - return 2; - } - if (length - from == 3) { - if (high_level_encoder::isDigit(input.charAt(from)) && high_level_encoder::isDigit(input.charAt(from + 1)) - && !isExtendedASCII(input.charAt(from + 2), input.getFNC1Character())) { - return 2; + if length - from == 3 { + if high_level_encoder::isDigit(self.input.charAt(from as usize)?) + && high_level_encoder::isDigit(self.input.charAt(from as usize + 1)?) + && !isExtendedASCII( + self.input.charAt(from as usize + 2)?, + Some(self.input.getFNC1Character()), + ) + { + return Ok(2); + } + if high_level_encoder::isDigit(self.input.charAt(from as usize + 1)?) + && high_level_encoder::isDigit(self.input.charAt(from as usize + 2)?) + && !isExtendedASCII( + self.input.charAt(from as usize)?, + Some(self.input.getFNC1Character()), + ) + { + return Ok(2); + } + return Ok(0); } - if (high_level_encoder::isDigit(input.charAt(from + 1)) && high_level_encoder::isDigit(input.charAt(from + 2)) - && !isExtendedASCII(input.charAt(from), input.getFNC1Character())) { - return 2; + if high_level_encoder::isDigit(self.input.charAt(from as usize)?) + && high_level_encoder::isDigit(self.input.charAt(from as usize + 1)?) + && high_level_encoder::isDigit(self.input.charAt(from as usize + 2)?) + && high_level_encoder::isDigit(self.input.charAt(from as usize + 3)?) + { + return Ok(2); } - return 0; - } - if (high_level_encoder::isDigit(input.charAt(from)) && high_level_encoder::isDigit(input.charAt(from + 1)) - && high_level_encoder::isDigit(input.charAt(from + 2)) && high_level_encoder::isDigit(input.charAt(from + 3))) { - return 2; - } - return 0; + + Ok(0) } /** Returns the capacity in codewords of the smallest symbol that has enough capacity to fit the given minimal * number of codewords. **/ - pub fn getMinSymbolSize(&self, minimum:u32) -> u32{ - switch (input.getShapeHint()) { - case FORCE_SQUARE: - for (int capacity : squareCodewordCapacities) { - if (capacity >= minimum) { - return capacity; + pub fn getMinSymbolSize(&self, minimum: u32) -> u32 { + match self.input.getShapeHint() { + SymbolShapeHint::FORCE_SQUARE => { + for capacity in SQUARE_CODEWORD_CAPACITIES { + // for (int capacity : squareCodewordCapacities) { + if capacity >= minimum { + return capacity; + } + } } - } - break; - case FORCE_RECTANGLE: - for (int capacity : rectangularCodewordCapacities) { - if (capacity >= minimum) { - return capacity; + SymbolShapeHint::FORCE_RECTANGLE => { + for capacity in RECTANGULAR_CODEWORD_CAPACITIES { + // for (int capacity : rectangularCodewordCapacities) { + if capacity >= minimum { + return capacity; + } + } } - } - break; - } - for (int capacity : allCodewordCapacities) { - if (capacity >= minimum) { - return capacity; + _ => {} } - } - return allCodewordCapacities[allCodewordCapacities.length - 1]; + for capacity in ALL_CODEWORD_CAPACITIES { + // for (int capacity : allCodewordCapacities) { + if capacity >= minimum { + return capacity; + } + } + + ALL_CODEWORD_CAPACITIES[ALL_CODEWORD_CAPACITIES.len() - 1] } /** Returns the remaining capacity in codewords of the smallest symbol that has enough capacity to fit the given * minimal number of codewords. **/ - pub fn getCodewordsRemaining( minimum:u32) -> u32{ - return getMinSymbolSize(minimum) - minimum; + pub fn getCodewordsRemaining(&self, minimum: u32) -> u32 { + Self::getMinSymbolSize(&self, minimum) - minimum } - pub fn getBytes1( c:u32) -> Vec{ - byte[] result = new byte[1]; - result[0] = (byte) c; - return result; + pub fn getBytes1(c: u32) -> Vec { + // let result = vec![0u8;1]; + // result[0] = c as u8; + // result + vec![c as u8] } - pub fn getBytes2( c1:u32, c2:u32) -> Vec{ - byte[] result = new byte[2]; - result[0] = (byte) c1; - result[1] = (byte) c2; - return result; + pub fn getBytes2(c1: u32, c2: u32) -> Vec { + // byte[] result = new byte[2]; + // result[0] = (byte) c1; + // result[1] = (byte) c2; + // return result; + vec![c1 as u8, c2 as u8] } - pub fn setC40Word( bytes:&[u8], offset:u32, c1:u32, c2:u32, c3:u32) { - int val16 = (1600 * (c1 & 0xff)) + (40 * (c2 & 0xff)) + (c3 & 0xff) + 1; - bytes[offset] = (byte) (val16 / 256); - bytes[offset + 1] = (byte) (val16 % 256); + pub fn setC40Word(bytes: &mut [u8], offset: u32, c1: u32, c2: u32, c3: u32) { + let val16 = (1600 * (c1 & 0xff)) + (40 * (c2 & 0xff)) + (c3 & 0xff) + 1; + bytes[offset as usize] = (val16 / 256) as u8; + bytes[offset as usize + 1] = (val16 % 256) as u8; } - fn getX12Value( c:char) -> u32{ - return c == 13 ? 0 : - c == 42 ? 1 : - c == 62 ? 2 : - c == 32 ? 3 : - c >= 48 && c <= 57 ? c - 44 : - c >= 65 && c <= 90 ? c - 51 : c; - } - - pub fn getX12Words(&self) -> Vec { - assert characterLength % 3 == 0; - byte[] result = new byte[characterLength / 3 * 2]; - for (int i = 0; i < result.length; i += 2) { - setC40Word(result,i,getX12Value(input.charAt(fromPosition + i / 2 * 3)), - getX12Value(input.charAt(fromPosition + i / 2 * 3 + 1)), - getX12Value(input.charAt(fromPosition + i / 2 * 3 + 2))); - } - return result; - } - - pub fn getShiftValue( c:char, c40:bool, fnc1:Option) -> u32{ - return (c40 && isInC40Shift1Set(c) || - !c40 && isInTextShift1Set(c)) ? 0 : - (c40 && isInC40Shift2Set(c, fnc1) || - !c40 && isInTextShift2Set(c, fnc1)) ? 1 : 2; - } - - fn getC40Value( c40:bool, setIndex:u32, c:char, fnc1:Option) -> u32{ - if (c == fnc1) { - assert setIndex == 2; - return 27; - } - if (c40) { - return c <= 31 ? c : - c == 32 ? 3 : - c <= 47 ? c - 33 : - c <= 57 ? c - 44 : - c <= 64 ? c - 43 : - c <= 90 ? c - 51 : - c <= 95 ? c - 69 : - c <= 127 ? c - 96 : c; - } else { - return c == 0 ? 0 : - setIndex == 0 && c <= 3 ? c - 1 : //is this a bug in the spec? - setIndex == 1 && c <= 31 ? c : - c == 32 ? 3 : - c >= 33 && c <= 47 ? c - 33 : - c >= 48 && c <= 57 ? c - 44 : - c >= 58 && c <= 64 ? c - 43 : - c >= 65 && c <= 90 ? c - 64 : - c >= 91 && c <= 95 ? c - 69 : - c == 96 ? 0 : - c >= 97 && c <= 122 ? c - 83 : - c >= 123 && c <= 127 ? c - 96 : c; - } - } - - pub fn getC40Words(&self, c40:bool, fnc1:Option) -> Vec{ - List c40Values = new ArrayList<>(); - for (int i = 0; i < characterLength; i++) { - char ci = input.charAt(fromPosition + i); - if (c40 && high_level_encoder::isNativeC40(ci) || !c40 && high_level_encoder::isNativeText(ci)) { - c40Values.add((byte) getC40Value(c40, 0, ci, fnc1)); - } else if (!isExtendedASCII(ci, fnc1)) { - int shiftValue = getShiftValue(ci, c40, fnc1); - c40Values.add((byte) shiftValue); //Shift[123] - c40Values.add((byte) getC40Value(c40, shiftValue, ci, fnc1)); + fn getX12Value(c: char) -> u32 { + let c = c as u32; + if c == 13 { + 0 + } else if c == 42 { + 1 + } else if c == 62 { + 2 + } else if c == 32 { + 3 + } else if c >= 48 && c <= 57 { + c - 44 + } else if c >= 65 && c <= 90 { + c - 51 } else { - char asciiValue = (char) ((ci & 0xff) - 128); - if (c40 && high_level_encoder::isNativeC40(asciiValue) || - !c40 && high_level_encoder::isNativeText(asciiValue)) { - c40Values.add((byte) 1); //Shift 2 - c40Values.add((byte) 30); //Upper Shift - c40Values.add((byte) getC40Value(c40, 0, asciiValue, fnc1)); - } else { - c40Values.add((byte) 1); //Shift 2 - c40Values.add((byte) 30); //Upper Shift - int shiftValue = getShiftValue(asciiValue, c40, fnc1); - c40Values.add((byte) shiftValue); // Shift[123] - c40Values.add((byte) getC40Value(c40, shiftValue, asciiValue, fnc1)); - } + c } - } - - if ((c40Values.size() % 3) != 0) { - assert (c40Values.size() - 2) % 3 == 0 && fromPosition + characterLength == input.length(); - c40Values.add((byte) 0); // pad with 0 (Shift 1) - } - - byte[] result = new byte[c40Values.size() / 3 * 2]; - int byteIndex = 0; - for (int i = 0; i < c40Values.size(); i += 3) { - setC40Word(result,byteIndex, c40Values.get(i) & 0xff, c40Values.get(i + 1) & 0xff, c40Values.get(i + 2) & 0xff); - byteIndex += 2; - } - return result; } - pub fn getEDFBytes(&self) -> Vec { - int numberOfThirds = (int) Math.ceil(characterLength / 4.0); - byte[] result = new byte[numberOfThirds * 3]; - int pos = fromPosition; - int endPos = Math.min(fromPosition + characterLength - 1 , input.length() - 1); - for (int i = 0; i < numberOfThirds; i += 3) { - int[] edfValues = new int[4]; - for (int j = 0; j < 4; j++) { - if (pos <= endPos) { - edfValues[j] = input.charAt(pos++) & 0x3f; - } else { - edfValues[j] = pos == endPos + 1 ? 0x1f : 0; - } + pub fn getX12Words(&self) -> Result, Exceptions> { + assert!(self.characterLength % 3 == 0); + let mut result = vec![0u8; self.characterLength as usize / 3 * 2]; + let mut i = 0; + while i <= result.len() { + // for (int i = 0; i < result.length; i += 2) { + Self::setC40Word( + &mut result, + i as u32, + Self::getX12Value(self.input.charAt(self.fromPosition as usize + i / 2 * 3)?), + Self::getX12Value( + self.input + .charAt(self.fromPosition as usize + i / 2 * 3 + 1)?, + ), + Self::getX12Value( + self.input + .charAt(self.fromPosition as usize + i / 2 * 3 + 2)?, + ), + ); + i += 2; } - int val24 = edfValues[0] << 18; - val24 |= edfValues[1] << 12; - val24 |= edfValues[2] << 6; - val24 |= edfValues[3]; - result[i] = (byte) ((val24 >> 16) & 0xff); - result[i + 1] = (byte) ((val24 >> 8) & 0xff); - result[i + 2] = (byte) (val24 & 0xff); - } - return result; + return Ok(result); } - pub fn getLatchBytes(&self) -> Vec { - switch (getPreviousMode()) { - case ASCII: - case B256: //after B256 ends (via length) we are back to ASCII - switch (mode) { - case B256: - return getBytes(231); - case C40: - return getBytes(230); - case TEXT: - return getBytes(239); - case X12: - return getBytes(238); - case EDF: - return getBytes(240); - } - break; - case C40: - case TEXT: - case X12: - if (mode != getPreviousMode()) { - switch (mode) { - case ASCII: - return getBytes(254); - case B256: - return getBytes(254, 231); - case C40: - return getBytes(254, 230); - case TEXT: - return getBytes(254, 239); - case X12: - return getBytes(254, 238); - case EDF: - return getBytes(254, 240); + pub fn getShiftValue(c: char, c40: bool, fnc1: Option) -> u32 { + if c40 && isInC40Shift1Set(c) || !c40 && isInTextShift1Set(c) { + 0 + } else if c40 && isInC40Shift2Set(c, fnc1) || !c40 && isInTextShift2Set(c, fnc1) { + 1 + } else { + 2 + } + } + + fn getC40Value(c40: bool, setIndex: u32, c: char, fnc1: Option) -> u32 { + if let Some(fnc1_char) = fnc1 { + if c == fnc1_char { + assert!(setIndex == 2); + return 27; } - } - break; - case EDF: - assert mode == Mode::EDF; //The rightmost EDIFACT edge always contains an unlatch character - break; - } - return new byte[0]; + } + if c40 { + let c = c as u32; + return if c <= 31 { + c + } else if c == 32 { + 3 + } else if c <= 47 { + c - 33 + } else if c <= 57 { + c - 44 + } else if c <= 64 { + c - 43 + } else if c <= 90 { + c - 51 + } else if c <= 95 { + c - 69 + } else if c <= 127 { + c - 96 + } else { + c + }; + } else { + let c = c as u32; + return if c == 0 { + 0 + } else if setIndex == 0 && c <= 3 { + c - 1 + } else if + //is this a bug in the spec? + setIndex == 1 && c <= 31 { + c + } else if c == 32 { + 3 + } else if c >= 33 && c <= 47 { + c - 33 + } else if c >= 48 && c <= 57 { + c - 44 + } else if c >= 58 && c <= 64 { + c - 43 + } else if c >= 65 && c <= 90 { + c - 64 + } else if c >= 91 && c <= 95 { + c - 69 + } else if c == 96 { + 0 + } else if c >= 97 && c <= 122 { + c - 83 + } else if c >= 123 && c <= 127 { + c - 96 + } else { + c + }; + } + } + + pub fn getC40Words(&self, c40: bool, fnc1: Option) -> Result, Exceptions> { + let mut c40Values: Vec = Vec::new(); + let fromPosition = self.fromPosition as usize; + for i in 0..self.characterLength as usize { + // for (int i = 0; i < characterLength; i++) { + let ci = self.input.charAt(fromPosition + i)?; + if c40 && high_level_encoder::isNativeC40(ci) + || !c40 && high_level_encoder::isNativeText(ci) + { + c40Values.push(Self::getC40Value(c40, 0, ci, fnc1) as u8); + } else if !isExtendedASCII(ci, fnc1) { + let shiftValue = Self::getShiftValue(ci, c40, fnc1); + c40Values.push(shiftValue as u8); //Shift[123] + c40Values.push(Self::getC40Value(c40, shiftValue, ci, fnc1) as u8); + } else { + let asciiValue = ((ci as u8 & 0xff) - 128) as char; + if c40 && high_level_encoder::isNativeC40(asciiValue) + || !c40 && high_level_encoder::isNativeText(asciiValue) + { + c40Values.push(1); //Shift 2 + c40Values.push(30); //Upper Shift + c40Values.push(Self::getC40Value(c40, 0, asciiValue, fnc1) as u8); + } else { + c40Values.push(1); //Shift 2 + c40Values.push(30); //Upper Shift + let shiftValue = Self::getShiftValue(asciiValue, c40, fnc1); + c40Values.push(shiftValue as u8); // Shift[123] + c40Values.push(Self::getC40Value(c40, shiftValue, asciiValue, fnc1) as u8); + } + } + } + + if (c40Values.len() % 3) != 0 { + assert!( + (c40Values.len() - 2) % 3 == 0 + && fromPosition + self.characterLength as usize == self.input.length() + ); + c40Values.push(0); // pad with 0 (Shift 1) + } + + let mut result = vec![0u8; c40Values.len() / 3 * 2]; + let mut byteIndex = 0; + let mut i = 0; + while i < c40Values.len() { + // for (int i = 0; i < c40Values.size(); i += 3) { + Self::setC40Word( + &mut result, + byteIndex, + c40Values[i] as u32, + c40Values[i + 1] as u32, + c40Values[i + 2] as u32, + ); + byteIndex += 2; + + i += 3; + } + + Ok(result) + } + + pub fn getEDFBytes(&self) -> Result, Exceptions> { + let numberOfThirds = (self.characterLength as f32 / 4.0).ceil() as usize; + let mut result = vec![0u8; numberOfThirds * 3]; + let mut pos = self.fromPosition as usize; + let endPos = (self.fromPosition as usize + self.characterLength as usize - 1) + .min(self.input.length() - 1); + let mut i = 0; + while i < numberOfThirds { + // for (int i = 0; i < numberOfThirds; i += 3) { + let mut edfValues = [0u32; 4]; + for j in 0..4 { + // for (int j = 0; j < 4; j++) { + if pos <= endPos { + edfValues[j] = self.input.charAt(pos)? as u32 & 0x3f; + pos += 1; + } else { + edfValues[j] = if pos == endPos + 1 { 0x1f } else { 0 }; + } + } + let mut val24 = edfValues[0] << 18; + val24 |= edfValues[1] << 12; + val24 |= edfValues[2] << 6; + val24 |= edfValues[3]; + result[i] = ((val24 >> 16) & 0xff) as u8; + result[i + 1] = ((val24 >> 8) & 0xff) as u8; + result[i + 2] = (val24 & 0xff) as u8; + + i += 3; + } + + Ok(result) + } + + pub fn getLatchBytes(&self) -> Result, Exceptions> { + match Self::getPreviousMode(self.previous.clone())? { + Mode::ASCII | Mode::B256 => + //after B256 ends (via length) we are back to ASCII + { + match self.mode { + Mode::B256 => return Ok(Self::getBytes1(231)), + Mode::C40 => return Ok(Self::getBytes1(230)), + Mode::TEXT => return Ok(Self::getBytes1(239)), + Mode::X12 => return Ok(Self::getBytes1(238)), + Mode::EDF => return Ok(Self::getBytes1(240)), + _ => {} + } + } + Mode::C40 | Mode::TEXT | Mode::X12 + if self.mode != Self::getPreviousMode(self.previous.clone())? => + { + match self.mode { + Mode::ASCII => return Ok(Self::getBytes1(254)), + Mode::B256 => return Ok(Self::getBytes2(254, 231)), + Mode::C40 => return Ok(Self::getBytes2(254, 230)), + Mode::TEXT => return Ok(Self::getBytes2(254, 239)), + Mode::X12 => return Ok(Self::getBytes2(254, 238)), + Mode::EDF => return Ok(Self::getBytes2(254, 240)), + } + } + Mode::C40 | Mode::TEXT | Mode::X12 => {} + Mode::EDF => assert!(self.mode == Mode::EDF), //The rightmost EDIFACT edge always contains an unlatch character + } + + Ok(vec![0]) } // Important: The function does not return the length bytes (one or two) in case of B256 encoding - pub fn getDataBytes(&self) -> Vec { - switch (mode) { - case ASCII: - if (input.isECI(fromPosition)) { - return getBytes(241,input.getECIValue(fromPosition) + 1); - } else if (isExtendedASCII(input.charAt(fromPosition), input.getFNC1Character())) { - return getBytes(235,input.charAt(fromPosition) - 127); - } else if (characterLength == 2) { - return getBytes((input.charAt(fromPosition) - '0') * 10 + input.charAt(fromPosition + 1) - '0' + 130); - } else if (input.isFNC1(fromPosition)) { - return getBytes(232); - } else { - return getBytes(input.charAt(fromPosition) + 1); - } - case B256: - return getBytes(input.charAt(fromPosition)); - case C40: - return getC40Words(true, input.getFNC1Character()); - case TEXT: - return getC40Words(false, input.getFNC1Character()); - case X12: - return getX12Words(); - case EDF: - return getEDFBytes(); - } - assert false; - return new byte[0]; - } - } - - struct RXingResult { - - bytes:Vec, - } - impl RXingResult{ - - pub fn new( solution:Rc) -> Self { - let input = solution.input; - let size = 0; - let bytesAL = new ArrayList<>(); - let randomizePostfixLength = new ArrayList<>(); - let randomizeLengths = new ArrayList<>(); - if ((solution.mode == Mode::C40 || - solution.mode == Mode::TEXT || - solution.mode == Mode::X12) && - solution.getEndMode() != Mode::ASCII) { - size += prepend(Edge::getBytes(254),bytesAL); - } - let current = solution; - while (current != null) { - size += prepend(current.getDataBytes(),bytesAL); - - if (current.previous == null || current.getPreviousStartMode() != current.getMode()) { - if (current.getMode() == Mode::B256) { - if (size <= 249) { - bytesAL.add(0, (byte) size); - size++; - } else { - bytesAL.add(0, (byte) (size % 250)); - bytesAL.add(0, (byte) (size / 250 + 249)); - size += 2; + pub fn getDataBytes(&self) -> Result, Exceptions> { + match self.mode { + Mode::ASCII => { + if self.input.isECI(self.fromPosition)? { + return Ok(Self::getBytes2( + 241, + self.input.getECIValue(self.fromPosition as usize)? + 1, + )); + } else if isExtendedASCII( + self.input.charAt(self.fromPosition as usize)?, + Some(self.input.getFNC1Character()), + ) { + return Ok(Self::getBytes2( + 235, + self.input.charAt(self.fromPosition as usize)? as u32 - 127, + )); + } else if self.characterLength == 2 { + return Ok(Self::getBytes1( + (self.input.charAt(self.fromPosition as usize)? as u32 - b'0' as u32) * 10 + + self.input.charAt(self.fromPosition as usize + 1)? as u32 + - b'0' as u32 + + 130, + )); + } else if self.input.isFNC1(self.fromPosition as usize)? { + return Ok(Self::getBytes1(232)); + } else { + return Ok(Self::getBytes1( + self.input.charAt(self.fromPosition as usize)? as u32 + 1, + )); + } } - randomizePostfixLength.add(bytesAL.size()); - randomizeLengths.add(size); - } - prepend(current.getLatchBytes(), bytesAL); - size = 0; + Mode::B256 => { + return Ok(Self::getBytes1( + self.input.charAt(self.fromPosition as usize)? as u32, + )) + } + Mode::C40 => return self.getC40Words(true, Some(self.input.getFNC1Character())), + Mode::TEXT => return self.getC40Words(false, Some(self.input.getFNC1Character())), + Mode::X12 => return self.getX12Words(), + Mode::EDF => return self.getEDFBytes(), + } + // assert!( false); + // Ok(vec![0]) + } +} + +struct RXingResult { + bytes: Vec, +} +impl RXingResult { + pub fn new(solution: Option>) -> Result { + let solution = if let Some(edge) = solution { + edge + } else { + return Err(Exceptions::IllegalArgumentException("()".to_string())); + }; + let input = solution.input.clone(); + let mut size = 0; + let mut bytesAL = Vec::new(); //new ArrayList<>(); + let mut randomizePostfixLength = Vec::new(); //new ArrayList<>(); + let mut randomizeLengths = Vec::new(); //new ArrayList<>(); + if (solution.mode == Mode::C40 || solution.mode == Mode::TEXT || solution.mode == Mode::X12) + && solution.getEndMode()? != Mode::ASCII + { + size += Self::prepend(&Edge::getBytes1(254), &mut bytesAL); + } + let mut hold_current = Some(solution.clone()); + while let Some(current) = hold_current { + size += Self::prepend(¤t.getDataBytes()?, &mut bytesAL); + + if current.previous.is_none() + || Edge::getPreviousStartMode(current.previous.clone()) != current.getMode() + { + if current.getMode() == Mode::B256 { + if size <= 249 { + bytesAL.insert(0, size as u8); + size += 1; + } else { + bytesAL.insert(0, (size % 250) as u8); + bytesAL.insert(0, (size / 250 + 249) as u8); + size += 2; + } + randomizePostfixLength.push(bytesAL.len()); + randomizeLengths.push(size); + } + Self::prepend(¤t.getLatchBytes()?, &mut bytesAL); + size = 0; + } + + hold_current = current.previous.clone(); + } + if input.getMacroId() == 5 { + size += Self::prepend(&Edge::getBytes1(236), &mut bytesAL); + } else if input.getMacroId() == 6 { + size += Self::prepend(&Edge::getBytes1(237), &mut bytesAL); } - current = current.previous; - } - if (input.getMacroId() == 5) { - size += prepend(MinimalEncoder.Edge.getBytes(236), bytesAL); - } else if (input.getMacroId() == 6) { - size += prepend(MinimalEncoder.Edge.getBytes(237), bytesAL); - } - - if (input.getFNC1Character() > 0) { - size += prepend(MinimalEncoder.Edge.getBytes(232), bytesAL); - } - for (int i = 0; i < randomizePostfixLength.size(); i++) { - applyRandomPattern(bytesAL,bytesAL.size() - randomizePostfixLength.get(i), randomizeLengths.get(i)); - } - //add padding - int capacity = solution.getMinSymbolSize(bytesAL.size()); - if (bytesAL.size() < capacity) { - bytesAL.add((byte) 129); - } - while (bytesAL.size() < capacity) { - bytesAL.add((byte) randomize253State(bytesAL.size() + 1)); - } + if input.getFNC1Character() as u8 > 0 { + size += Self::prepend(&Edge::getBytes1(232), &mut bytesAL); + } + for i in 0..randomizePostfixLength.len() { + // for (int i = 0; i < randomizePostfixLength.size(); i++) { + let bytes_al_len = bytesAL.len() as u32; + Self::applyRandomPattern( + &mut bytesAL, + bytes_al_len - *randomizePostfixLength.get(i).unwrap() as u32, + *randomizeLengths.get(i).unwrap() as u32, + ); + } + //add padding + let capacity = solution.getMinSymbolSize(bytesAL.len() as u32); + if bytesAL.len() < capacity as usize { + bytesAL.push(129); + } + while bytesAL.len() < capacity as usize { + bytesAL.push(Self::randomize253State(bytesAL.len() as u32 + 1) as u8); + } - bytes = new byte[bytesAL.size()]; - for (int i = 0; i < bytes.length; i++) { - bytes[i] = bytesAL.get(i); - } + let mut bytes = vec![0u8; bytesAL.len()]; + for i in 0..bytes.len() { + // for (int i = 0; i < bytes.length; i++) { + bytes[i] = *bytesAL.get(i).unwrap(); + } + + Ok(Self { bytes }) } - pub fn prepend(bytes:&[u8], into:&[u8]) -> u32{ - for (int i = bytes.length - 1; i >= 0; i--) { - into.add(0, bytes[i]); - } - return bytes.length; + pub fn prepend(bytes: &[u8], into: &mut Vec) -> usize { + for i in (0..bytes.len()).rev() { + // for (int i = bytes.length - 1; i >= 0; i--) { + into.insert(0, bytes[i]); + } + bytes.len() } - fn randomize253State( codewordPosition:u32) -> u32{ - let pseudoRandom = ((149 * codewordPosition) % 253) + 1; - let tempVariable = 129 + pseudoRandom; - return tempVariable <= 254 ? tempVariable : tempVariable - 254; + fn randomize253State(codewordPosition: u32) -> u32 { + let pseudoRandom = ((149 * codewordPosition) % 253) + 1; + let tempVariable = 129 + pseudoRandom; + if tempVariable <= 254 { + tempVariable + } else { + tempVariable - 254 + } } - pub fn applyRandomPattern(bytesAL:&[u8], startPosition:u32, length:u32) { - for i in 0..length { - // for (int i = 0; i < length; i++) { - //See "B.1 253-state algorithm - let Pad_codeword_position = startPosition + i; - let Pad_codeword_value = bytesAL.get(Pad_codeword_position) & 0xff; - let pseudo_random_number = ((149 * (Pad_codeword_position + 1)) % 255) + 1; - let temp_variable = Pad_codeword_value + pseudo_random_number; - bytesAL.set(Pad_codeword_position, (byte) (temp_variable <= 255 ? temp_variable : temp_variable - 256)); - } + pub fn applyRandomPattern(bytesAL: &mut [u8], startPosition: u32, length: u32) { + for i in 0..length as usize { + // for (int i = 0; i < length; i++) { + //See "B.1 253-state algorithm + let Pad_codeword_position = startPosition as usize + i; + let Pad_codeword_value = bytesAL.get(Pad_codeword_position).expect("known to exist"); + let pseudo_random_number = ((149 * (Pad_codeword_position + 1)) % 255) + 1; + let temp_variable: u16 = *Pad_codeword_value as u16 + pseudo_random_number as u16; + bytesAL[Pad_codeword_position] = if temp_variable <= 255 { + temp_variable as u8 + } else { + (temp_variable - 256) as u8 + }; + } } pub fn getBytes(&self) -> &[u8] { - &self.bytes + &self.bytes } +} - } - - struct Input { +struct Input { shape: SymbolShapeHint, - macroId:i32, - internal: MinimalECIInput - } + macroId: i32, + internal: MinimalECIInput, +} - impl Input{ - - pub fn new( stringToEncode:&str, priorityCharset:Option, fnc1:Option, shape:SymbolShapeHint, macroId:i32) -> Self{ - Self { - shape, - macroId, - internal: MinimalECIInput::new(stringToEncode, priorityCharset, if fnc1 >= 0 {Some(&(fnc1 as u8 as char).to_string())} else {None}) - } - // super(stringToEncode, priorityCharset, fnc1); - // this.shape = shape; - // this.macroId = macroId; +impl Input { + pub fn new( + stringToEncode: &str, + priorityCharset: Option, + fnc1: Option, + shape: SymbolShapeHint, + macroId: i32, + ) -> Self { + let z = fnc1.unwrap_or_default().to_string(); + let v = if fnc1.is_some() { + Some(z.as_str()) + } else { + None + }; + Self { + shape, + macroId, + internal: MinimalECIInput::new(stringToEncode, priorityCharset, v), + } + // super(stringToEncode, priorityCharset, fnc1); + // this.shape = shape; + // this.macroId = macroId; } - pub fn getMacroId(&self) -> i32{ - self.macroId + pub fn getMacroId(&self) -> i32 { + self.macroId } - pub fn getShapeHint(&self) -> SymbolShapeHint{ - self.shape + pub fn getShapeHint(&self) -> SymbolShapeHint { + self.shape } pub fn length(&self) -> usize { - self.internal.length() - } - pub fn isECI(&self, index: u32) -> Result { - self.internal.isECI(index) -} -pub fn charAt(&self, index: usize) -> Result { - self.internal.charAt(index) -} -pub fn getFNC1Character(&self) -> char { - self.internal.getFNC1Character() as u8 as char - } - fn haveNCharacters(&self, index: usize, n: usize) -> bool { - self.internal.haveNCharacters(index, n) - } + self.internal.length() + } + pub fn isECI(&self, index: u32) -> Result { + self.internal.isECI(index) + } + pub fn charAt(&self, index: usize) -> Result { + self.internal.charAt(index) + } + pub fn getFNC1Character(&self) -> char { + self.internal.getFNC1Character() as u8 as char + } + fn haveNCharacters(&self, index: usize, n: usize) -> bool { + self.internal.haveNCharacters(index, n) + } + fn isFNC1(&self, index: usize) -> Result { + self.internal.isFNC1(index) + } + fn getECIValue(&self, index: usize) -> Result { + self.internal.getECIValue(index) + } } impl fmt::Display for Input { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.internal.fmt(f) } -} \ No newline at end of file +}