From e3c7af36f1cdf501eb3409dfe09b915debff0da4 Mon Sep 17 00:00:00 2001 From: Henry Schimke Date: Mon, 3 Oct 2022 16:03:28 -0500 Subject: [PATCH] encoder builds, no tests --- src/common/mod.rs | 1 + src/qrcode/decoder/data_block.rs | 12 +- src/qrcode/encoder/encoder.rs | 986 ++++++++------- src/qrcode/encoder/minimal_encoder.rs | 1653 +++++++++++++++---------- 4 files changed, 1506 insertions(+), 1146 deletions(-) diff --git a/src/common/mod.rs b/src/common/mod.rs index d004c66..92e0c57 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -2867,6 +2867,7 @@ impl fmt::Display for ECIStringBuilder { * * @author Alex Geller */ +#[derive(Clone)] pub struct ECIEncoderSet { encoders: Vec, priorityEncoderIndex: usize, diff --git a/src/qrcode/decoder/data_block.rs b/src/qrcode/decoder/data_block.rs index 97d8733..c9fd416 100755 --- a/src/qrcode/decoder/data_block.rs +++ b/src/qrcode/decoder/data_block.rs @@ -64,7 +64,7 @@ impl DataBlock { let ecBlocks = version.getECBlocksForLevel(ecLevel); // First count the total number of data blocks - let totalBlocks = 0; + let mut totalBlocks = 0; let ecBlockArray = ecBlocks.getECBlocks(); for ecBlock in ecBlockArray { // for (Version.ECB ecBlock : ecBlockArray) { @@ -72,11 +72,11 @@ impl DataBlock { } // Now establish DataBlocks of the appropriate size and number of data codewords - let result = Vec::new(); - let numRXingResultBlocks = 0; + let mut result = Vec::new(); + let mut numRXingResultBlocks = 0; for ecBlock in ecBlockArray { // for (Version.ECB ecBlock : ecBlockArray) { - for i in 0..ecBlock.getCount() { + for _i in 0..ecBlock.getCount() { // for (int i = 0; i < ecBlock.getCount(); i++) { let numDataCodewords = ecBlock.getDataCodewords(); let numBlockCodewords = ecBlocks.getECCodewordsPerBlock() + numDataCodewords; @@ -88,7 +88,7 @@ impl DataBlock { // All blocks have the same amount of data, except that the last n // (where n may be 0) have 1 more byte. Figure out where these start. let shorterBlocksTotalCodewords = result[0].codewords.len(); - let longerBlocksStartAt = result.len() - 1; + let mut longerBlocksStartAt = result.len() - 1; while (longerBlocksStartAt >= 0) { let numCodewords = result[longerBlocksStartAt].codewords.len(); if (numCodewords == shorterBlocksTotalCodewords) { @@ -101,7 +101,7 @@ impl DataBlock { let shorterBlocksNumDataCodewords = shorterBlocksTotalCodewords - ecBlocks.getECCodewordsPerBlock() as usize; // The last elements of result may be 1 element longer; // first fill out as many elements as all of them have - let rawCodewordsOffset = 0; + let mut rawCodewordsOffset = 0; for i in 0..shorterBlocksNumDataCodewords { // for (int i = 0; i < shorterBlocksNumDataCodewords; i++) { for j in 0..numRXingResultBlocks { diff --git a/src/qrcode/encoder/encoder.rs b/src/qrcode/encoder/encoder.rs index faec31e..ff33001 100644 --- a/src/qrcode/encoder/encoder.rs +++ b/src/qrcode/encoder/encoder.rs @@ -39,175 +39,198 @@ use encoding::EncodingRef; use lazy_static::lazy_static; -use crate::{EncodingHintDictionary, common::{BitArray, CharacterSetECI, reedsolomon::{ReedSolomonEncoder, get_predefined_genericgf, PredefinedGenericGF}, StringUtils}, Exceptions, qrcode::decoder::{ErrorCorrectionLevel, Mode, VersionRef, Version}, EncodeHintType, EncodeHintValue}; +use crate::{ + common::{ + reedsolomon::{get_predefined_genericgf, PredefinedGenericGF, ReedSolomonEncoder}, + BitArray, CharacterSetECI, StringUtils, + }, + qrcode::decoder::{ErrorCorrectionLevel, Mode, Version, VersionRef}, + EncodeHintType, EncodeHintValue, EncodingHintDictionary, Exceptions, +}; -use super::{mask_util, ByteMatrix, QRCode, matrix_util, MinimalEncoder, BlockPair}; +use super::{mask_util, matrix_util, BlockPair, ByteMatrix, MinimalEncoder, QRCode}; /** * @author satorux@google.com (Satoru Takabayashi) - creator * @author dswitkin@google.com (Daniel Switkin) - ported from C++ */ - lazy_static !{ -static ref SHIFT_JIS_CHARSET : EncodingRef = encoding::label::encoding_from_whatwg_label("SJIS").unwrap(); - } +lazy_static! { + static ref SHIFT_JIS_CHARSET: EncodingRef = + encoding::label::encoding_from_whatwg_label("SJIS").unwrap(); +} - // The original table is defined in the table 5 of JISX0510:2004 (p.19). - const ALPHANUMERIC_TABLE : [i8;96]= [ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x00-0x0f - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x10-0x1f - 36, -1, -1, -1, 37, 38, -1, -1, -1, -1, 39, 40, -1, 41, 42, 43, // 0x20-0x2f - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 44, -1, -1, -1, -1, -1, // 0x30-0x3f - -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, // 0x40-0x4f - 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, // 0x50-0x5f - ]; +// The original table is defined in the table 5 of JISX0510:2004 (p.19). +const ALPHANUMERIC_TABLE: [i8; 96] = [ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x00-0x0f + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x10-0x1f + 36, -1, -1, -1, 37, 38, -1, -1, -1, -1, 39, 40, -1, 41, 42, 43, // 0x20-0x2f + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 44, -1, -1, -1, -1, -1, // 0x30-0x3f + -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, // 0x40-0x4f + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, // 0x50-0x5f +]; - const DEFAULT_BYTE_MODE_ENCODING : EncodingRef = encoding::all::ISO_8859_1; +const DEFAULT_BYTE_MODE_ENCODING: EncodingRef = encoding::all::ISO_8859_1; - - // The mask penalty calculation is complicated. See Table 21 of JISX0510:2004 (p.45) for details. - // Basically it applies four rules and summate all penalties. - pub fn calculateMaskPenalty( matrix:&ByteMatrix) -> u32{ +// The mask penalty calculation is complicated. See Table 21 of JISX0510:2004 (p.45) for details. +// Basically it applies four rules and summate all penalties. +pub fn calculateMaskPenalty(matrix: &ByteMatrix) -> u32 { return mask_util::applyMaskPenaltyRule1(matrix) + mask_util::applyMaskPenaltyRule2(matrix) + mask_util::applyMaskPenaltyRule3(matrix) + mask_util::applyMaskPenaltyRule4(matrix); - } +} - /** - * @param content text to encode - * @param ecLevel error correction level to use - * @return {@link QRCode} representing the encoded QR code - * @throws WriterException if encoding can't succeed, because of for example invalid content - * or configuration - */ - pub fn encode( content:&str, ecLevel:ErrorCorrectionLevel) -> Result { +/** + * @param content text to encode + * @param ecLevel error correction level to use + * @return {@link QRCode} representing the encoded QR code + * @throws WriterException if encoding can't succeed, because of for example invalid content + * or configuration + */ +pub fn encode(content: &str, ecLevel: ErrorCorrectionLevel) -> Result { return encode_with_hints(content, ecLevel, HashMap::new()); - } - - pub fn encode_with_hints( content:&str, - ecLevel:ErrorCorrectionLevel, - hints:EncodingHintDictionary) -> Result { +} +pub fn encode_with_hints( + content: &str, + ecLevel: ErrorCorrectionLevel, + hints: EncodingHintDictionary, +) -> Result { let version; - let headerAndDataBits; + let mut headerAndDataBits; let mode; - let hasGS1FormatHint = hints.contains_key(&EncodeHintType::GS1_FORMAT) && - if let EncodeHintValue::Gs1Format(v) = hints.get(&EncodeHintType::GS1_FORMAT).unwrap() { - if let Ok(vb) = v.parse::() { - vb - }else { - false - } - }else { - false - }; - let hasCompactionHint = hints.contains_key(&EncodeHintType::QR_COMPACT) && - if let EncodeHintValue::QrCompact(v) = hints.get(&EncodeHintType::QR_COMPACT).unwrap() { - if let Ok(vb) = v.parse::() { - vb - }else { - false - } - }else { - false - }; + let hasGS1FormatHint = hints.contains_key(&EncodeHintType::GS1_FORMAT) + && if let EncodeHintValue::Gs1Format(v) = hints.get(&EncodeHintType::GS1_FORMAT).unwrap() { + if let Ok(vb) = v.parse::() { + vb + } else { + false + } + } else { + false + }; + let hasCompactionHint = hints.contains_key(&EncodeHintType::QR_COMPACT) + && if let EncodeHintValue::QrCompact(v) = hints.get(&EncodeHintType::QR_COMPACT).unwrap() { + if let Ok(vb) = v.parse::() { + vb + } else { + false + } + } else { + false + }; // Determine what character encoding has been specified by the caller, if any - let encoding = DEFAULT_BYTE_MODE_ENCODING; + let mut encoding = DEFAULT_BYTE_MODE_ENCODING; let hasEncodingHint = hints.contains_key(&EncodeHintType::CHARACTER_SET); if hasEncodingHint { - if let EncodeHintValue::CharacterSet(v) = hints.get(&EncodeHintType::CHARACTER_SET).unwrap() { - encoding = encoding::label::encoding_from_whatwg_label(v).unwrap() - } - // encoding = encoding::label::encoding_from_whatwg_label(hints.get(&EncodeHintType::CHARACTER_SET).unwrap()); + if let EncodeHintValue::CharacterSet(v) = hints.get(&EncodeHintType::CHARACTER_SET).unwrap() + { + encoding = encoding::label::encoding_from_whatwg_label(v).unwrap() + } + // encoding = encoding::label::encoding_from_whatwg_label(hints.get(&EncodeHintType::CHARACTER_SET).unwrap()); } if hasCompactionHint { - mode = Mode::BYTE; + mode = Mode::BYTE; - let priorityEncoding = encoding; //if encoding.name() == DEFAULT_BYTE_MODE_ENCODING.name() {None} else {Some(encoding)}; - let rn = MinimalEncoder::encode_with_details(content, None, priorityEncoding, hasGS1FormatHint, ecLevel)?; - - headerAndDataBits = BitArray::new(); - rn.getBits(&headerAndDataBits); - version = rn.getVersion(); + let priorityEncoding = encoding; //if encoding.name() == DEFAULT_BYTE_MODE_ENCODING.name() {None} else {Some(encoding)}; + let rn = MinimalEncoder::encode_with_details( + content, + None, + priorityEncoding, + hasGS1FormatHint, + ecLevel, + )?; + headerAndDataBits = BitArray::new(); + rn.getBits(&mut headerAndDataBits); + version = rn.getVersion(); } else { - - // Pick an encoding mode appropriate for the content. Note that this will not attempt to use - // multiple modes / segments even if that were more efficient. - mode = chooseModeWithEncoding(content, encoding); - - // This will store the header information, like mode and - // length, as well as "header" segments like an ECI segment. - let headerBits = BitArray::new(); - - // Append ECI segment if applicable - if mode == Mode::BYTE && hasEncodingHint { - let eci = CharacterSetECI::getCharacterSetECI(encoding); - if eci.is_some() { - appendECI(&eci.unwrap(), &headerBits); + // Pick an encoding mode appropriate for the content. Note that this will not attempt to use + // multiple modes / segments even if that were more efficient. + mode = chooseModeWithEncoding(content, encoding); + + // This will store the header information, like mode and + // length, as well as "header" segments like an ECI segment. + let mut headerBits = BitArray::new(); + + // Append ECI segment if applicable + if mode == Mode::BYTE && hasEncodingHint { + let eci = CharacterSetECI::getCharacterSetECI(encoding); + if eci.is_some() { + appendECI(&eci.unwrap(), &mut headerBits); + } } - } - - // Append the FNC1 mode header for GS1 formatted data if applicable - if hasGS1FormatHint { - // GS1 formatted codes are prefixed with a FNC1 in first position mode header - appendModeInfo(Mode::FNC1_FIRST_POSITION, &headerBits); - } - - // (With ECI in place,) Write the mode marker - appendModeInfo(mode, &headerBits); - - // Collect data within the main segment, separately, to count its size if needed. Don't add it to - // main payload yet. - let dataBits = BitArray::new(); - appendBytes(content, mode, &dataBits, encoding); - - if hints.contains_key(&EncodeHintType::QR_VERSION) { - let versionNumber = if let EncodeHintValue::QrVersion(v) = hints.get(&EncodeHintType::QR_VERSION).unwrap() { - if let Ok(vb) = v.parse::() { - vb - }else { - 0 - } - }else { - 0 + + // Append the FNC1 mode header for GS1 formatted data if applicable + if hasGS1FormatHint { + // GS1 formatted codes are prefixed with a FNC1 in first position mode header + appendModeInfo(Mode::FNC1_FIRST_POSITION, &mut headerBits); + } + + // (With ECI in place,) Write the mode marker + appendModeInfo(mode, &mut headerBits); + + // Collect data within the main segment, separately, to count its size if needed. Don't add it to + // main payload yet. + let mut dataBits = BitArray::new(); + appendBytes(content, mode, &mut dataBits, encoding); + + if hints.contains_key(&EncodeHintType::QR_VERSION) { + let versionNumber = if let EncodeHintValue::QrVersion(v) = + hints.get(&EncodeHintType::QR_VERSION).unwrap() + { + if let Ok(vb) = v.parse::() { + vb + } else { + 0 + } + } else { + 0 + }; + // let versionNumber = Integer.parseInt(hints.get(&EncodeHintType::QR_VERSION).unwrap()()); + version = Version::getVersionForNumber(versionNumber)?; + let bitsNeeded = calculateBitsNeeded(mode, &headerBits, &dataBits, version); + if !willFit(bitsNeeded, version, &ecLevel) { + return Err(Exceptions::WriterException( + "Data too big for requested version".to_owned(), + )); + } + } else { + version = recommendVersion(&ecLevel, mode, &headerBits, &dataBits)?; + } + + headerAndDataBits = BitArray::new(); + headerAndDataBits.appendBitArray(headerBits); + // Find "length" of main segment and write it + let numLetters = if mode == Mode::BYTE { + dataBits.getSizeInBytes() + } else { + content.len() }; - // let versionNumber = Integer.parseInt(hints.get(&EncodeHintType::QR_VERSION).unwrap()()); - version = Version::getVersionForNumber(versionNumber)?; - let bitsNeeded = calculateBitsNeeded(mode, &headerBits, &dataBits, version); - if !willFit(bitsNeeded, version, &ecLevel) { - return Err(Exceptions::WriterException("Data too big for requested version".to_owned())) - } - } else { - version = recommendVersion(&ecLevel, mode, &headerBits, &dataBits)?; - } - - headerAndDataBits = BitArray::new(); - headerAndDataBits.appendBitArray(headerBits); - // Find "length" of main segment and write it - let numLetters = if mode == Mode::BYTE {dataBits.getSizeInBytes() }else {content.len()}; - appendLengthInfo(numLetters as u32, version, mode, &headerAndDataBits); - // Put data together into the overall payload - headerAndDataBits.appendBitArray(dataBits); + appendLengthInfo(numLetters as u32, version, mode, &mut headerAndDataBits); + // Put data together into the overall payload + headerAndDataBits.appendBitArray(dataBits); } let ecBlocks = version.getECBlocksForLevel(ecLevel); let numDataBytes = version.getTotalCodewords() - ecBlocks.getTotalECCodewords(); // Terminate the bits properly. - terminateBits(numDataBytes, &headerAndDataBits); + terminateBits(numDataBytes, &mut headerAndDataBits); // Interleave data bits with error correction code. - let finalBits = interleaveWithECBytes(&headerAndDataBits, - version.getTotalCodewords(), - numDataBytes, - ecBlocks.getNumBlocks())?; + let finalBits = interleaveWithECBytes( + &headerAndDataBits, + version.getTotalCodewords(), + numDataBytes, + ecBlocks.getNumBlocks(), + )?; - let qrCode = QRCode::new(); + let mut qrCode = QRCode::new(); qrCode.setECLevel(ecLevel); qrCode.setMode(mode); @@ -215,26 +238,32 @@ static ref SHIFT_JIS_CHARSET : EncodingRef = encoding::label::encoding_from_what // Choose the mask pattern and set to "qrCode". let dimension = version.getDimensionForVersion(); - let matrix = ByteMatrix::new(dimension, dimension); + let mut matrix = ByteMatrix::new(dimension, dimension); // Enable manual selection of the pattern to be used via hint - let maskPattern = -1; + let mut maskPattern = -1; if hints.contains_key(&EncodeHintType::QR_MASK_PATTERN) { - let hintMaskPattern =if let EncodeHintValue::QrMaskPattern(v) = hints.get(&EncodeHintType::QR_MASK_PATTERN).unwrap() { - if let Ok(vb) = v.parse::() { - vb - }else { - -1 - } - }else { - -1 - }; - // let hintMaskPattern = Integer.parseInt(hints.get(&EncodeHintType::QR_MASK_PATTERN).unwrap()); - maskPattern = if QRCode::isValidMaskPattern(hintMaskPattern) {hintMaskPattern} else {-1}; + let hintMaskPattern = if let EncodeHintValue::QrMaskPattern(v) = + hints.get(&EncodeHintType::QR_MASK_PATTERN).unwrap() + { + if let Ok(vb) = v.parse::() { + vb + } else { + -1 + } + } else { + -1 + }; + // let hintMaskPattern = Integer.parseInt(hints.get(&EncodeHintType::QR_MASK_PATTERN).unwrap()); + maskPattern = if QRCode::isValidMaskPattern(hintMaskPattern) { + hintMaskPattern + } else { + -1 + }; } if maskPattern == -1 { - maskPattern = chooseMaskPattern(&finalBits, &ecLevel, version, &matrix)? as i32; + maskPattern = chooseMaskPattern(&finalBits, &ecLevel, version, &mut matrix)? as i32; } qrCode.setMaskPattern(maskPattern); @@ -242,139 +271,151 @@ static ref SHIFT_JIS_CHARSET : EncodingRef = encoding::label::encoding_from_what matrix_util::buildMatrix(&finalBits, &ecLevel, version, maskPattern, &mut matrix); qrCode.setMatrix(matrix); - Ok( qrCode) - } + Ok(qrCode) +} - /** - * Decides the smallest version of QR code that will contain all of the provided data. - * - * @throws WriterException if the data cannot fit in any version - */ - fn recommendVersion( ecLevel:&ErrorCorrectionLevel, - mode:Mode, - headerBits:&BitArray, - dataBits:&BitArray) -> Result { +/** + * Decides the smallest version of QR code that will contain all of the provided data. + * + * @throws WriterException if the data cannot fit in any version + */ +fn recommendVersion( + ecLevel: &ErrorCorrectionLevel, + mode: Mode, + headerBits: &BitArray, + dataBits: &BitArray, +) -> Result { // Hard part: need to know version to know how many bits length takes. But need to know how many // bits it takes to know version. First we take a guess at version by assuming version will be // the minimum, 1: - let provisionalBitsNeeded = calculateBitsNeeded(mode, headerBits, dataBits, Version::getVersionForNumber(1)?); + let provisionalBitsNeeded = + calculateBitsNeeded(mode, headerBits, dataBits, Version::getVersionForNumber(1)?); let provisionalVersion = chooseVersion(provisionalBitsNeeded, ecLevel)?; // Use that guess to calculate the right version. I am still not sure this works in 100% of cases. let bitsNeeded = calculateBitsNeeded(mode, headerBits, dataBits, provisionalVersion); return chooseVersion(bitsNeeded, ecLevel); - } +} - fn calculateBitsNeeded( mode:Mode, - headerBits:&BitArray, - dataBits:&BitArray, - version:VersionRef) -> u32 { - (headerBits.getSize() + mode.getCharacterCountBits(version) as usize + dataBits.getSize()) as u32 - } +fn calculateBitsNeeded( + mode: Mode, + headerBits: &BitArray, + dataBits: &BitArray, + version: VersionRef, +) -> u32 { + (headerBits.getSize() + mode.getCharacterCountBits(version) as usize + dataBits.getSize()) + as u32 +} - /** - * @return the code point of the table used in alphanumeric mode or - * -1 if there is no corresponding code in the table. - */ - pub fn getAlphanumericCode( code:u32) -> i8{ +/** + * @return the code point of the table used in alphanumeric mode or + * -1 if there is no corresponding code in the table. + */ +pub fn getAlphanumericCode(code: u32) -> i8 { let code = code as usize; if code < ALPHANUMERIC_TABLE.len() { - ALPHANUMERIC_TABLE[code] - }else{ - -1 + ALPHANUMERIC_TABLE[code] + } else { + -1 } - } +} - pub fn chooseMode( content:&str) -> Mode{ +pub fn chooseMode(content: &str) -> Mode { return chooseModeWithEncoding(content, encoding::all::ISO_8859_1); - } +} - /** - * Choose the best mode by examining the content. Note that 'encoding' is used as a hint; - * if it is Shift_JIS, and the input is only double-byte Kanji, then we return {@link Mode#KANJI}. - */ - fn chooseModeWithEncoding( content:&str, encoding:EncodingRef) -> Mode{ +/** + * Choose the best mode by examining the content. Note that 'encoding' is used as a hint; + * if it is Shift_JIS, and the input is only double-byte Kanji, then we return {@link Mode#KANJI}. + */ +fn chooseModeWithEncoding(content: &str, encoding: EncodingRef) -> Mode { if SHIFT_JIS_CHARSET.name() == encoding.name() && isOnlyDoubleByteKanji(content) { - // if (StringUtils.SHIFT_JIS_CHARSET.equals(encoding) && isOnlyDoubleByteKanji(content)) { - // Choose Kanji mode if all input are double-byte characters - return Mode::KANJI; + // if (StringUtils.SHIFT_JIS_CHARSET.equals(encoding) && isOnlyDoubleByteKanji(content)) { + // Choose Kanji mode if all input are double-byte characters + return Mode::KANJI; } - let hasNumeric = false; - let hasAlphanumeric = false; + let mut hasNumeric = false; + let mut hasAlphanumeric = false; for i in 0..content.len() { - // for (int i = 0; i < content.length(); ++i) { - let c = content.chars().nth(i).unwrap(); - if c >= '0' && c <= '9' { - hasNumeric = true; - } else if (getAlphanumericCode(c as u32) != -1) { - hasAlphanumeric = true; - } else { - return Mode::BYTE; - } + // for (int i = 0; i < content.length(); ++i) { + let c = content.chars().nth(i).unwrap(); + if c >= '0' && c <= '9' { + hasNumeric = true; + } else if (getAlphanumericCode(c as u32) != -1) { + hasAlphanumeric = true; + } else { + return Mode::BYTE; + } } if (hasAlphanumeric) { - return Mode::ALPHANUMERIC; + return Mode::ALPHANUMERIC; } if (hasNumeric) { - return Mode::NUMERIC; + return Mode::NUMERIC; } return Mode::BYTE; - } +} - pub fn isOnlyDoubleByteKanji( content:&str) -> bool{ - let bytes = SHIFT_JIS_CHARSET.encode(content,encoding::EncoderTrap::Strict).expect("encode"); +pub fn isOnlyDoubleByteKanji(content: &str) -> bool { + let bytes = SHIFT_JIS_CHARSET + .encode(content, encoding::EncoderTrap::Strict) + .expect("encode"); let length = bytes.len(); if length % 2 != 0 { - return false; + return false; } let mut i = 0; while i < length { - // for (int i = 0; i < length; i += 2) { - let byte1 = bytes[i] & 0xFF; - if (byte1 < 0x81 || byte1 > 0x9F) && (byte1 < 0xE0 || byte1 > 0xEB) { - return false; - } - i+=2; + // for (int i = 0; i < length; i += 2) { + let byte1 = bytes[i] & 0xFF; + if (byte1 < 0x81 || byte1 > 0x9F) && (byte1 < 0xE0 || byte1 > 0xEB) { + return false; + } + i += 2; } return true; - } +} - fn chooseMaskPattern( bits:&BitArray, - ecLevel:&ErrorCorrectionLevel, - version:VersionRef, - matrix:&ByteMatrix) -> Result { - - let minPenalty = u32::MAX; // Lower penalty is better. - let bestMaskPattern = -1; +fn chooseMaskPattern( + bits: &BitArray, + ecLevel: &ErrorCorrectionLevel, + version: VersionRef, + matrix: &mut ByteMatrix, +) -> Result { + let mut minPenalty = u32::MAX; // Lower penalty is better. + let mut bestMaskPattern = -1; // We try all mask patterns to choose the best one. for maskPattern in 0..QRCode::NUM_MASK_PATTERNS { - // for (int maskPattern = 0; maskPattern < QRCode.NUM_MASK_PATTERNS; maskPattern++) { - matrix_util::buildMatrix(bits, ecLevel, version, maskPattern, &mut matrix); - let penalty = calculateMaskPenalty(matrix); - if penalty < minPenalty { - minPenalty = penalty; - bestMaskPattern = maskPattern; - } + // for (int maskPattern = 0; maskPattern < QRCode.NUM_MASK_PATTERNS; maskPattern++) { + matrix_util::buildMatrix(bits, ecLevel, version, maskPattern, matrix); + let penalty = calculateMaskPenalty(matrix); + if penalty < minPenalty { + minPenalty = penalty; + bestMaskPattern = maskPattern; + } } - Ok( bestMaskPattern as u32 ) - } + Ok(bestMaskPattern as u32) +} - fn chooseVersion( numInputBits:u32, ecLevel:&ErrorCorrectionLevel) -> Result { +fn chooseVersion( + numInputBits: u32, + ecLevel: &ErrorCorrectionLevel, +) -> Result { for versionNum in 1..=40 { - // for (int versionNum = 1; versionNum <= 40; versionNum++) { - let version = Version::getVersionForNumber(versionNum)?; - if willFit(numInputBits, version, ecLevel) { - return Ok(version); - } + // for (int versionNum = 1; versionNum <= 40; versionNum++) { + let version = Version::getVersionForNumber(versionNum)?; + if willFit(numInputBits, version, ecLevel) { + return Ok(version); + } } Err(Exceptions::WriterException("Data too big".to_owned())) - } +} - /** - * @return true if the number of input bits will fit in a code with the specified version and - * error correction level. - */ - pub fn willFit( numInputBits:u32, version:VersionRef, ecLevel:&ErrorCorrectionLevel) -> bool { +/** + * @return true if the number of input bits will fit in a code with the specified version and + * error correction level. + */ +pub fn willFit(numInputBits: u32, version: VersionRef, ecLevel: &ErrorCorrectionLevel) -> bool { // In the following comments, we use numbers of Version 7-H. // numBytes = 196 let numBytes = version.getTotalCodewords(); @@ -385,62 +426,70 @@ static ref SHIFT_JIS_CHARSET : EncodingRef = encoding::label::encoding_from_what let numDataBytes = numBytes - numEcBytes; let totalInputBytes = (numInputBits + 7) / 8; return numDataBytes >= totalInputBytes; - } +} - /** - * Terminate bits as described in 8.4.8 and 8.4.9 of JISX0510:2004 (p.24). - */ - pub fn terminateBits( numDataBytes:u32, bits:&BitArray) -> Result<(),Exceptions> { +/** + * Terminate bits as described in 8.4.8 and 8.4.9 of JISX0510:2004 (p.24). + */ +pub fn terminateBits(numDataBytes: u32, bits: &mut BitArray) -> Result<(), Exceptions> { let capacity = numDataBytes * 8; if bits.getSize() > capacity as usize { - return Err(Exceptions::WriterException(format!("data bits cannot fit in the QR Code{} > " , - capacity))) - // throw new WriterException("data bits cannot fit in the QR Code" + bits.getSize() + " > " + - // capacity); + return Err(Exceptions::WriterException(format!( + "data bits cannot fit in the QR Code{} > ", + capacity + ))); + // throw new WriterException("data bits cannot fit in the QR Code" + bits.getSize() + " > " + + // capacity); } // Append Mode.TERMINATE if there is enough space (value is 0000) for i in 0..4 { - if bits.getSize() > 0 { break } - // } - // for (int i = 0; i < 4 && bits.getSize() < capacity; ++i) { - bits.appendBit(false); + if bits.getSize() > 0 { + break; + } + // } + // for (int i = 0; i < 4 && bits.getSize() < capacity; ++i) { + bits.appendBit(false); } // Append termination bits. See 8.4.8 of JISX0510:2004 (p.24) for details. // If the last byte isn't 8-bit aligned, we'll add padding bits. let numBitsInLastByte = bits.getSize() & 0x07; if numBitsInLastByte > 0 { - for i in numBitsInLastByte..8 { - // for (int i = numBitsInLastByte; i < 8; i++) { - bits.appendBit(false); - } + for i in numBitsInLastByte..8 { + // for (int i = numBitsInLastByte; i < 8; i++) { + bits.appendBit(false); + } } // If we have more space, we'll fill the space with padding patterns defined in 8.4.9 (p.24). let numPaddingBytes = numDataBytes as usize - bits.getSizeInBytes(); for i in 0..numPaddingBytes { - // for (int i = 0; i < numPaddingBytes; ++i) { - bits.appendBits(if (i & 0x01) == 0 {0xEC} else {0x11}, 8); + // for (int i = 0; i < numPaddingBytes; ++i) { + bits.appendBits(if (i & 0x01) == 0 { 0xEC } else { 0x11 }, 8); } if bits.getSize() != capacity as usize { - return Err(Exceptions::WriterException("Bits size does not equal capacity".to_owned())) - // throw new WriterException("Bits size does not equal capacity"); + return Err(Exceptions::WriterException( + "Bits size does not equal capacity".to_owned(), + )); + // throw new WriterException("Bits size does not equal capacity"); } Ok(()) - } +} - /** - * Get number of data bytes and number of error correction bytes for block id "blockID". Store - * the result in "numDataBytesInBlock", and "numECBytesInBlock". See table 12 in 8.5.1 of - * JISX0510:2004 (p.30) - */ - pub fn getNumDataBytesAndNumECBytesForBlockID( numTotalBytes:u32, - numDataBytes:u32, - numRSBlocks:u32, - blockID:u32, - numDataBytesInBlock:&[u32], - numECBytesInBlock:&[u32]) -> Result<(),Exceptions> { +/** + * Get number of data bytes and number of error correction bytes for block id "blockID". Store + * the result in "numDataBytesInBlock", and "numECBytesInBlock". See table 12 in 8.5.1 of + * JISX0510:2004 (p.30) + */ +pub fn getNumDataBytesAndNumECBytesForBlockID( + numTotalBytes: u32, + numDataBytes: u32, + numRSBlocks: u32, + blockID: u32, + numDataBytesInBlock: &mut [u32], + numECBytesInBlock: &mut [u32], +) -> Result<(), Exceptions> { if blockID >= numRSBlocks { - return Err(Exceptions::WriterException("Block ID too large".to_owned())) - // throw new WriterException("Block ID too large"); + return Err(Exceptions::WriterException("Block ID too large".to_owned())); + // throw new WriterException("Block ID too large"); } // numRsBlocksInGroup2 = 196 % 5 = 1 let numRsBlocksInGroup2 = numTotalBytes % numRSBlocks; @@ -461,174 +510,201 @@ static ref SHIFT_JIS_CHARSET : EncodingRef = encoding::label::encoding_from_what // Sanity checks. // 26 = 26 if (numEcBytesInGroup1 != numEcBytesInGroup2) { - return Err(Exceptions::WriterException("EC bytes mismatch".to_owned())) - // throw new WriterException("EC bytes mismatch"); + return Err(Exceptions::WriterException("EC bytes mismatch".to_owned())); + // throw new WriterException("EC bytes mismatch"); } // 5 = 4 + 1. if (numRSBlocks != numRsBlocksInGroup1 + numRsBlocksInGroup2) { - return Err(Exceptions::WriterException("RS blocks mismatch".to_owned())) + return Err(Exceptions::WriterException("RS blocks mismatch".to_owned())); - // throw new WriterException("RS blocks mismatch"); + // throw new WriterException("RS blocks mismatch"); } // 196 = (13 + 26) * 4 + (14 + 26) * 1 - if (numTotalBytes != - ((numDataBytesInGroup1 + numEcBytesInGroup1) * - numRsBlocksInGroup1) + - ((numDataBytesInGroup2 + numEcBytesInGroup2) * - numRsBlocksInGroup2)) { - return Err(Exceptions::WriterException("Total bytes mismatch".to_owned())) - - // throw new WriterException("Total bytes mismatch"); + if (numTotalBytes + != ((numDataBytesInGroup1 + numEcBytesInGroup1) * numRsBlocksInGroup1) + + ((numDataBytesInGroup2 + numEcBytesInGroup2) * numRsBlocksInGroup2)) + { + return Err(Exceptions::WriterException( + "Total bytes mismatch".to_owned(), + )); + + // throw new WriterException("Total bytes mismatch"); } if (blockID < numRsBlocksInGroup1) { - numDataBytesInBlock[0] = numDataBytesInGroup1; - numECBytesInBlock[0] = numEcBytesInGroup1; + numDataBytesInBlock[0] = numDataBytesInGroup1; + numECBytesInBlock[0] = numEcBytesInGroup1; } else { - numDataBytesInBlock[0] = numDataBytesInGroup2; - numECBytesInBlock[0] = numEcBytesInGroup2; + numDataBytesInBlock[0] = numDataBytesInGroup2; + numECBytesInBlock[0] = numEcBytesInGroup2; } Ok(()) - } - - /** - * Interleave "bits" with corresponding error correction bytes. On success, store the result in - * "result". The interleave rule is complicated. See 8.6 of JISX0510:2004 (p.37) for details. - */ - pub fn interleaveWithECBytes( bits:&BitArray, - numTotalBytes:u32, - numDataBytes:u32, - numRSBlocks:u32) -> Result { +} +/** + * Interleave "bits" with corresponding error correction bytes. On success, store the result in + * "result". The interleave rule is complicated. See 8.6 of JISX0510:2004 (p.37) for details. + */ +pub fn interleaveWithECBytes( + bits: &BitArray, + numTotalBytes: u32, + numDataBytes: u32, + numRSBlocks: u32, +) -> Result { // "bits" must have "getNumDataBytes" bytes of data. if bits.getSizeInBytes() as u32 != numDataBytes { - return Err(Exceptions::WriterException("Number of bits and data bytes does not match".to_owned())) + return Err(Exceptions::WriterException( + "Number of bits and data bytes does not match".to_owned(), + )); } // Step 1. Divide data bytes into blocks and generate error correction bytes for them. We'll // store the divided data bytes blocks and error correction bytes blocks into "blocks". - let dataBytesOffset = 0; - let maxNumDataBytes = 0; - let maxNumEcBytes = 0; + let mut dataBytesOffset = 0; + let mut maxNumDataBytes = 0; + let mut maxNumEcBytes = 0; // Since, we know the number of reedsolmon blocks, we can initialize the vector with the number. - let blocks = Vec::new(); + let mut blocks = Vec::new(); for i in 0..numRSBlocks { - // for (int i = 0; i < numRSBlocks; ++i) { - let numDataBytesInBlock = vec![0;1];//new int[1]; - let numEcBytesInBlock = vec![0;1];//new int[1]; - getNumDataBytesAndNumECBytesForBlockID( - numTotalBytes, numDataBytes, numRSBlocks, i, - &numDataBytesInBlock, &numEcBytesInBlock); + // for (int i = 0; i < numRSBlocks; ++i) { + let mut numDataBytesInBlock = vec![0; 1]; //new int[1]; + let mut numEcBytesInBlock = vec![0; 1]; //new int[1]; + getNumDataBytesAndNumECBytesForBlockID( + numTotalBytes, + numDataBytes, + numRSBlocks, + i, + &mut numDataBytesInBlock, + &mut numEcBytesInBlock, + ); - let size = numDataBytesInBlock[0]; - let dataBytes = vec![0u8;size as usize]; - bits.toBytes(8 * dataBytesOffset, &mut dataBytes, 0, size as usize); - let ecBytes = generateECBytes(&dataBytes, numEcBytesInBlock[0] as usize); - blocks.push( BlockPair::new(dataBytes, ecBytes)); + let size = numDataBytesInBlock[0]; + let mut dataBytes = vec![0u8; size as usize]; + bits.toBytes(8 * dataBytesOffset, &mut dataBytes, 0, size as usize); + let ecBytes = generateECBytes(&dataBytes, numEcBytesInBlock[0] as usize); + blocks.push(BlockPair::new(dataBytes, ecBytes.clone())); - maxNumDataBytes = maxNumDataBytes.max( size); - maxNumEcBytes = maxNumEcBytes.max( ecBytes.len()); - dataBytesOffset += numDataBytesInBlock[0] as usize; + maxNumDataBytes = maxNumDataBytes.max(size); + maxNumEcBytes = maxNumEcBytes.max(ecBytes.len()); + dataBytesOffset += numDataBytesInBlock[0] as usize; } if numDataBytes != dataBytesOffset as u32 { - return Err(Exceptions::WriterException("Data bytes does not match offset".to_owned())) + return Err(Exceptions::WriterException( + "Data bytes does not match offset".to_owned(), + )); } - let result = BitArray::new(); + let mut result = BitArray::new(); // First, place data blocks. - for i in 0..maxNumDataBytes as usize{ - // for (int i = 0; i < maxNumDataBytes; ++i) { - for block in blocks { - // for (BlockPair block : blocks) { - let dataBytes = block.getDataBytes(); - if i < dataBytes.len() { - result.appendBits(dataBytes[i] as u32, 8); + for i in 0..maxNumDataBytes as usize { + // for (int i = 0; i < maxNumDataBytes; ++i) { + for block in &blocks { + // for (BlockPair block : blocks) { + let dataBytes = block.getDataBytes(); + if i < dataBytes.len() { + result.appendBits(dataBytes[i] as u32, 8); + } } - } } // Then, place error correction blocks. for i in 0..maxNumEcBytes { - // for (int i = 0; i < maxNumEcBytes; ++i) { - for block in blocks { - // for (BlockPair block : blocks) { - let ecBytes = block.getErrorCorrectionBytes(); - if (i < ecBytes.len()) { - result.appendBits(ecBytes[i] as u32, 8); + // for (int i = 0; i < maxNumEcBytes; ++i) { + for block in &blocks { + // for (BlockPair block : blocks) { + let ecBytes = block.getErrorCorrectionBytes(); + if (i < ecBytes.len()) { + result.appendBits(ecBytes[i] as u32, 8); + } } - } } - if (numTotalBytes != result.getSizeInBytes() as u32) { // Should be same. - return Err( - Exceptions::WriterException( - format!( + if (numTotalBytes != result.getSizeInBytes() as u32) { + // Should be same. + return Err(Exceptions::WriterException(format!( "Interleaving error: {} and {} differ.", - numTotalBytes,result.getSizeInBytes() - ) - ) - ) - // throw new WriterException("Interleaving error: " + numTotalBytes + " and " + - // result.getSizeInBytes() + " differ."); + numTotalBytes, + result.getSizeInBytes() + ))); + // throw new WriterException("Interleaving error: " + numTotalBytes + " and " + + // result.getSizeInBytes() + " differ."); } Ok(result) - } +} - pub fn generateECBytes( dataBytes:&[u8], numEcBytesInBlock:usize) -> Vec { +pub fn generateECBytes(dataBytes: &[u8], numEcBytesInBlock: usize) -> Vec { let numDataBytes = dataBytes.len(); - let toEncode = vec![0;numDataBytes + numEcBytesInBlock]; + let mut toEncode = vec![0; numDataBytes + numEcBytesInBlock]; for i in 0..numDataBytes { - // for (int i = 0; i < numDataBytes; i++) { - toEncode[i] = dataBytes[i] as i32; + // for (int i = 0; i < numDataBytes; i++) { + toEncode[i] = dataBytes[i] as i32; } - - ReedSolomonEncoder::new(get_predefined_genericgf(PredefinedGenericGF::QrCodeField256)).encode(&mut toEncode, numEcBytesInBlock); - let ecBytes = vec![0u8;numEcBytesInBlock]; + ReedSolomonEncoder::new(get_predefined_genericgf( + PredefinedGenericGF::QrCodeField256, + )) + .encode(&mut toEncode, numEcBytesInBlock); + + let mut ecBytes = vec![0u8; numEcBytesInBlock]; for i in 0..numEcBytesInBlock { - // for (int i = 0; i < numEcBytesInBlock; i++) { - ecBytes[i] = toEncode[numDataBytes + i] as u8; + // for (int i = 0; i < numEcBytesInBlock; i++) { + ecBytes[i] = toEncode[numDataBytes + i] as u8; } return ecBytes; - } +} - /** - * Append mode info. On success, store the result in "bits". - */ - pub fn appendModeInfo( mode:Mode, bits:&BitArray) { +/** + * Append mode info. On success, store the result in "bits". + */ +pub fn appendModeInfo(mode: Mode, bits: &mut BitArray) { bits.appendBits(mode.getBits() as u32, 4); - } +} - - /** - * Append length info. On success, store the result in "bits". - */ - pub fn appendLengthInfo( numLetters:u32, version:VersionRef, mode:Mode, bits:&BitArray) -> Result<(),Exceptions> { +/** + * Append length info. On success, store the result in "bits". + */ +pub fn appendLengthInfo( + numLetters: u32, + version: VersionRef, + mode: Mode, + bits: &mut BitArray, +) -> Result<(), Exceptions> { let numBits = mode.getCharacterCountBits(version); if numLetters >= (1 << numBits) { - return Err(Exceptions::WriterException(format!("{} is bigger than {}" ,numLetters , ((1 << numBits) - 1)))) + return Err(Exceptions::WriterException(format!( + "{} is bigger than {}", + numLetters, + ((1 << numBits) - 1) + ))); } bits.appendBits(numLetters, numBits as usize); Ok(()) - } +} - /** - * Append "bytes" in "mode" mode (encoding) into "bits". On success, store the result in "bits". - */ - pub fn appendBytes( content:&str, - mode:Mode, - bits:&BitArray, - encoding:EncodingRef) -> Result<(),Exceptions>{ - match mode { - Mode::NUMERIC => appendNumericBytes(content, bits), - Mode::ALPHANUMERIC => appendAlphanumericBytes(content, bits)?, - Mode::BYTE => append8BitBytes(content, bits, encoding), - Mode::KANJI => appendKanjiBytes(content, bits)?, - _=> return Err(Exceptions::WriterException(format!("Invalid mode: {:?}" , mode))) - }; -Ok(()) +/** + * Append "bytes" in "mode" mode (encoding) into "bits". On success, store the result in "bits". + */ +pub fn appendBytes( + content: &str, + mode: Mode, + bits: &mut BitArray, + encoding: EncodingRef, +) -> Result<(), Exceptions> { + match mode { + Mode::NUMERIC => appendNumericBytes(content, bits), + Mode::ALPHANUMERIC => appendAlphanumericBytes(content, bits)?, + Mode::BYTE => append8BitBytes(content, bits, encoding), + Mode::KANJI => appendKanjiBytes(content, bits)?, + _ => { + return Err(Exceptions::WriterException(format!( + "Invalid mode: {:?}", + mode + ))) + } + }; + Ok(()) // switch (mode) { // case NUMERIC: // appendNumericBytes(content, bits); @@ -645,102 +721,108 @@ Ok(()) // default: // throw new WriterException("Invalid mode: " + mode); // } - } +} - pub fn appendNumericBytes( content:&str, bits:&BitArray) { +pub fn appendNumericBytes(content: &str, bits: &mut BitArray) { let length = content.len(); - let i = 0; + let mut i = 0; while i < length { - let num1 = content.chars().nth(i).unwrap() as u8 - b'0'; - if i + 2 < length { - // Encode three numeric letters in ten bits. - let num2 = content.chars().nth(i + 1).unwrap() as u8 - b'0'; - let num3 = content.chars().nth(i + 2).unwrap() as u8 - b'0'; - bits.appendBits((num1 * 100 + num2 * 10 + num3) as u32, 10); - i += 3; - } else if i + 1 < length { - // Encode two numeric letters in seven bits. - let num2 = content.chars().nth(i + 1).unwrap() as u8 - b'0'; - bits.appendBits((num1 * 10 + num2) as u32, 7); - i += 2; - } else { - // Encode one numeric letter in four bits. - bits.appendBits(num1 as u32, 4); - i+=1; - } - } - } - - pub fn appendAlphanumericBytes( content:&str, bits:&BitArray) -> Result<(),Exceptions> { - let length = content.len(); - let i = 0; - while i < length { - let code1 = getAlphanumericCode(content.chars().nth(i).unwrap() as u32); - if code1 == -1 { - return Err(Exceptions::WriterException("".to_owned())); - } - if i + 1 < length { - let code2 = getAlphanumericCode(content.chars().nth(i + 1).unwrap() as u32); - if (code2 == -1) { - return Err(Exceptions::WriterException("".to_owned())); - + let num1 = content.chars().nth(i).unwrap() as u8 - b'0'; + if i + 2 < length { + // Encode three numeric letters in ten bits. + let num2 = content.chars().nth(i + 1).unwrap() as u8 - b'0'; + let num3 = content.chars().nth(i + 2).unwrap() as u8 - b'0'; + bits.appendBits((num1 * 100 + num2 * 10 + num3) as u32, 10); + i += 3; + } else if i + 1 < length { + // Encode two numeric letters in seven bits. + let num2 = content.chars().nth(i + 1).unwrap() as u8 - b'0'; + bits.appendBits((num1 * 10 + num2) as u32, 7); + i += 2; + } else { + // Encode one numeric letter in four bits. + bits.appendBits(num1 as u32, 4); + i += 1; + } + } +} + +pub fn appendAlphanumericBytes(content: &str, bits: &mut BitArray) -> Result<(), Exceptions> { + let length = content.len(); + let mut i = 0; + while i < length { + let code1 = getAlphanumericCode(content.chars().nth(i).unwrap() as u32); + if code1 == -1 { + return Err(Exceptions::WriterException("".to_owned())); + } + if i + 1 < length { + let code2 = getAlphanumericCode(content.chars().nth(i + 1).unwrap() as u32); + if (code2 == -1) { + return Err(Exceptions::WriterException("".to_owned())); + } + // Encode two alphanumeric letters in 11 bits. + bits.appendBits((code1 * 45 + code2) as u32, 11); + i += 2; + } else { + // Encode one alphanumeric letter in six bits. + bits.appendBits(code1 as u32, 6); + i += 1; } - // Encode two alphanumeric letters in 11 bits. - bits.appendBits((code1 * 45 + code2) as u32, 11); - i += 2; - } else { - // Encode one alphanumeric letter in six bits. - bits.appendBits(code1 as u32, 6); - i+=1; - } } Ok(()) - } +} - fn append8BitBytes( content:&str, bits:&BitArray, encoding:EncodingRef) { - let bytes = encoding.encode(content, encoding::EncoderTrap::Strict).expect("should encode"); +fn append8BitBytes(content: &str, bits: &mut BitArray, encoding: EncodingRef) { + let bytes = encoding + .encode(content, encoding::EncoderTrap::Strict) + .expect("should encode"); // let bytes = content.getBytes(encoding); for b in bytes { - // for (byte b : bytes) { - bits.appendBits(b as u32, 8); + // for (byte b : bytes) { + bits.appendBits(b as u32, 8); } - } +} - fn appendKanjiBytes( content:&str, bits:&BitArray) -> Result<(),Exceptions> { - let sjis = SHIFT_JIS_CHARSET; //encoding::label::encoding_from_whatwg_label("SJIS").unwrap(); - - let bytes = sjis.encode(content, encoding::EncoderTrap::Strict).expect("should encode fine"); +fn appendKanjiBytes(content: &str, bits: &mut BitArray) -> Result<(), Exceptions> { + let sjis = &SHIFT_JIS_CHARSET; //encoding::label::encoding_from_whatwg_label("SJIS").unwrap(); + + let bytes = sjis + .encode(content, encoding::EncoderTrap::Strict) + .expect("should encode fine"); // let bytes = content.getBytes(StringUtils::SHIFT_JIS_CHARSET); if bytes.len() % 2 != 0 { - return Err(Exceptions::WriterException("Kanji byte size not even".to_owned())) + return Err(Exceptions::WriterException( + "Kanji byte size not even".to_owned(), + )); } let maxI = bytes.len() - 1; // bytes.length must be even let mut i = 0; while i < maxI { - // for (int i = 0; i < maxI; i += 2) { - let byte1 = bytes[i] & 0xFF; - let byte2 = bytes[i + 1] & 0xFF; - let code = (byte1 << 8) | byte2; - let subtracted:i32 = -1; - if code >= 0x8140 && code <= 0x9ffc { - subtracted = code as i32 - 0x8140; - } else if code >= 0xe040 && code <= 0xebbf { - subtracted = code as i32 - 0xc140; - } - if subtracted == -1 { - return Err(Exceptions::WriterException("Invalid byte sequence".to_owned())) - } - let encoded = ((subtracted >> 8) * 0xc0) + (subtracted & 0xff); - bits.appendBits(encoded as u32, 13); + // for (int i = 0; i < maxI; i += 2) { + let byte1 = bytes[i];// & 0xFF; + let byte2 = bytes[i + 1];// & 0xFF; + let code:u16 = ((byte1 as u16) << 8u16) | byte2 as u16; + let mut subtracted: i32 = -1; + if code >= 0x8140 && code <= 0x9ffc { + subtracted = code as i32 - 0x8140; + } else if code >= 0xe040 && code <= 0xebbf { + subtracted = code as i32 - 0xc140; + } + if subtracted == -1 { + return Err(Exceptions::WriterException( + "Invalid byte sequence".to_owned(), + )); + } + let encoded = ((subtracted >> 8) * 0xc0) + (subtracted & 0xff); + bits.appendBits(encoded as u32, 13); - i+=2; + i += 2; } Ok(()) - } +} - fn appendECI( eci:&CharacterSetECI, bits:&BitArray) { +fn appendECI(eci: &CharacterSetECI, bits: &mut BitArray) { bits.appendBits(Mode::ECI.getBits() as u32, 4); // This is correct for values up to 127, which is all we need now. bits.appendBits(eci.getValueSelf(), 8); - } - +} diff --git a/src/qrcode/encoder/minimal_encoder.rs b/src/qrcode/encoder/minimal_encoder.rs index b4692a3..cef8ff9 100644 --- a/src/qrcode/encoder/minimal_encoder.rs +++ b/src/qrcode/encoder/minimal_encoder.rs @@ -18,38 +18,44 @@ use std::{fmt, rc::Rc}; use encoding::EncodingRef; -use crate::{common::{ECIEncoderSet, BitArray}, qrcode::decoder::{ErrorCorrectionLevel, Version, Mode, VersionRef}, Exceptions}; +use crate::{ + common::{BitArray, ECIEncoderSet}, + qrcode::decoder::{ErrorCorrectionLevel, Mode, Version, VersionRef}, + Exceptions, +}; use super::encoder; +pub enum VersionSize { + SMALL, //("version 1-9"), + MEDIUM, //("version 10-26"), + LARGE, //("version 27-40"); -enum VersionSize { - SMALL,//("version 1-9"), - MEDIUM,//("version 10-26"), - LARGE,//("version 27-40"); + // private final String description; - // private final String description; + // VersionSize(String description) { + // this.description = description; + // } - // VersionSize(String description) { - // this.description = description; - // } - - // public String toString() { - // return description; - // } + // public String toString() { + // return description; + // } } impl fmt::Display for VersionSize { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( f, "{}", match self { - VersionSize::SMALL => "version 1-9", - VersionSize::MEDIUM => "version 10-26", - VersionSize::LARGE => "version 27-40", - }) + write!( + f, + "{}", + match self { + VersionSize::SMALL => "version 1-9", + VersionSize::MEDIUM => "version 10-26", + VersionSize::LARGE => "version 27-40", + } + ) } } - /** * Encoder that encodes minimally * @@ -77,667 +83,912 @@ impl fmt::Display for VersionSize { * * @author Alex Geller */ -pub struct MinimalEncoder<'a> { - - stringToEncode: &'a str, - isGS1: bool, - encoders: ECIEncoderSet, - ecLevel:ErrorCorrectionLevel, +pub struct MinimalEncoder { + stringToEncode: String, + isGS1: bool, + encoders: ECIEncoderSet, + ecLevel: ErrorCorrectionLevel, } -impl MinimalEncoder<'_> { - - /** - * Creates a MinimalEncoder - * - * @param stringToEncode 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 isGS1 {@code true} if a FNC1 is to be prepended; {@code false} otherwise - * @param ecLevel The error correction level. - * @see RXingResultList#getVersion - */ - pub fn new( stringToEncode:&str, priorityCharset: EncodingRef, isGS1:bool, ecLevel:ErrorCorrectionLevel) -> Self { - Self { - stringToEncode, - isGS1, - encoders: ECIEncoderSet::new(&stringToEncode, priorityCharset, -1), - ecLevel, - } - - // let encoders = ECIEncoderSet::new(&stringToEncode, priorityCharset, -1); - - } - - /** - * Encodes the string minimally - * - * @param stringToEncode The string to encode - * @param version The preferred {@link Version}. A minimal version is computed (see - * {@link RXingResultList#getVersion method} when the value of the argument is null - * @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 isGS1 {@code true} if a FNC1 is to be prepended; {@code false} otherwise - * @param ecLevel The error correction level. - * @return An instance of {@code RXingResultList} representing the minimal solution. - * @see RXingResultList#getBits - * @see RXingResultList#getVersion - * @see RXingResultList#getSize - */ - pub fn encode_with_details<'a>( stringToEncode:&str, version:Option, priorityCharset:EncodingRef, isGS1:bool, - ecLevel:ErrorCorrectionLevel) -> Result,Exceptions> { - MinimalEncoder::new(stringToEncode, priorityCharset, isGS1, ecLevel).encode(version) - } - - pub fn encode(&self, version:Option) -> Result { - if version.is_none() { // compute minimal encoding trying the three version sizes. - let versions = [ Self::getVersion(VersionSize::SMALL), - Self::getVersion(VersionSize::MEDIUM), - Self::getVersion(VersionSize::LARGE) ]; - let results = [ self.encodeSpecificVersion(&versions[0])?, - self.encodeSpecificVersion(&versions[1])?, - self.encodeSpecificVersion(&versions[2])? ]; - let smallestSize = u32::MAX; - let smallestRXingResult:i32 = -1; - for i in 0..3 { - // for (int i = 0; i < 3; i++) { - let size = results[i].getSize(); - if encoder::willFit(size, versions[i], &self.ecLevel) && size < smallestSize { - smallestSize = size; - smallestRXingResult = i as i32; - } - } - if smallestRXingResult < 0 { - return Err(Exceptions::WriterException("Data too big for any version".to_owned())); - } - Ok(results[smallestRXingResult as usize]) - } else { // compute minimal encoding for a given version - let version = version.unwrap(); - let result = self.encodeSpecificVersion(version)?; - if !encoder::willFit(result.getSize(), Self::getVersion(Self::getVersionSize(result.getVersion())), &self.ecLevel) { - return Err(Exceptions::WriterException(format!("Data too big for version {}" , version))); - } - Ok(result) - } - } - - pub fn getVersionSize( version:&Version) -> VersionSize { - return if version.getVersionNumber() <= 9 { VersionSize::SMALL} else {if version.getVersionNumber() <= 26 - {VersionSize::MEDIUM} else {VersionSize::LARGE}}; - } - - pub fn getVersion( versionSize:VersionSize)->VersionRef { - match versionSize { - VersionSize::SMALL => Version::getVersionForNumber(9).expect("should always exist"), - VersionSize::MEDIUM => Version::getVersionForNumber(26).expect("should always exist"), - VersionSize::LARGE => Version::getVersionForNumber(40).expect("should always exist"), - } - // switch (versionSize) { - // case SMALL: - // return Version.getVersionForNumber(9); - // case MEDIUM: - // return Version.getVersionForNumber(26); - // case LARGE: - // default: - // return Version.getVersionForNumber(40); - // } - } - - pub fn isNumeric( c:char) -> bool{ - return c >= '0' && c <= '9'; - } - - pub fn isDoubleByteKanji( c:char) -> bool{ - return encoder::isOnlyDoubleByteKanji(&String::from(c)); - } - - pub fn isAlphanumeric( c: char) -> bool{ - return encoder::getAlphanumericCode(c as u8 as u32) != -1; - } - - pub fn canEncode(&self, mode:&Mode, c:char) -> bool { - match mode { - Mode::NUMERIC => Self::isNumeric(c), - Mode::ALPHANUMERIC => Self::isAlphanumeric(c), - Mode::STRUCTURED_APPEND => todo!(), - Mode::BYTE => true, - Mode::KANJI => Self::isDoubleByteKanji(c), - _=> false,// any character can be encoded as byte(s). Up to the caller to manage splitting into - // multiple bytes when String.getBytes(Charset) return more than one byte. - } - } - - pub fn getCompactedOrdinal( mode:Option) -> Result{ - if mode.is_none() { - return Ok(0); - } - match &mode.unwrap() { - Mode::NUMERIC => Ok(2), - Mode::ALPHANUMERIC => Ok(1), - Mode::BYTE => Ok(3), - Mode::KANJI => Ok(0), - _=> Err(Exceptions::IllegalArgumentException(format!("Illegal mode {:?}", mode))), - } - // switch (mode) { - // case KANJI: - // return 0; - // case ALPHANUMERIC: - // return 1; - // case NUMERIC: - // return 2; - // case BYTE: - // return 3; - // default: - // throw new IllegalStateException("Illegal mode " + mode); - // } - } - - pub fn addEdge(&self, edges:&Vec>>>, position:usize, edge:Option<&Edge>) { - let vertexIndex = position + edge.as_ref().unwrap().characterLength as usize; - let modeEdges = edges[vertexIndex as usize][edge.as_ref().unwrap().charsetEncoderIndex as usize]; - let modeOrdinal = Self::getCompactedOrdinal(Some(edge.as_ref().unwrap().mode)).expect("value") as usize; - if modeEdges[modeOrdinal].is_none() || modeEdges[modeOrdinal].as_ref().unwrap().cachedTotalSize > (&edge).unwrap().cachedTotalSize { - modeEdges[modeOrdinal] = edge; - } - } - - pub fn addEdges( &self, version:VersionRef, edges:&Vec>>>, from:usize, previous:Option<&Edge>) { - let start = 0; - let end = self.encoders.len(); - let priorityEncoderIndex = self.encoders.getPriorityEncoderIndex(); - if priorityEncoderIndex >= 0 && self.encoders.canEncode(self.stringToEncode.chars().nth(from as usize).unwrap() as i16,priorityEncoderIndex) { - start = priorityEncoderIndex; - end = priorityEncoderIndex + 1; - } - - for i in start..end { - // for (int i = start; i < end; i++) { - if self.encoders.canEncode(self.stringToEncode.chars().nth(from).unwrap() as i16, i) { - self.addEdge(edges, from, Some(&Edge::new(Mode::BYTE, from, i, 1, previous, version,&self.encoders,&self.stringToEncode))); - } - } - - if self.canEncode(&Mode::KANJI, self.stringToEncode.chars().nth(from).unwrap()) { - self.addEdge(edges, from, Some(&Edge::new(Mode::KANJI, from, 0, 1, previous, version,&self.encoders,&self.stringToEncode))); - } - - let inputLength = self.stringToEncode.len(); - if self.canEncode(&Mode::ALPHANUMERIC, self.stringToEncode.chars().nth(from).unwrap()) { - self.addEdge(edges, from, Some(&Edge::new(Mode::ALPHANUMERIC, from, 0, if from + 1 >= inputLength || - !self.canEncode(&Mode::ALPHANUMERIC, self.stringToEncode.chars().nth(from + 1).unwrap()) {1} else {2}, previous, version,&self.encoders,&self.stringToEncode))); - } - - if self.canEncode(&Mode::NUMERIC, self.stringToEncode.chars().nth(from).unwrap()) { - self.addEdge(edges, from, Some(&Edge::new(Mode::NUMERIC, from, 0, if from + 1 >= inputLength || - !self.canEncode(&Mode::NUMERIC, self.stringToEncode.chars().nth(from + 1).unwrap()) {1} else {if from + 2 >= inputLength || - !self.canEncode(&Mode::NUMERIC, self.stringToEncode.chars().nth(from + 2).unwrap()) {2} else {3}}, previous, version,&self.encoders,&self.stringToEncode))); - } - } - pub fn encodeSpecificVersion(&self, version:VersionRef) ->Result< RXingResultList, Exceptions >{ - - // @SuppressWarnings("checkstyle:lineLength") - /* A vertex represents a tuple of a position in the input, a mode and a character encoding where position 0 - * denotes the position left of the first character, 1 the position left of the second character and so on. - * Likewise the end vertices are located after the last character at position stringToEncode.length(). +impl MinimalEncoder { + /** + * Creates a MinimalEncoder * - * An edge leading to such a vertex encodes one or more of the characters left of the position that the vertex - * represents and encodes it in the same encoding and mode as the vertex on which the edge ends. In other words, - * all edges leading to a particular vertex encode the same characters in the same mode with the same character - * encoding. They differ only by their source vertices who are all located at i+1 minus the number of encoded - * characters. - * - * The edges leading to a vertex are stored in such a way that there is a fast way to enumerate the edges ending - * on a particular vertex. - * - * The algorithm processes the vertices in order of their position thereby performing the following: - * - * For every vertex at position i the algorithm enumerates the edges ending on the vertex and removes all but the - * shortest from that list. - * Then it processes the vertices for the position i+1. If i+1 == stringToEncode.length() then the algorithm ends - * and chooses the the edge with the smallest size from any of the edges leading to vertices at this position. - * Otherwise the algorithm computes all possible outgoing edges for the vertices at the position i+1 - * - * Examples: - * The process is illustrated by showing the graph (edges) after each iteration from left to right over the input: - * An edge is drawn as follows "(" + fromVertex + ") -- " + encodingMode + "(" + encodedInput + ") (" + - * accumulatedSize + ") --> (" + toVertex + ")" - * - * Example 1 encoding the string "ABCDE": - * Note: This example assumes that alphanumeric encoding is only possible in multiples of two characters so that - * the example is both short and showing the principle. In reality this restriction does not exist. - * - * Initial situation - * (initial) -- BYTE(A) (20) --> (1_BYTE) - * (initial) -- ALPHANUMERIC(AB) (24) --> (2_ALPHANUMERIC) - * - * Situation after adding edges to vertices at position 1 - * (initial) -- BYTE(A) (20) --> (1_BYTE) -- BYTE(B) (28) --> (2_BYTE) - * (1_BYTE) -- ALPHANUMERIC(BC) (44) --> (3_ALPHANUMERIC) - * (initial) -- ALPHANUMERIC(AB) (24) --> (2_ALPHANUMERIC) - * - * Situation after adding edges to vertices at position 2 - * (initial) -- BYTE(A) (20) --> (1_BYTE) - * (initial) -- ALPHANUMERIC(AB) (24) --> (2_ALPHANUMERIC) - * (initial) -- BYTE(A) (20) --> (1_BYTE) -- BYTE(B) (28) --> (2_BYTE) - * (1_BYTE) -- ALPHANUMERIC(BC) (44) --> (3_ALPHANUMERIC) - * (initial) -- ALPHANUMERIC(AB) (24) --> (2_ALPHANUMERIC) -- BYTE(C) (44) --> (3_BYTE) - * (2_ALPHANUMERIC) -- ALPHANUMERIC(CD) (35) --> (4_ALPHANUMERIC) - * - * Situation after adding edges to vertices at position 3 - * (initial) -- BYTE(A) (20) --> (1_BYTE) -- BYTE(B) (28) --> (2_BYTE) -- BYTE(C) (36) --> (3_BYTE) - * (1_BYTE) -- ALPHANUMERIC(BC) (44) --> (3_ALPHANUMERIC) -- BYTE(D) (64) --> (4_BYTE) - * (3_ALPHANUMERIC) -- ALPHANUMERIC(DE) (55) --> (5_ALPHANUMERIC) - * (initial) -- ALPHANUMERIC(AB) (24) --> (2_ALPHANUMERIC) -- ALPHANUMERIC(CD) (35) --> (4_ALPHANUMERIC) - * (2_ALPHANUMERIC) -- ALPHANUMERIC(CD) (35) --> (4_ALPHANUMERIC) - * - * Situation after adding edges to vertices at position 4 - * (initial) -- BYTE(A) (20) --> (1_BYTE) -- BYTE(B) (28) --> (2_BYTE) -- BYTE(C) (36) --> (3_BYTE) -- BYTE(D) (44) --> (4_BYTE) - * (1_BYTE) -- ALPHANUMERIC(BC) (44) --> (3_ALPHANUMERIC) -- ALPHANUMERIC(DE) (55) --> (5_ALPHANUMERIC) - * (initial) -- ALPHANUMERIC(AB) (24) --> (2_ALPHANUMERIC) -- ALPHANUMERIC(CD) (35) --> (4_ALPHANUMERIC) -- BYTE(E) (55) --> (5_BYTE) - * - * Situation after adding edges to vertices at position 5 - * (initial) -- BYTE(A) (20) --> (1_BYTE) -- BYTE(B) (28) --> (2_BYTE) -- BYTE(C) (36) --> (3_BYTE) -- BYTE(D) (44) --> (4_BYTE) -- BYTE(E) (52) --> (5_BYTE) - * (1_BYTE) -- ALPHANUMERIC(BC) (44) --> (3_ALPHANUMERIC) -- ALPHANUMERIC(DE) (55) --> (5_ALPHANUMERIC) - * (initial) -- ALPHANUMERIC(AB) (24) --> (2_ALPHANUMERIC) -- ALPHANUMERIC(CD) (35) --> (4_ALPHANUMERIC) - * - * Encoding as BYTE(ABCDE) has the smallest size of 52 and is hence chosen. The encodation ALPHANUMERIC(ABCD), - * BYTE(E) is longer with a size of 55. - * - * Example 2 encoding the string "XXYY" where X denotes a character unique to character set ISO-8859-2 and Y a - * character unique to ISO-8859-3. Both characters encode as double byte in UTF-8: - * - * Initial situation - * (initial) -- BYTE(X) (32) --> (1_BYTE_ISO-8859-2) - * (initial) -- BYTE(X) (40) --> (1_BYTE_UTF-8) - * (initial) -- BYTE(X) (40) --> (1_BYTE_UTF-16BE) - * - * Situation after adding edges to vertices at position 1 - * (initial) -- BYTE(X) (32) --> (1_BYTE_ISO-8859-2) -- BYTE(X) (40) --> (2_BYTE_ISO-8859-2) - * (1_BYTE_ISO-8859-2) -- BYTE(X) (72) --> (2_BYTE_UTF-8) - * (1_BYTE_ISO-8859-2) -- BYTE(X) (72) --> (2_BYTE_UTF-16BE) - * (initial) -- BYTE(X) (40) --> (1_BYTE_UTF-8) - * (initial) -- BYTE(X) (40) --> (1_BYTE_UTF-16BE) - * - * Situation after adding edges to vertices at position 2 - * (initial) -- BYTE(X) (32) --> (1_BYTE_ISO-8859-2) -- BYTE(X) (40) --> (2_BYTE_ISO-8859-2) - * (2_BYTE_ISO-8859-2) -- BYTE(Y) (72) --> (3_BYTE_ISO-8859-3) - * (2_BYTE_ISO-8859-2) -- BYTE(Y) (80) --> (3_BYTE_UTF-8) - * (2_BYTE_ISO-8859-2) -- BYTE(Y) (80) --> (3_BYTE_UTF-16BE) - * (initial) -- BYTE(X) (40) --> (1_BYTE_UTF-8) -- BYTE(X) (56) --> (2_BYTE_UTF-8) - * (initial) -- BYTE(X) (40) --> (1_BYTE_UTF-16BE) -- BYTE(X) (56) --> (2_BYTE_UTF-16BE) - * - * Situation after adding edges to vertices at position 3 - * (initial) -- BYTE(X) (32) --> (1_BYTE_ISO-8859-2) -- BYTE(X) (40) --> (2_BYTE_ISO-8859-2) -- BYTE(Y) (72) --> (3_BYTE_ISO-8859-3) - * (3_BYTE_ISO-8859-3) -- BYTE(Y) (80) --> (4_BYTE_ISO-8859-3) - * (3_BYTE_ISO-8859-3) -- BYTE(Y) (112) --> (4_BYTE_UTF-8) - * (3_BYTE_ISO-8859-3) -- BYTE(Y) (112) --> (4_BYTE_UTF-16BE) - * (initial) -- BYTE(X) (40) --> (1_BYTE_UTF-8) -- BYTE(X) (56) --> (2_BYTE_UTF-8) -- BYTE(Y) (72) --> (3_BYTE_UTF-8) - * (initial) -- BYTE(X) (40) --> (1_BYTE_UTF-16BE) -- BYTE(X) (56) --> (2_BYTE_UTF-16BE) -- BYTE(Y) (72) --> (3_BYTE_UTF-16BE) - * - * Situation after adding edges to vertices at position 4 - * (initial) -- BYTE(X) (32) --> (1_BYTE_ISO-8859-2) -- BYTE(X) (40) --> (2_BYTE_ISO-8859-2) -- BYTE(Y) (72) --> (3_BYTE_ISO-8859-3) -- BYTE(Y) (80) --> (4_BYTE_ISO-8859-3) - * (3_BYTE_UTF-8) -- BYTE(Y) (88) --> (4_BYTE_UTF-8) - * (3_BYTE_UTF-16BE) -- BYTE(Y) (88) --> (4_BYTE_UTF-16BE) - * (initial) -- BYTE(X) (40) --> (1_BYTE_UTF-8) -- BYTE(X) (56) --> (2_BYTE_UTF-8) -- BYTE(Y) (72) --> (3_BYTE_UTF-8) - * (initial) -- BYTE(X) (40) --> (1_BYTE_UTF-16BE) -- BYTE(X) (56) --> (2_BYTE_UTF-16BE) -- BYTE(Y) (72) --> (3_BYTE_UTF-16BE) - * - * Encoding as ECI(ISO-8859-2),BYTE(XX),ECI(ISO-8859-3),BYTE(YY) has the smallest size of 80 and is hence chosen. - * The encodation ECI(UTF-8),BYTE(XXYY) is longer with a size of 88. + * @param stringToEncode 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 isGS1 {@code true} if a FNC1 is to be prepended; {@code false} otherwise + * @param ecLevel The error correction level. + * @see RXingResultList#getVersion */ - - let inputLength = self.stringToEncode.len(); - - // Array that represents vertices. There is a vertex for every character, encoding and mode. The vertex contains - // a list of all edges that lead to it that have the same encoding and mode. - // The lists are created lazily - - // The last dimension in the array below encodes the 4 modes KANJI, ALPHANUMERIC, NUMERIC and BYTE via the - // function getCompactedOrdinal(Mode) - let edges = vec![vec![vec![None;4];self.encoders.len()];inputLength+1];//new Edge[inputLength + 1][encoders.length()][4]; - self. addEdges(version, &edges, 0, None); - - for i in 1..=inputLength { - // for (int i = 1; i <= inputLength; i++) { - for j in 0..self.encoders.len() { - // for (int j = 0; j < encoders.length(); j++) { - for k in 0..4 { - // for (int k = 0; k < 4; k++) { - if edges[i][j][k].is_some() && i < inputLength { - self.addEdges(version, &edges, i, edges[i][j][k]); - } + pub fn new( + stringToEncode: &str, + priorityCharset: EncodingRef, + isGS1: bool, + ecLevel: ErrorCorrectionLevel, + ) -> Self { + Self { + stringToEncode: String::from(stringToEncode), + isGS1, + encoders: ECIEncoderSet::new(&stringToEncode, priorityCharset, -1), + ecLevel, } - } + // let encoders = ECIEncoderSet::new(&stringToEncode, priorityCharset, -1); } - let minimalJ = None; - let minimalK = None; - let minimalSize = u32::MAX; - for j in 0..self.encoders.len() { - // for (int j = 0; j < encoders.length(); j++) { - for k in 0..4 { - // for (int k = 0; k < 4; k++) { - if edges[inputLength][j][k].is_some() { - let edge = edges[inputLength][j][k].as_ref().unwrap(); - if edge.cachedTotalSize < minimalSize { - minimalSize = edge.cachedTotalSize; - minimalJ = Some(j); - minimalK = Some(k); - } + + /** + * Encodes the string minimally + * + * @param stringToEncode The string to encode + * @param version The preferred {@link Version}. A minimal version is computed (see + * {@link RXingResultList#getVersion method} when the value of the argument is null + * @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 isGS1 {@code true} if a FNC1 is to be prepended; {@code false} otherwise + * @param ecLevel The error correction level. + * @return An instance of {@code RXingResultList} representing the minimal solution. + * @see RXingResultList#getBits + * @see RXingResultList#getVersion + * @see RXingResultList#getSize + */ + pub fn encode_with_details( + stringToEncode: &str, + version: Option, + priorityCharset: EncodingRef, + isGS1: bool, + ecLevel: ErrorCorrectionLevel, + ) -> Result { + MinimalEncoder::new(stringToEncode, priorityCharset, isGS1, ecLevel).encode(version) + } + + pub fn encode(&self, version: Option) -> Result { + if version.is_none() { + // compute minimal encoding trying the three version sizes. + let versions = [ + Self::getVersion(VersionSize::SMALL), + Self::getVersion(VersionSize::MEDIUM), + Self::getVersion(VersionSize::LARGE), + ]; + let results = [ + self.encodeSpecificVersion(&versions[0])?, + self.encodeSpecificVersion(&versions[1])?, + self.encodeSpecificVersion(&versions[2])?, + ]; + let mut smallestSize = u32::MAX; + let mut smallestRXingResult: i32 = -1; + for i in 0..3 { + // for (int i = 0; i < 3; i++) { + let size = results[i].getSize(); + if encoder::willFit(size, versions[i], &self.ecLevel) && size < smallestSize { + smallestSize = size; + smallestRXingResult = i as i32; + } + } + if smallestRXingResult < 0 { + return Err(Exceptions::WriterException( + "Data too big for any version".to_owned(), + )); + } + Ok(results[smallestRXingResult as usize].clone()) + } else { + // compute minimal encoding for a given version + let version = version.unwrap(); + let result = self.encodeSpecificVersion(version)?; + if !encoder::willFit( + result.getSize(), + Self::getVersion(Self::getVersionSize(result.getVersion())), + &self.ecLevel, + ) { + return Err(Exceptions::WriterException(format!( + "Data too big for version {}", + version + ))); + } + Ok(result) } - } } - if minimalJ.is_none() { - return Err(Exceptions::WriterException(format!(r#"Internal error: failed to encode "{}"#,self.stringToEncode))); + + pub fn getVersionSize(version: VersionRef) -> VersionSize { + return if version.getVersionNumber() <= 9 { + VersionSize::SMALL + } else { + if version.getVersionNumber() <= 26 { + VersionSize::MEDIUM + } else { + VersionSize::LARGE + } + }; + } + + pub fn getVersion(versionSize: VersionSize) -> VersionRef { + match versionSize { + VersionSize::SMALL => Version::getVersionForNumber(9).expect("should always exist"), + VersionSize::MEDIUM => Version::getVersionForNumber(26).expect("should always exist"), + VersionSize::LARGE => Version::getVersionForNumber(40).expect("should always exist"), + } + // switch (versionSize) { + // case SMALL: + // return Version.getVersionForNumber(9); + // case MEDIUM: + // return Version.getVersionForNumber(26); + // case LARGE: + // default: + // return Version.getVersionForNumber(40); + // } + } + + pub fn isNumeric(c: char) -> bool { + return c >= '0' && c <= '9'; + } + + pub fn isDoubleByteKanji(c: char) -> bool { + return encoder::isOnlyDoubleByteKanji(&String::from(c)); + } + + pub fn isAlphanumeric(c: char) -> bool { + return encoder::getAlphanumericCode(c as u8 as u32) != -1; + } + + pub fn canEncode(&self, mode: &Mode, c: char) -> bool { + match mode { + Mode::NUMERIC => Self::isNumeric(c), + Mode::ALPHANUMERIC => Self::isAlphanumeric(c), + Mode::STRUCTURED_APPEND => todo!(), + Mode::BYTE => true, + Mode::KANJI => Self::isDoubleByteKanji(c), + _ => false, // any character can be encoded as byte(s). Up to the caller to manage splitting into + // multiple bytes when String.getBytes(Charset) return more than one byte. + } + } + + pub fn getCompactedOrdinal(mode: Option) -> Result { + if mode.is_none() { + return Ok(0); + } + match &mode.unwrap() { + Mode::NUMERIC => Ok(2), + Mode::ALPHANUMERIC => Ok(1), + Mode::BYTE => Ok(3), + Mode::KANJI => Ok(0), + _ => Err(Exceptions::IllegalArgumentException(format!( + "Illegal mode {:?}", + mode + ))), + } + // switch (mode) { + // case KANJI: + // return 0; + // case ALPHANUMERIC: + // return 1; + // case NUMERIC: + // return 2; + // case BYTE: + // return 3; + // default: + // throw new IllegalStateException("Illegal mode " + mode); + // } + } + + pub fn addEdge( + &self, + edges: &mut Vec>>>>, + position: usize, + edge: Option>, + ) { + let vertexIndex = position + edge.as_ref().unwrap().characterLength as usize; + let modeEdges = + &mut edges[vertexIndex as usize][edge.as_ref().unwrap().charsetEncoderIndex as usize]; + let modeOrdinal = + Self::getCompactedOrdinal(Some(edge.as_ref().unwrap().mode)).expect("value") as usize; + if modeEdges[modeOrdinal].is_none() + || modeEdges[modeOrdinal].as_ref().unwrap().cachedTotalSize + > edge.as_ref().unwrap().cachedTotalSize + { + modeEdges[modeOrdinal] = edge; + } + } + + pub fn addEdges( + &self, + version: VersionRef, + edges: &mut Vec>>>>, + from: usize, + previous: Option>, + ) { + let mut start = 0; + let mut end = self.encoders.len(); + let priorityEncoderIndex = self.encoders.getPriorityEncoderIndex(); + if priorityEncoderIndex >= 0 + && self.encoders.canEncode( + self.stringToEncode.chars().nth(from as usize).unwrap() as i16, + priorityEncoderIndex, + ) + { + start = priorityEncoderIndex; + end = priorityEncoderIndex + 1; + } + + for i in start..end { + // for (int i = start; i < end; i++) { + if self + .encoders + .canEncode(self.stringToEncode.chars().nth(from).unwrap() as i16, i) + { + self.addEdge( + edges, + from, + Some(Rc::new(Edge::new( + Mode::BYTE, + from, + i, + 1, + previous.clone(), + version, + self.encoders.clone(), + &self.stringToEncode, + ))), + ); + } + } + + if self.canEncode(&Mode::KANJI, self.stringToEncode.chars().nth(from).unwrap()) { + self.addEdge( + edges, + from, + Some(Rc::new(Edge::new( + Mode::KANJI, + from, + 0, + 1, + previous.clone(), + version, + self.encoders.clone(), + &self.stringToEncode, + ))), + ); + } + + let inputLength = self.stringToEncode.len(); + if self.canEncode( + &Mode::ALPHANUMERIC, + self.stringToEncode.chars().nth(from).unwrap(), + ) { + self.addEdge( + edges, + from, + Some(Rc::new(Edge::new( + Mode::ALPHANUMERIC, + from, + 0, + if from + 1 >= inputLength + || !self.canEncode( + &Mode::ALPHANUMERIC, + self.stringToEncode.chars().nth(from + 1).unwrap(), + ) + { + 1 + } else { + 2 + }, + previous.clone(), + version, + self.encoders.clone(), + &self.stringToEncode, + ))), + ); + } + + if self.canEncode( + &Mode::NUMERIC, + self.stringToEncode.chars().nth(from).unwrap(), + ) { + self.addEdge( + edges, + from, + Some(Rc::new(Edge::new( + Mode::NUMERIC, + from, + 0, + if from + 1 >= inputLength + || !self.canEncode( + &Mode::NUMERIC, + self.stringToEncode.chars().nth(from + 1).unwrap(), + ) + { + 1 + } else { + if from + 2 >= inputLength + || !self.canEncode( + &Mode::NUMERIC, + self.stringToEncode.chars().nth(from + 2).unwrap(), + ) + { + 2 + } else { + 3 + } + }, + previous.clone(), + version, + self.encoders.clone(), + &self.stringToEncode, + ))), + ); + } + } + pub fn encodeSpecificVersion( + &self, + version: VersionRef, + ) -> Result { + // @SuppressWarnings("checkstyle:lineLength") + /* A vertex represents a tuple of a position in the input, a mode and a character encoding where position 0 + * denotes the position left of the first character, 1 the position left of the second character and so on. + * Likewise the end vertices are located after the last character at position stringToEncode.length(). + * + * An edge leading to such a vertex encodes one or more of the characters left of the position that the vertex + * represents and encodes it in the same encoding and mode as the vertex on which the edge ends. In other words, + * all edges leading to a particular vertex encode the same characters in the same mode with the same character + * encoding. They differ only by their source vertices who are all located at i+1 minus the number of encoded + * characters. + * + * The edges leading to a vertex are stored in such a way that there is a fast way to enumerate the edges ending + * on a particular vertex. + * + * The algorithm processes the vertices in order of their position thereby performing the following: + * + * For every vertex at position i the algorithm enumerates the edges ending on the vertex and removes all but the + * shortest from that list. + * Then it processes the vertices for the position i+1. If i+1 == stringToEncode.length() then the algorithm ends + * and chooses the the edge with the smallest size from any of the edges leading to vertices at this position. + * Otherwise the algorithm computes all possible outgoing edges for the vertices at the position i+1 + * + * Examples: + * The process is illustrated by showing the graph (edges) after each iteration from left to right over the input: + * An edge is drawn as follows "(" + fromVertex + ") -- " + encodingMode + "(" + encodedInput + ") (" + + * accumulatedSize + ") --> (" + toVertex + ")" + * + * Example 1 encoding the string "ABCDE": + * Note: This example assumes that alphanumeric encoding is only possible in multiples of two characters so that + * the example is both short and showing the principle. In reality this restriction does not exist. + * + * Initial situation + * (initial) -- BYTE(A) (20) --> (1_BYTE) + * (initial) -- ALPHANUMERIC(AB) (24) --> (2_ALPHANUMERIC) + * + * Situation after adding edges to vertices at position 1 + * (initial) -- BYTE(A) (20) --> (1_BYTE) -- BYTE(B) (28) --> (2_BYTE) + * (1_BYTE) -- ALPHANUMERIC(BC) (44) --> (3_ALPHANUMERIC) + * (initial) -- ALPHANUMERIC(AB) (24) --> (2_ALPHANUMERIC) + * + * Situation after adding edges to vertices at position 2 + * (initial) -- BYTE(A) (20) --> (1_BYTE) + * (initial) -- ALPHANUMERIC(AB) (24) --> (2_ALPHANUMERIC) + * (initial) -- BYTE(A) (20) --> (1_BYTE) -- BYTE(B) (28) --> (2_BYTE) + * (1_BYTE) -- ALPHANUMERIC(BC) (44) --> (3_ALPHANUMERIC) + * (initial) -- ALPHANUMERIC(AB) (24) --> (2_ALPHANUMERIC) -- BYTE(C) (44) --> (3_BYTE) + * (2_ALPHANUMERIC) -- ALPHANUMERIC(CD) (35) --> (4_ALPHANUMERIC) + * + * Situation after adding edges to vertices at position 3 + * (initial) -- BYTE(A) (20) --> (1_BYTE) -- BYTE(B) (28) --> (2_BYTE) -- BYTE(C) (36) --> (3_BYTE) + * (1_BYTE) -- ALPHANUMERIC(BC) (44) --> (3_ALPHANUMERIC) -- BYTE(D) (64) --> (4_BYTE) + * (3_ALPHANUMERIC) -- ALPHANUMERIC(DE) (55) --> (5_ALPHANUMERIC) + * (initial) -- ALPHANUMERIC(AB) (24) --> (2_ALPHANUMERIC) -- ALPHANUMERIC(CD) (35) --> (4_ALPHANUMERIC) + * (2_ALPHANUMERIC) -- ALPHANUMERIC(CD) (35) --> (4_ALPHANUMERIC) + * + * Situation after adding edges to vertices at position 4 + * (initial) -- BYTE(A) (20) --> (1_BYTE) -- BYTE(B) (28) --> (2_BYTE) -- BYTE(C) (36) --> (3_BYTE) -- BYTE(D) (44) --> (4_BYTE) + * (1_BYTE) -- ALPHANUMERIC(BC) (44) --> (3_ALPHANUMERIC) -- ALPHANUMERIC(DE) (55) --> (5_ALPHANUMERIC) + * (initial) -- ALPHANUMERIC(AB) (24) --> (2_ALPHANUMERIC) -- ALPHANUMERIC(CD) (35) --> (4_ALPHANUMERIC) -- BYTE(E) (55) --> (5_BYTE) + * + * Situation after adding edges to vertices at position 5 + * (initial) -- BYTE(A) (20) --> (1_BYTE) -- BYTE(B) (28) --> (2_BYTE) -- BYTE(C) (36) --> (3_BYTE) -- BYTE(D) (44) --> (4_BYTE) -- BYTE(E) (52) --> (5_BYTE) + * (1_BYTE) -- ALPHANUMERIC(BC) (44) --> (3_ALPHANUMERIC) -- ALPHANUMERIC(DE) (55) --> (5_ALPHANUMERIC) + * (initial) -- ALPHANUMERIC(AB) (24) --> (2_ALPHANUMERIC) -- ALPHANUMERIC(CD) (35) --> (4_ALPHANUMERIC) + * + * Encoding as BYTE(ABCDE) has the smallest size of 52 and is hence chosen. The encodation ALPHANUMERIC(ABCD), + * BYTE(E) is longer with a size of 55. + * + * Example 2 encoding the string "XXYY" where X denotes a character unique to character set ISO-8859-2 and Y a + * character unique to ISO-8859-3. Both characters encode as double byte in UTF-8: + * + * Initial situation + * (initial) -- BYTE(X) (32) --> (1_BYTE_ISO-8859-2) + * (initial) -- BYTE(X) (40) --> (1_BYTE_UTF-8) + * (initial) -- BYTE(X) (40) --> (1_BYTE_UTF-16BE) + * + * Situation after adding edges to vertices at position 1 + * (initial) -- BYTE(X) (32) --> (1_BYTE_ISO-8859-2) -- BYTE(X) (40) --> (2_BYTE_ISO-8859-2) + * (1_BYTE_ISO-8859-2) -- BYTE(X) (72) --> (2_BYTE_UTF-8) + * (1_BYTE_ISO-8859-2) -- BYTE(X) (72) --> (2_BYTE_UTF-16BE) + * (initial) -- BYTE(X) (40) --> (1_BYTE_UTF-8) + * (initial) -- BYTE(X) (40) --> (1_BYTE_UTF-16BE) + * + * Situation after adding edges to vertices at position 2 + * (initial) -- BYTE(X) (32) --> (1_BYTE_ISO-8859-2) -- BYTE(X) (40) --> (2_BYTE_ISO-8859-2) + * (2_BYTE_ISO-8859-2) -- BYTE(Y) (72) --> (3_BYTE_ISO-8859-3) + * (2_BYTE_ISO-8859-2) -- BYTE(Y) (80) --> (3_BYTE_UTF-8) + * (2_BYTE_ISO-8859-2) -- BYTE(Y) (80) --> (3_BYTE_UTF-16BE) + * (initial) -- BYTE(X) (40) --> (1_BYTE_UTF-8) -- BYTE(X) (56) --> (2_BYTE_UTF-8) + * (initial) -- BYTE(X) (40) --> (1_BYTE_UTF-16BE) -- BYTE(X) (56) --> (2_BYTE_UTF-16BE) + * + * Situation after adding edges to vertices at position 3 + * (initial) -- BYTE(X) (32) --> (1_BYTE_ISO-8859-2) -- BYTE(X) (40) --> (2_BYTE_ISO-8859-2) -- BYTE(Y) (72) --> (3_BYTE_ISO-8859-3) + * (3_BYTE_ISO-8859-3) -- BYTE(Y) (80) --> (4_BYTE_ISO-8859-3) + * (3_BYTE_ISO-8859-3) -- BYTE(Y) (112) --> (4_BYTE_UTF-8) + * (3_BYTE_ISO-8859-3) -- BYTE(Y) (112) --> (4_BYTE_UTF-16BE) + * (initial) -- BYTE(X) (40) --> (1_BYTE_UTF-8) -- BYTE(X) (56) --> (2_BYTE_UTF-8) -- BYTE(Y) (72) --> (3_BYTE_UTF-8) + * (initial) -- BYTE(X) (40) --> (1_BYTE_UTF-16BE) -- BYTE(X) (56) --> (2_BYTE_UTF-16BE) -- BYTE(Y) (72) --> (3_BYTE_UTF-16BE) + * + * Situation after adding edges to vertices at position 4 + * (initial) -- BYTE(X) (32) --> (1_BYTE_ISO-8859-2) -- BYTE(X) (40) --> (2_BYTE_ISO-8859-2) -- BYTE(Y) (72) --> (3_BYTE_ISO-8859-3) -- BYTE(Y) (80) --> (4_BYTE_ISO-8859-3) + * (3_BYTE_UTF-8) -- BYTE(Y) (88) --> (4_BYTE_UTF-8) + * (3_BYTE_UTF-16BE) -- BYTE(Y) (88) --> (4_BYTE_UTF-16BE) + * (initial) -- BYTE(X) (40) --> (1_BYTE_UTF-8) -- BYTE(X) (56) --> (2_BYTE_UTF-8) -- BYTE(Y) (72) --> (3_BYTE_UTF-8) + * (initial) -- BYTE(X) (40) --> (1_BYTE_UTF-16BE) -- BYTE(X) (56) --> (2_BYTE_UTF-16BE) -- BYTE(Y) (72) --> (3_BYTE_UTF-16BE) + * + * Encoding as ECI(ISO-8859-2),BYTE(XX),ECI(ISO-8859-3),BYTE(YY) has the smallest size of 80 and is hence chosen. + * The encodation ECI(UTF-8),BYTE(XXYY) is longer with a size of 88. + */ + + let inputLength = self.stringToEncode.len(); + + // Array that represents vertices. There is a vertex for every character, encoding and mode. The vertex contains + // a list of all edges that lead to it that have the same encoding and mode. + // The lists are created lazily + + // The last dimension in the array below encodes the 4 modes KANJI, ALPHANUMERIC, NUMERIC and BYTE via the + // function getCompactedOrdinal(Mode) + let mut edges = vec![vec![vec![None; 4]; self.encoders.len()]; inputLength + 1]; //new Edge[inputLength + 1][encoders.length()][4]; + self.addEdges(version, &mut edges, 0, None); + + for i in 1..=inputLength { + // for (int i = 1; i <= inputLength; i++) { + for j in 0..self.encoders.len() { + // for (int j = 0; j < encoders.length(); j++) { + for k in 0..4 { + // for (int k = 0; k < 4; k++) { + if edges[i][j][k].is_some() && i < inputLength { + let e = edges[i][j][k].clone(); + self.addEdges(version, &mut edges, i, e); + } + } + } + } + let mut minimalJ = None; + let mut minimalK = None; + let mut minimalSize = u32::MAX; + for j in 0..self.encoders.len() { + // for (int j = 0; j < encoders.length(); j++) { + for k in 0..4 { + // for (int k = 0; k < 4; k++) { + if edges[inputLength][j][k].is_some() { + let edge = edges[inputLength][j][k].as_ref().unwrap(); + if edge.cachedTotalSize < minimalSize { + minimalSize = edge.cachedTotalSize; + minimalJ = Some(j); + minimalK = Some(k); + } + } + } + } + if minimalJ.is_none() { + return Err(Exceptions::WriterException(format!( + r#"Internal error: failed to encode "{}"#, + self.stringToEncode + ))); + } + Ok(RXingResultList::new( + version, + edges[inputLength][minimalJ.unwrap()][minimalK.unwrap()].as_ref().unwrap().clone(), + self.isGS1, + &self.ecLevel, + self.encoders.clone(), + &self.stringToEncode, + )) } - Ok(RXingResultList::new(version, edges[inputLength][minimalJ.unwrap()][minimalK.unwrap()].unwrap(),self.isGS1,&self.ecLevel, &self.encoders, &self.stringToEncode)) - } } - -struct Edge<'a> { - pub mode:Mode, - fromPosition:usize, - charsetEncoderIndex:usize, - characterLength:u32, - previous:Option<&'a Edge<'a>>, - cachedTotalSize:u32, - encoders:&'a ECIEncoderSet, - stringToEncode: &'a str, +pub struct Edge { + pub mode: Mode, + fromPosition: usize, + charsetEncoderIndex: usize, + characterLength: u32, + previous: Option>, + cachedTotalSize: u32, + encoders: ECIEncoderSet, + stringToEncode: String, } -impl Edge<'_> { +impl Edge { + pub fn new( + mode: Mode, + fromPosition: usize, + charsetEncoderIndex: usize, + characterLength: u32, + previous: Option>, + version: VersionRef, + encoders: ECIEncoderSet, + stringToEncode: &'_ str, + ) -> Self { + let nci = if mode == Mode::BYTE || previous.is_none() { + charsetEncoderIndex + } else { + previous.as_ref().unwrap().charsetEncoderIndex + }; + Self { + mode, + fromPosition, + charsetEncoderIndex: nci, + characterLength, + previous: previous.clone(), + stringToEncode: String::from(stringToEncode), + cachedTotalSize: { + let mut size = if previous.is_some() { + previous.as_ref().unwrap().cachedTotalSize + } else { + 0 + }; - pub fn new( mode:Mode, fromPosition:usize, charsetEncoderIndex:usize, characterLength:u32, previous:Option<&'_ Edge>, - version:&Version, encoders: &'_ ECIEncoderSet, stringToEncode: &'_ str) -> Self { - let nci = if mode == Mode::BYTE || previous.is_none() {charsetEncoderIndex} else - {previous.as_ref().unwrap().charsetEncoderIndex}; - Self { - mode, - fromPosition, - charsetEncoderIndex: nci, - characterLength, - previous, - stringToEncode, - cachedTotalSize: { - let size = if previous.is_some() {previous.as_ref().unwrap().cachedTotalSize} else {0}; - - let needECI = mode == Mode::BYTE && + let needECI = mode == Mode::BYTE && (previous.is_none() && nci != 0) || // at the beginning and charset is not ISO-8859-1 (previous.is_some() && nci != previous.as_ref().unwrap().charsetEncoderIndex); - - if previous.is_none()|| mode != previous.as_ref().unwrap().mode || needECI { - size += 4 + mode.getCharacterCountBits(&version) as u32; - } - match mode { - Mode::NUMERIC => size += if characterLength == 1 {4} else {if characterLength == 2 {7} else {10}}, - Mode::ALPHANUMERIC => size += if characterLength == 1 {6} else {11}, - Mode::BYTE =>{ - size += 8 * encoders.encode_string(&stringToEncode[fromPosition as usize..(fromPosition + characterLength as usize)], - charsetEncoderIndex as usize).len() as u32; - if needECI { - size += 4 + 8; // the ECI assignment numbers for ISO-8859-x, UTF-8 and UTF-16 are all 8 bit long - } - }, - Mode::KANJI => size += 13, - _=> {}, - } - // switch (mode) { - // case KANJI: - // size += 13; - // break; - // case ALPHANUMERIC: - // size += characterLength == 1 ? 6 : 11; - // break; - // case NUMERIC: - // size += characterLength == 1 ? 4 : characterLength == 2 ? 7 : 10; - // break; - // case BYTE: - // size += 8 * encoders.encode(stringToEncode.substring(fromPosition, fromPosition + characterLength), - // charsetEncoderIndex).length; - // if (needECI) { - // size += 4 + 8; // the ECI assignment numbers for ISO-8859-x, UTF-8 and UTF-16 are all 8 bit long - // } - // break; - // } - size - }, - encoders + + if previous.is_none() || mode != previous.as_ref().unwrap().mode || needECI { + size += 4 + mode.getCharacterCountBits(&version) as u32; } - // this.mode = mode; - // this.fromPosition = fromPosition; - // this.charsetEncoderIndex = mode == Mode.BYTE || previous == null ? charsetEncoderIndex : - // previous.charsetEncoderIndex; // inherit the encoding if not of type BYTE - // this.characterLength = characterLength; - // this.previous = previous; + match mode { + Mode::NUMERIC => { + size += if characterLength == 1 { + 4 + } else { + if characterLength == 2 { + 7 + } else { + 10 + } + } + } + Mode::ALPHANUMERIC => size += if characterLength == 1 { 6 } else { 11 }, + Mode::BYTE => { + size += 8 * encoders + .encode_string( + &stringToEncode[fromPosition as usize + ..(fromPosition + characterLength as usize)], + charsetEncoderIndex as usize, + ) + .len() as u32; + if needECI { + size += 4 + 8; // the ECI assignment numbers for ISO-8859-x, UTF-8 and UTF-16 are all 8 bit long + } + } + Mode::KANJI => size += 13, + _ => {} + } + // switch (mode) { + // case KANJI: + // size += 13; + // break; + // case ALPHANUMERIC: + // size += characterLength == 1 ? 6 : 11; + // break; + // case NUMERIC: + // size += characterLength == 1 ? 4 : characterLength == 2 ? 7 : 10; + // break; + // case BYTE: + // size += 8 * encoders.encode(stringToEncode.substring(fromPosition, fromPosition + characterLength), + // charsetEncoderIndex).length; + // if (needECI) { + // size += 4 + 8; // the ECI assignment numbers for ISO-8859-x, UTF-8 and UTF-16 are all 8 bit long + // } + // break; + // } + size + }, + encoders, + } + // this.mode = mode; + // this.fromPosition = fromPosition; + // this.charsetEncoderIndex = mode == Mode.BYTE || previous == null ? charsetEncoderIndex : + // previous.charsetEncoderIndex; // inherit the encoding if not of type BYTE + // this.characterLength = characterLength; + // this.previous = previous; - // int size = previous != null ? previous.cachedTotalSize : 0; + // int size = previous != null ? previous.cachedTotalSize : 0; - // boolean needECI = mode == Mode.BYTE && - // (previous == null && this.charsetEncoderIndex != 0) || // at the beginning and charset is not ISO-8859-1 - // (previous != null && this.charsetEncoderIndex != previous.charsetEncoderIndex); + // boolean needECI = mode == Mode.BYTE && + // (previous == null && this.charsetEncoderIndex != 0) || // at the beginning and charset is not ISO-8859-1 + // (previous != null && this.charsetEncoderIndex != previous.charsetEncoderIndex); - // if (previous == null || mode != previous.mode || needECI) { - // size += 4 + mode.getCharacterCountBits(version); - // } - // switch (mode) { - // case KANJI: - // size += 13; - // break; - // case ALPHANUMERIC: - // size += characterLength == 1 ? 6 : 11; - // break; - // case NUMERIC: - // size += characterLength == 1 ? 4 : characterLength == 2 ? 7 : 10; - // break; - // case BYTE: - // size += 8 * encoders.encode(stringToEncode.substring(fromPosition, fromPosition + characterLength), - // charsetEncoderIndex).length; - // if (needECI) { - // size += 4 + 8; // the ECI assignment numbers for ISO-8859-x, UTF-8 and UTF-16 are all 8 bit long - // } - // break; - // } - // cachedTotalSize = size; - } + // if (previous == null || mode != previous.mode || needECI) { + // size += 4 + mode.getCharacterCountBits(version); + // } + // switch (mode) { + // case KANJI: + // size += 13; + // break; + // case ALPHANUMERIC: + // size += characterLength == 1 ? 6 : 11; + // break; + // case NUMERIC: + // size += characterLength == 1 ? 4 : characterLength == 2 ? 7 : 10; + // break; + // case BYTE: + // size += 8 * encoders.encode(stringToEncode.substring(fromPosition, fromPosition + characterLength), + // charsetEncoderIndex).length; + // if (needECI) { + // size += 4 + 8; // the ECI assignment numbers for ISO-8859-x, UTF-8 and UTF-16 are all 8 bit long + // } + // break; + // } + // cachedTotalSize = size; + } } -struct RXingResultList<'a> { - list: Vec>, - version: VersionRef, +#[derive(Clone)] +pub struct RXingResultList { + list: Vec, + version: VersionRef, } -impl RXingResultList<'_> { +impl RXingResultList { + pub fn new( + version: VersionRef, + solution: Rc, + isGS1: bool, + ecLevel: &ErrorCorrectionLevel, + encoders: ECIEncoderSet, + stringToEncode: &str, + ) -> Self { + let mut length = 0; + let mut current = Some(solution); + let mut containsECI = false; + let mut list = Vec::new(); - pub fn new( version:VersionRef, solution:&'_ Edge, isGS1:bool, ecLevel: &ErrorCorrectionLevel, encoders:&'_ ECIEncoderSet, stringToEncode: &'_ str) -> Self { - let length = 0; - let current = Some(solution); - let containsECI = false; - let list = Vec::new(); + while current.is_some() { + length += current.as_ref().unwrap().characterLength; + let previous = current.as_ref().unwrap().previous.clone(); - while current.is_some() { - length += current.as_ref().unwrap().characterLength; - let previous = current.as_ref().unwrap().previous; - - let needECI = current.as_ref().unwrap().mode == Mode::BYTE && + let needECI = current.as_ref().unwrap().mode == Mode::BYTE && (previous.is_none() && current.as_ref().unwrap().charsetEncoderIndex != 0) || // at the beginning and charset is not ISO-8859-1 (previous.is_some() && current.as_ref().unwrap().charsetEncoderIndex != previous.as_ref().unwrap().charsetEncoderIndex); - if needECI { - containsECI = true; - } + if needECI { + containsECI = true; + } - if previous.is_none() || previous.as_ref().unwrap().mode != current.as_ref().unwrap().mode || needECI { - list.push( RXingResultNode::new(current.as_ref().unwrap().mode, current.as_ref().unwrap().fromPosition, current.as_ref().unwrap().charsetEncoderIndex, length,encoders,stringToEncode, version)); - length = 0; - } + if previous.is_none() + || previous.as_ref().unwrap().mode != current.as_ref().unwrap().mode + || needECI + { + list.push(RXingResultNode::new( + current.as_ref().unwrap().mode, + current.as_ref().unwrap().fromPosition, + current.as_ref().unwrap().charsetEncoderIndex, + length, + encoders.clone(), + stringToEncode, + version, + )); + length = 0; + } - if needECI { - list.push( RXingResultNode::new(Mode::ECI, current.as_ref().unwrap().fromPosition, current.as_ref().unwrap().charsetEncoderIndex, 0,encoders,stringToEncode, version)); - } - current = previous; - } - - // prepend FNC1 if needed. If the bits contain an ECI then the FNC1 must be preceeded by an ECI. - // If there is no ECI at the beginning then we put an ECI to the default charset (ISO-8859-1) - if isGS1 { - if let Some(first) = list.get(0){ - // if first != null && first.mode != Mode.ECI && containsECI { - if first.mode != Mode::ECI && containsECI { - // prepend a default character set ECI - list.push( RXingResultNode::new(Mode::ECI, 0, 0, 0,encoders,stringToEncode,version)); - }} - let first = list.get(0).unwrap(); - // prepend or insert a FNC1_FIRST_POSITION after the ECI (if any) - // if first != null && first.mode != Mode.ECI && containsECI { - list.push( RXingResultNode::new(Mode::FNC1_FIRST_POSITION, 0, 0, 0,encoders,stringToEncode,version)); - } - - // set version to smallest version into which the bits fit. - let versionNumber = version.getVersionNumber(); - let (lowerLimit,upperLimit) = - match MinimalEncoder::getVersionSize(&version) { - VersionSize::SMALL => - (1,9), - VersionSize::MEDIUM=>(10,26), - _=>(27,40), - }; - // let lowerLimit; - // let upperLimit; - // switch (getVersionSize(version)) { - // case SMALL: - // lowerLimit = 1; - // upperLimit = 9; - // break; - // case MEDIUM: - // lowerLimit = 10; - // upperLimit = 26; - // break; - // case LARGE: - // default: - // lowerLimit = 27; - // upperLimit = 40; - // break; - // } - let size = Self::internal_static_get_size(version,&list); - // increase version if needed - while versionNumber < upperLimit && !encoder::willFit(size, Version::getVersionForNumber(versionNumber).unwrap(), - ecLevel) { - versionNumber+=1; - } - // shrink version if possible - while versionNumber > lowerLimit && encoder::willFit(size, Version::getVersionForNumber(versionNumber - 1).unwrap(), - ecLevel) { - versionNumber-=1; - } - let version = Version::getVersionForNumber(versionNumber).unwrap(); - Self { - list, - version, - } - } - - /** - * returns the size in bits - */ - pub fn getSize(&self) -> u32{ - self. getSizeLocal(self.version) - } - - fn getSizeLocal(&self, version:VersionRef) -> u32{ - let result = 0; - for resultNode in &self.list { - result += resultNode.getSize(version); - } - return result; - } - - fn internal_static_get_size(version:VersionRef, list: &Vec) -> u32 { - let result = 0; - for resultNode in list { - result += resultNode.getSize(version); - } - return result; - } - - /** - * appends the bits - */ - pub fn getBits(&self, bits:&BitArray) -> Result<(),Exceptions> { - for resultNode in &self.list { - resultNode.getBits(bits); - } - Ok(()) - } - - pub fn getVersion(&self) -> &Version{ - &self. version - } -} - -impl fmt::Display for RXingResultList<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut result = String::new(); - let previous = None; - for current in &self.list { - // for (RXingResultNode current : list) { - if previous.is_some() { - result.push_str(","); + if needECI { + list.push(RXingResultNode::new( + Mode::ECI, + current.as_ref().unwrap().fromPosition, + current.as_ref().unwrap().charsetEncoderIndex, + 0, + encoders.clone(), + stringToEncode, + version, + )); + } + current = previous; } - result.push_str(¤t.to_string()); - previous = Some(current); - } - write!(f,"{}", result) - } -} -struct RXingResultNode<'a> { + // prepend FNC1 if needed. If the bits contain an ECI then the FNC1 must be preceeded by an ECI. + // If there is no ECI at the beginning then we put an ECI to the default charset (ISO-8859-1) + if isGS1 { + if let Some(first) = list.get(0) { + // if first != null && first.mode != Mode.ECI && containsECI { + if first.mode != Mode::ECI && containsECI { + // prepend a default character set ECI + list.push(RXingResultNode::new( + Mode::ECI, + 0, + 0, + 0, + encoders.clone(), + stringToEncode, + version, + )); + } + } + let first = list.get(0).unwrap(); + // prepend or insert a FNC1_FIRST_POSITION after the ECI (if any) + // if first != null && first.mode != Mode.ECI && containsECI { + list.push(RXingResultNode::new( + Mode::FNC1_FIRST_POSITION, + 0, + 0, + 0, + encoders.clone(), + stringToEncode, + version, + )); + } - mode:Mode, - fromPosition:usize, - charsetEncoderIndex:usize, - characterLength:u32, - encoders:&'a ECIEncoderSet, - version: VersionRef, - stringToEncode: &'a str, -} - -impl RXingResultNode<'_> { - - pub fn new( mode:Mode, fromPosition:usize, charsetEncoderIndex:usize, characterLength:u32, encoders:&'_ ECIEncoderSet, stringToEncode: &'_ str, version: &'_ Version) -> Self { - Self { - mode, - fromPosition, - charsetEncoderIndex, - characterLength, - encoders, - stringToEncode, - version, - } + // set version to smallest version into which the bits fit. + let mut versionNumber = version.getVersionNumber(); + let (lowerLimit, upperLimit) = match MinimalEncoder::getVersionSize(&version) { + VersionSize::SMALL => (1, 9), + VersionSize::MEDIUM => (10, 26), + _ => (27, 40), + }; + // let lowerLimit; + // let upperLimit; + // switch (getVersionSize(version)) { + // case SMALL: + // lowerLimit = 1; + // upperLimit = 9; + // break; + // case MEDIUM: + // lowerLimit = 10; + // upperLimit = 26; + // break; + // case LARGE: + // default: + // lowerLimit = 27; + // upperLimit = 40; + // break; + // } + let size = Self::internal_static_get_size(version, &list); + // increase version if needed + while versionNumber < upperLimit + && !encoder::willFit( + size, + Version::getVersionForNumber(versionNumber).unwrap(), + ecLevel, + ) + { + versionNumber += 1; + } + // shrink version if possible + while versionNumber > lowerLimit + && encoder::willFit( + size, + Version::getVersionForNumber(versionNumber - 1).unwrap(), + ecLevel, + ) + { + versionNumber -= 1; + } + let version = Version::getVersionForNumber(versionNumber).unwrap(); + Self { list, version } } /** * returns the size in bits */ - fn getSize(&self, version:&Version) -> u32{ - let size = 4 + self.mode.getCharacterCountBits(version) as u32; - match self.mode { - Mode::NUMERIC => { - size += (self.characterLength / 3) * 10; - let rest = self.characterLength % 3; - size += if rest == 1 {4} else { if rest == 2 {7} else {0}}; - }, - Mode::ALPHANUMERIC => { - size += (self.characterLength / 2) * 11; - size += if (self.characterLength % 2) == 1 {6} else {0}; - }, - Mode::BYTE => size += 8 * self.getCharacterCountIndicator(), - Mode::ECI => size += 8, - Mode::KANJI => size += 13 * self.characterLength, - _ => {}, + pub fn getSize(&self) -> u32 { + self.getSizeLocal(self.version) } - // switch (mode) { + + fn getSizeLocal(&self, version: VersionRef) -> u32 { + let mut result = 0; + for resultNode in &self.list { + result += resultNode.getSize(version); + } + return result; + } + + fn internal_static_get_size(version: VersionRef, list: &Vec) -> u32 { + let mut result = 0; + for resultNode in list { + result += resultNode.getSize(version); + } + return result; + } + + /** + * appends the bits + */ + pub fn getBits(&self, bits: &mut BitArray) -> Result<(), Exceptions> { + for resultNode in &self.list { + resultNode.getBits(bits); + } + Ok(()) + } + + pub fn getVersion(&self) -> VersionRef { + self.version + } +} + +impl fmt::Display for RXingResultList { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut result = String::new(); + let mut previous = None; + for current in &self.list { + // for (RXingResultNode current : list) { + if previous.is_some() { + result.push_str(","); + } + result.push_str(¤t.to_string()); + previous = Some(current); + } + write!(f, "{}", result) + } +} + +#[derive(Clone)] +struct RXingResultNode { + mode: Mode, + fromPosition: usize, + charsetEncoderIndex: usize, + characterLength: u32, + encoders: ECIEncoderSet, + version: VersionRef, + stringToEncode: String, +} + +impl RXingResultNode { + pub fn new( + mode: Mode, + fromPosition: usize, + charsetEncoderIndex: usize, + characterLength: u32, + encoders: ECIEncoderSet, + stringToEncode: &str, + version: VersionRef, + ) -> Self { + Self { + mode, + fromPosition, + charsetEncoderIndex, + characterLength, + encoders, + stringToEncode: String::from(stringToEncode), + version, + } + } + + /** + * returns the size in bits + */ + fn getSize(&self, version: &Version) -> u32 { + let mut size = 4 + self.mode.getCharacterCountBits(version) as u32; + match self.mode { + Mode::NUMERIC => { + size += (self.characterLength / 3) * 10; + let rest = self.characterLength % 3; + size += if rest == 1 { + 4 + } else { + if rest == 2 { + 7 + } else { + 0 + } + }; + } + Mode::ALPHANUMERIC => { + size += (self.characterLength / 2) * 11; + size += if (self.characterLength % 2) == 1 { + 6 + } else { + 0 + }; + } + Mode::BYTE => size += 8 * self.getCharacterCountIndicator(), + Mode::ECI => size += 8, + Mode::KANJI => size += 13 * self.characterLength, + _ => {} + } + // switch (mode) { // case KANJI: // size += 13 * characterLength; // break; @@ -755,65 +1006,91 @@ impl RXingResultNode<'_> { // break; // case ECI: // size += 8; // the ECI assignment numbers for ISO-8859-x, UTF-8 and UTF-16 are all 8 bit long - // } - size + // } + size } /** * returns the length in characters according to the specification (differs from getCharacterLength() in BYTE mode * for multi byte encoded characters) */ - fn getCharacterCountIndicator(&self) -> u32{ - if self.mode == Mode::BYTE - {self.encoders.encode_string(&self.stringToEncode[self.fromPosition as usize..(self.fromPosition + self.characterLength as usize)], - self.charsetEncoderIndex as usize).len() as u32} else {self.characterLength} + fn getCharacterCountIndicator(&self) -> u32 { + if self.mode == Mode::BYTE { + self.encoders + .encode_string( + &self.stringToEncode[self.fromPosition as usize + ..(self.fromPosition + self.characterLength as usize)], + self.charsetEncoderIndex as usize, + ) + .len() as u32 + } else { + self.characterLength + } } /** * appends the bits */ - fn getBits(&self, bits:&BitArray) -> Result<(),Exceptions> { - bits.appendBits(self.mode.getBits() as u32, 4); - if self.characterLength > 0 { - let length = self.getCharacterCountIndicator(); - bits.appendBits(length, self.mode.getCharacterCountBits(self.version) as usize); - } - if self.mode == Mode::ECI { - bits.appendBits(self.encoders.getECIValue(self.charsetEncoderIndex as usize), 8); - } else if self.characterLength > 0 { - // append data - encoder::appendBytes(&self.stringToEncode[self.fromPosition as usize..(self.fromPosition + self.characterLength as usize)], self.mode, bits, - self.encoders.getCharset(self.charsetEncoderIndex as usize)); - } - Ok(()) - } - - fn makePrintable( s:&str) -> String { - let mut result = String::new(); - for i in 0..s.chars().count() { - // for (int i = 0; i < s.length(); i++) { - if (s.chars().nth(i).unwrap() as u8) < 32 || (s.chars().nth(i).unwrap() as u8) > 126 { - result.push('.'); - } else { - result.push(s.chars().nth(i).unwrap()); + fn getBits(&self, bits: &mut BitArray) -> Result<(), Exceptions> { + bits.appendBits(self.mode.getBits() as u32, 4); + if self.characterLength > 0 { + let length = self.getCharacterCountIndicator(); + bits.appendBits( + length, + self.mode.getCharacterCountBits(self.version) as usize, + ); } - } - result + if self.mode == Mode::ECI { + bits.appendBits( + self.encoders.getECIValue(self.charsetEncoderIndex as usize), + 8, + ); + } else if self.characterLength > 0 { + // append data + encoder::appendBytes( + &self.stringToEncode[self.fromPosition as usize + ..(self.fromPosition + self.characterLength as usize)], + self.mode, + bits, + self.encoders.getCharset(self.charsetEncoderIndex as usize), + ); + } + Ok(()) } - } - impl fmt::Display for RXingResultNode<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let result = String::new(); - result.push_str(&format!("{:?}",self.mode)); - result.push('('); - if self.mode == Mode::ECI { - result.push_str(self.encoders.getCharset(self.charsetEncoderIndex as usize).name()); - } else { - result.push_str(&Self::makePrintable(&self.stringToEncode[self.fromPosition as usize..(self.fromPosition + self.characterLength as usize)])); - } - result.push(')'); - - write!(f,"{}", result) + fn makePrintable(s: &str) -> String { + let mut result = String::new(); + for i in 0..s.chars().count() { + // for (int i = 0; i < s.length(); i++) { + if (s.chars().nth(i).unwrap() as u8) < 32 || (s.chars().nth(i).unwrap() as u8) > 126 { + result.push('.'); + } else { + result.push(s.chars().nth(i).unwrap()); + } + } + result } -} \ No newline at end of file +} + +impl fmt::Display for RXingResultNode { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut result = String::new(); + result.push_str(&format!("{:?}", self.mode)); + result.push('('); + if self.mode == Mode::ECI { + result.push_str( + self.encoders + .getCharset(self.charsetEncoderIndex as usize) + .name(), + ); + } else { + result.push_str(&Self::makePrintable( + &self.stringToEncode[self.fromPosition as usize + ..(self.fromPosition + self.characterLength as usize)], + )); + } + result.push(')'); + + write!(f, "{}", result) + } +}