From eaff1b6b644d1781edc4d48ba1130b9d2217724a Mon Sep 17 00:00:00 2001 From: Henry Schimke Date: Mon, 3 Oct 2022 14:34:17 -0500 Subject: [PATCH] encoder ported, no compile --- src/common/mod.rs | 27 +- src/lib.rs | 4 +- src/qrcode/decoder/DataBlock.java | 122 -------- src/qrcode/decoder/data_block.rs | 141 +++++++++ src/qrcode/decoder/mod.rs | 4 +- src/qrcode/encoder/encoder.rs | 418 +++++++++++++++----------- src/qrcode/encoder/minimal_encoder.rs | 133 ++++---- src/qrcode/encoder/qr_code.rs | 5 +- 8 files changed, 484 insertions(+), 370 deletions(-) delete mode 100755 src/qrcode/decoder/DataBlock.java create mode 100755 src/qrcode/decoder/data_block.rs diff --git a/src/common/mod.rs b/src/common/mod.rs index fd52779..d004c66 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -14,6 +14,9 @@ use crate::Exceptions; use crate::LuminanceSource; use crate::RXingResultPoint; use encoding::Encoding; +use encoding::EncodingRef; + +use lazy_static::lazy_static; #[cfg(test)] mod StringUtilsTestCase; @@ -80,6 +83,10 @@ pub struct StringUtils { const ASSUME_SHIFT_JIS: bool = false; static SHIFT_JIS: &'static str = "SJIS"; static GB2312: &'static str = "GB2312"; +lazy_static! { + pub static ref SHIFT_JIS_CHARSET: EncodingRef = encoding::label::encoding_from_whatwg_label("SJIS").unwrap(); +} + // private static final boolean ASSUME_SHIFT_JIS = // SHIFT_JIS_CHARSET.equals(PLATFORM_DEFAULT_ENCODING) || @@ -2466,6 +2473,10 @@ impl CharacterSetECI { // this.otherEncodingNames = otherEncodingNames; // } + pub fn getValueSelf(&self) -> u32{ + Self::getValue(self) + } + pub fn getValue(cs_eci: &CharacterSetECI) -> u32 { match cs_eci { CharacterSetECI::Cp437 => 0, @@ -2498,7 +2509,7 @@ impl CharacterSetECI { } } - pub fn getCharset(cs_eci: &CharacterSetECI) -> &'static dyn Encoding { + pub fn getCharset(cs_eci: &CharacterSetECI) -> EncodingRef { let name = match cs_eci { CharacterSetECI::Cp437 => "CP437", CharacterSetECI::ISO8859_1 => "ISO-8859-1", @@ -2857,7 +2868,7 @@ impl fmt::Display for ECIStringBuilder { * @author Alex Geller */ pub struct ECIEncoderSet { - encoders: Vec<&'static dyn encoding::Encoding>, + encoders: Vec, priorityEncoderIndex: usize, } @@ -2872,11 +2883,11 @@ impl ECIEncoderSet { */ pub fn new( stringToEncode: &str, - priorityCharset: &'static dyn encoding::Encoding, + priorityCharset: EncodingRef, fnc1: i16, ) -> Self { // List of encoders that potentially encode characters not in ISO-8859-1 in one byte. - let mut ENCODERS = Vec::new(); + let mut ENCODERS :Vec = Vec::new(); let names = [ "IBM437", @@ -2910,10 +2921,10 @@ impl ECIEncoderSet { } } - let mut encoders: Vec<&'static dyn Encoding>; + let mut encoders: Vec; let mut priorityEncoderIndexValue = 0; - let mut neededEncoders: Vec<&'static dyn encoding::Encoding> = Vec::new(); + let mut neededEncoders: Vec = Vec::new(); //we always need the ISO-8859-1 encoder. It is the default encoding neededEncoders.push(encoding::all::ISO_8859_1); @@ -3010,7 +3021,7 @@ impl ECIEncoderSet { return self.encoders[index].name(); } - pub fn getCharset(&self, index: usize) -> &'static dyn Encoding { + pub fn getCharset(&self, index: usize) -> EncodingRef { assert!(index < self.len()); return self.encoders[index]; } @@ -3244,7 +3255,7 @@ impl MinimalECIInput { * @param fnc1 denotes the character in the input that represents the FNC1 character or -1 if this is not GS1 * input. */ - pub fn new(stringToEncode: &str, priorityCharset: &'static dyn Encoding, fnc1: i16) -> Self { + pub fn new(stringToEncode: &str, priorityCharset: EncodingRef, fnc1: i16) -> Self { let encoderSet = ECIEncoderSet::new(stringToEncode, priorityCharset, fnc1); let bytes = if encoderSet.len() == 1 { //optimization for the case when all can be encoded without ECI in ISO-8859-1 diff --git a/src/lib.rs b/src/lib.rs index fa4f4cc..74e846b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -702,7 +702,7 @@ pub trait Writer { format: &BarcodeFormat, width: i32, height: i32, - hints: &HashMap, + hints: &EncodingHintDictionary, ) -> Result; } @@ -765,7 +765,7 @@ pub trait Reader { fn decode_with_hints( &self, image: &BinaryBitmap, - hints: &HashMap, + hints: &DecodingHintDictionary, ) -> Result; /** diff --git a/src/qrcode/decoder/DataBlock.java b/src/qrcode/decoder/DataBlock.java deleted file mode 100755 index 05f4726..0000000 --- a/src/qrcode/decoder/DataBlock.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright 2007 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing.qrcode.decoder; - -/** - *

Encapsulates a block of data within a QR Code. QR Codes may split their data into - * multiple blocks, each of which is a unit of data and error-correction codewords. Each - * is represented by an instance of this class.

- * - * @author Sean Owen - */ -final class DataBlock { - - private final int numDataCodewords; - private final byte[] codewords; - - private DataBlock(int numDataCodewords, byte[] codewords) { - this.numDataCodewords = numDataCodewords; - this.codewords = codewords; - } - - /** - *

When QR Codes use multiple data blocks, they are actually interleaved. - * That is, the first byte of data block 1 to n is written, then the second bytes, and so on. This - * method will separate the data into original blocks.

- * - * @param rawCodewords bytes as read directly from the QR Code - * @param version version of the QR Code - * @param ecLevel error-correction level of the QR Code - * @return DataBlocks containing original bytes, "de-interleaved" from representation in the - * QR Code - */ - static DataBlock[] getDataBlocks(byte[] rawCodewords, - Version version, - ErrorCorrectionLevel ecLevel) { - - if (rawCodewords.length != version.getTotalCodewords()) { - throw new IllegalArgumentException(); - } - - // Figure out the number and size of data blocks used by this version and - // error correction level - Version.ECBlocks ecBlocks = version.getECBlocksForLevel(ecLevel); - - // First count the total number of data blocks - int totalBlocks = 0; - Version.ECB[] ecBlockArray = ecBlocks.getECBlocks(); - for (Version.ECB ecBlock : ecBlockArray) { - totalBlocks += ecBlock.getCount(); - } - - // Now establish DataBlocks of the appropriate size and number of data codewords - DataBlock[] result = new DataBlock[totalBlocks]; - int numRXingResultBlocks = 0; - for (Version.ECB ecBlock : ecBlockArray) { - for (int i = 0; i < ecBlock.getCount(); i++) { - int numDataCodewords = ecBlock.getDataCodewords(); - int numBlockCodewords = ecBlocks.getECCodewordsPerBlock() + numDataCodewords; - result[numRXingResultBlocks++] = new DataBlock(numDataCodewords, new byte[numBlockCodewords]); - } - } - - // 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. - int shorterBlocksTotalCodewords = result[0].codewords.length; - int longerBlocksStartAt = result.length - 1; - while (longerBlocksStartAt >= 0) { - int numCodewords = result[longerBlocksStartAt].codewords.length; - if (numCodewords == shorterBlocksTotalCodewords) { - break; - } - longerBlocksStartAt--; - } - longerBlocksStartAt++; - - int shorterBlocksNumDataCodewords = shorterBlocksTotalCodewords - ecBlocks.getECCodewordsPerBlock(); - // The last elements of result may be 1 element longer; - // first fill out as many elements as all of them have - int rawCodewordsOffset = 0; - for (int i = 0; i < shorterBlocksNumDataCodewords; i++) { - for (int j = 0; j < numRXingResultBlocks; j++) { - result[j].codewords[i] = rawCodewords[rawCodewordsOffset++]; - } - } - // Fill out the last data block in the longer ones - for (int j = longerBlocksStartAt; j < numRXingResultBlocks; j++) { - result[j].codewords[shorterBlocksNumDataCodewords] = rawCodewords[rawCodewordsOffset++]; - } - // Now add in error correction blocks - int max = result[0].codewords.length; - for (int i = shorterBlocksNumDataCodewords; i < max; i++) { - for (int j = 0; j < numRXingResultBlocks; j++) { - int iOffset = j < longerBlocksStartAt ? i : i + 1; - result[j].codewords[iOffset] = rawCodewords[rawCodewordsOffset++]; - } - } - return result; - } - - int getNumDataCodewords() { - return numDataCodewords; - } - - byte[] getCodewords() { - return codewords; - } - -} diff --git a/src/qrcode/decoder/data_block.rs b/src/qrcode/decoder/data_block.rs new file mode 100755 index 0000000..97d8733 --- /dev/null +++ b/src/qrcode/decoder/data_block.rs @@ -0,0 +1,141 @@ +/* + * Copyright 2007 ZXing authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use crate::Exceptions; + +use super::{VersionRef, ErrorCorrectionLevel}; + +/** + *

Encapsulates a block of data within a QR Code. QR Codes may split their data into + * multiple blocks, each of which is a unit of data and error-correction codewords. Each + * is represented by an instance of this class.

+ * + * @author Sean Owen + */ +pub struct DataBlock { + + numDataCodewords:u32, + codewords:Vec, +} + +impl DataBlock { + + fn new( numDataCodewords:u32, codewords:Vec) -> Self{ + Self{ + numDataCodewords, + codewords, + } + } + + /** + *

When QR Codes use multiple data blocks, they are actually interleaved. + * That is, the first byte of data block 1 to n is written, then the second bytes, and so on. This + * method will separate the data into original blocks.

+ * + * @param rawCodewords bytes as read directly from the QR Code + * @param version version of the QR Code + * @param ecLevel error-correction level of the QR Code + * @return DataBlocks containing original bytes, "de-interleaved" from representation in the + * QR Code + */ + pub fn getDataBlocks( rawCodewords:&[u8], + version:VersionRef, + ecLevel:ErrorCorrectionLevel) -> Result,Exceptions> { + + if rawCodewords.len() as u32 != version.getTotalCodewords() { + return Err(Exceptions::IllegalArgumentException("".to_owned())) + } + + // Figure out the number and size of data blocks used by this version and + // error correction level + let ecBlocks = version.getECBlocksForLevel(ecLevel); + + // First count the total number of data blocks + let totalBlocks = 0; + let ecBlockArray = ecBlocks.getECBlocks(); + for ecBlock in ecBlockArray { + // for (Version.ECB ecBlock : ecBlockArray) { + totalBlocks += ecBlock.getCount(); + } + + // Now establish DataBlocks of the appropriate size and number of data codewords + let result = Vec::new(); + let numRXingResultBlocks = 0; + for ecBlock in ecBlockArray { + // for (Version.ECB ecBlock : ecBlockArray) { + for i in 0..ecBlock.getCount() { + // for (int i = 0; i < ecBlock.getCount(); i++) { + let numDataCodewords = ecBlock.getDataCodewords(); + let numBlockCodewords = ecBlocks.getECCodewordsPerBlock() + numDataCodewords; + result[numRXingResultBlocks] = DataBlock::new(numDataCodewords, vec![0u8;numBlockCodewords as usize]); + numRXingResultBlocks += 1; + } + } + + // 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; + while (longerBlocksStartAt >= 0) { + let numCodewords = result[longerBlocksStartAt].codewords.len(); + if (numCodewords == shorterBlocksTotalCodewords) { + break; + } + longerBlocksStartAt-=1; + } + longerBlocksStartAt+=1; + + 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; + for i in 0..shorterBlocksNumDataCodewords { + // for (int i = 0; i < shorterBlocksNumDataCodewords; i++) { + for j in 0..numRXingResultBlocks { + // for (int j = 0; j < numRXingResultBlocks; j++) { + result[j].codewords[i] = rawCodewords[rawCodewordsOffset]; + rawCodewordsOffset += 1; + } + } + // Fill out the last data block in the longer ones + for j in longerBlocksStartAt..numRXingResultBlocks{ + // for (int j = longerBlocksStartAt; j < numRXingResultBlocks; j++) { + result[j].codewords[shorterBlocksNumDataCodewords] = rawCodewords[rawCodewordsOffset]; + rawCodewordsOffset += 1; + } + // Now add in error correction blocks + let max = result[0].codewords.len(); + for i in shorterBlocksNumDataCodewords..max { + // for (int i = shorterBlocksNumDataCodewords; i < max; i++) { + for j in 0..numRXingResultBlocks { + // for (int j = 0; j < numRXingResultBlocks; j++) { + let iOffset = if j < longerBlocksStartAt {i} else {i + 1}; + result[j].codewords[iOffset] = rawCodewords[rawCodewordsOffset]; + rawCodewordsOffset += 1; + } + } + Ok(result) + } + + pub fn getNumDataCodewords(&self) -> u32{ + self. numDataCodewords + } + + pub fn getCodewords(&self) -> &[u8] { + &self.codewords + } + +} diff --git a/src/qrcode/decoder/mod.rs b/src/qrcode/decoder/mod.rs index 0ae2763..aabbbe2 100644 --- a/src/qrcode/decoder/mod.rs +++ b/src/qrcode/decoder/mod.rs @@ -2,6 +2,7 @@ mod version; mod mode; mod error_correction_level; mod format_information; +mod data_block; #[cfg(test)] mod ErrorCorrectionLevelTestCase; @@ -13,4 +14,5 @@ mod VersionTestCase; pub use version::*; pub use mode::*; pub use error_correction_level::*; -pub use format_information::*; \ No newline at end of file +pub use format_information::*; +pub use data_block::*; \ No newline at end of file diff --git a/src/qrcode/encoder/encoder.rs b/src/qrcode/encoder/encoder.rs index 9e89758..faec31e 100644 --- a/src/qrcode/encoder/encoder.rs +++ b/src/qrcode/encoder/encoder.rs @@ -37,15 +37,21 @@ use std::collections::HashMap; use encoding::EncodingRef; -use crate::{EncodingHintDictionary, common::{BitArray, CharacterSetECI, reedsolomon::{ReedSolomonEncoder, get_predefined_genericgf, PredefinedGenericGF}, StringUtils}, Exceptions, qrcode::decoder::{ErrorCorrectionLevel, Mode, VersionRef, Version}}; +use lazy_static::lazy_static; -use super::{mask_util, ByteMatrix, QRCode, matrix_util}; +use crate::{EncodingHintDictionary, common::{BitArray, CharacterSetECI, reedsolomon::{ReedSolomonEncoder, get_predefined_genericgf, PredefinedGenericGF}, StringUtils}, Exceptions, qrcode::decoder::{ErrorCorrectionLevel, Mode, VersionRef, Version}, EncodeHintType, EncodeHintValue}; + +use super::{mask_util, ByteMatrix, QRCode, matrix_util, MinimalEncoder, BlockPair}; /** * @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(); + } + // 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 @@ -75,131 +81,168 @@ use super::{mask_util, ByteMatrix, QRCode, matrix_util}; * @throws WriterException if encoding can't succeed, because of for example invalid content * or configuration */ - pub fn encode( content:&str, ecLevel:&ErrorCorrectionLevel) -> Result { + 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, + ecLevel:ErrorCorrectionLevel, hints:EncodingHintDictionary) -> Result { let version; let headerAndDataBits; let mode; - let hasGS1FormatHint = hints != null && hints.containsKey(EncodeHintType::GS1_FORMAT) && - Boolean.parseBoolean(hints.get(EncodeHintType::GS1_FORMAT).toString()); - let hasCompactionHint = hints != null && hints.containsKey(EncodeHintType::QR_COMPACT) && - Boolean.parseBoolean(hints.get(EncodeHintType::QR_COMPACT).toString()); + 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 hasEncodingHint = hints != null && hints.containsKey(EncodeHintType::CHARACTER_SET); - if (hasEncodingHint) { - encoding = Charset.forName(hints.get(EncodeHintType.CHARACTER_SET).toString()); + 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 (hasCompactionHint) { - mode = Mode.BYTE; + if hasCompactionHint { + mode = Mode::BYTE; - Charset priorityEncoding = encoding.equals(DEFAULT_BYTE_MODE_ENCODING) ? null : encoding; - MinimalEncoder.RXingResultList rn = MinimalEncoder.encode(content, null, priorityEncoding, hasGS1FormatHint, ecLevel); + 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 = new BitArray(); - rn.getBits(headerAndDataBits); + headerAndDataBits = BitArray::new(); + rn.getBits(&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 = chooseMode(content, encoding); + mode = chooseModeWithEncoding(content, encoding); // This will store the header information, like mode and // length, as well as "header" segments like an ECI segment. - BitArray headerBits = new BitArray(); + let headerBits = BitArray::new(); // Append ECI segment if applicable - if (mode == Mode.BYTE && hasEncodingHint) { - CharacterSetECI eci = CharacterSetECI.getCharacterSetECI(encoding); - if (eci != null) { - appendECI(eci, headerBits); + if mode == Mode::BYTE && hasEncodingHint { + let eci = CharacterSetECI::getCharacterSetECI(encoding); + if eci.is_some() { + appendECI(&eci.unwrap(), &headerBits); } } // Append the FNC1 mode header for GS1 formatted data if applicable - if (hasGS1FormatHint) { + if hasGS1FormatHint { // GS1 formatted codes are prefixed with a FNC1 in first position mode header - appendModeInfo(Mode.FNC1_FIRST_POSITION, headerBits); + appendModeInfo(Mode::FNC1_FIRST_POSITION, &headerBits); } // (With ECI in place,) Write the mode marker - appendModeInfo(mode, headerBits); + appendModeInfo(mode, &headerBits); // Collect data within the main segment, separately, to count its size if needed. Don't add it to // main payload yet. - BitArray dataBits = new BitArray(); - appendBytes(content, mode, dataBits, encoding); + let dataBits = BitArray::new(); + appendBytes(content, mode, &dataBits, encoding); - if (hints != null && hints.containsKey(EncodeHintType.QR_VERSION)) { - int versionNumber = Integer.parseInt(hints.get(EncodeHintType.QR_VERSION).toString()); - version = Version.getVersionForNumber(versionNumber); - int bitsNeeded = calculateBitsNeeded(mode, headerBits, dataBits, version); - if (!willFit(bitsNeeded, version, ecLevel)) { - throw new WriterException("Data too big for requested version"); + 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); + version = recommendVersion(&ecLevel, mode, &headerBits, &dataBits)?; } - headerAndDataBits = new BitArray(); + headerAndDataBits = BitArray::new(); headerAndDataBits.appendBitArray(headerBits); // Find "length" of main segment and write it - int numLetters = mode == Mode.BYTE ? dataBits.getSizeInBytes() : content.length(); - appendLengthInfo(numLetters, version, mode, headerAndDataBits); + 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); } - Version.ECBlocks ecBlocks = version.getECBlocksForLevel(ecLevel); - int numDataBytes = version.getTotalCodewords() - ecBlocks.getTotalECCodewords(); + let ecBlocks = version.getECBlocksForLevel(ecLevel); + let numDataBytes = version.getTotalCodewords() - ecBlocks.getTotalECCodewords(); // Terminate the bits properly. - terminateBits(numDataBytes, headerAndDataBits); + terminateBits(numDataBytes, &headerAndDataBits); // Interleave data bits with error correction code. - BitArray finalBits = interleaveWithECBytes(headerAndDataBits, + let finalBits = interleaveWithECBytes(&headerAndDataBits, version.getTotalCodewords(), numDataBytes, - ecBlocks.getNumBlocks()); + ecBlocks.getNumBlocks())?; - QRCode qrCode = new QRCode(); + let qrCode = QRCode::new(); qrCode.setECLevel(ecLevel); qrCode.setMode(mode); qrCode.setVersion(version); // Choose the mask pattern and set to "qrCode". - int dimension = version.getDimensionForVersion(); - ByteMatrix matrix = new ByteMatrix(dimension, dimension); + let dimension = version.getDimensionForVersion(); + let matrix = ByteMatrix::new(dimension, dimension); // Enable manual selection of the pattern to be used via hint - int maskPattern = -1; - if (hints != null && hints.containsKey(EncodeHintType.QR_MASK_PATTERN)) { - int hintMaskPattern = Integer.parseInt(hints.get(EncodeHintType.QR_MASK_PATTERN).toString()); - maskPattern = QRCode.isValidMaskPattern(hintMaskPattern) ? hintMaskPattern : -1; + let 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}; } - if (maskPattern == -1) { - maskPattern = chooseMaskPattern(finalBits, ecLevel, version, matrix); + if maskPattern == -1 { + maskPattern = chooseMaskPattern(&finalBits, &ecLevel, version, &matrix)? as i32; } qrCode.setMaskPattern(maskPattern); // Build the matrix and set it to "qrCode". - matrix_util::buildMatrix(finalBits, ecLevel, version, maskPattern, matrix); + matrix_util::buildMatrix(&finalBits, &ecLevel, version, maskPattern, &mut matrix); qrCode.setMatrix(matrix); - return qrCode; + Ok( qrCode) } /** @@ -214,8 +257,8 @@ use super::{mask_util, ByteMatrix, QRCode, matrix_util}; // 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 provisionalVersion = chooseVersion(provisionalBitsNeeded, ecLevel); + 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); @@ -226,56 +269,60 @@ use super::{mask_util, ByteMatrix, QRCode, matrix_util}; headerBits:&BitArray, dataBits:&BitArray, version:VersionRef) -> u32 { - return headerBits.getSize() + mode.getCharacterCountBits(version) + dataBits.getSize(); + (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) -> u32{ + pub fn getAlphanumericCode( code:u32) -> i8{ + let code = code as usize; if code < ALPHANUMERIC_TABLE.len() { - return ALPHANUMERIC_TABLE[code]; + ALPHANUMERIC_TABLE[code] + }else{ + -1 } - return -1; } pub fn chooseMode( content:&str) -> Mode{ - return chooseModeWithEncoding(content, None); + 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:Option) -> Mode{ - if (StringUtils.SHIFT_JIS_CHARSET.equals(encoding) && isOnlyDoubleByteKanji(content)) { + 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; + return Mode::KANJI; } - boolean hasNumeric = false; - boolean hasAlphanumeric = false; - for (int i = 0; i < content.length(); ++i) { - char c = content.charAt(i); - if (c >= '0' && c <= '9') { + let hasNumeric = false; + let 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) != -1) { + } else if (getAlphanumericCode(c as u32) != -1) { hasAlphanumeric = true; } else { - return Mode.BYTE; + return Mode::BYTE; } } if (hasAlphanumeric) { - return Mode.ALPHANUMERIC; + return Mode::ALPHANUMERIC; } if (hasNumeric) { - return Mode.NUMERIC; + return Mode::NUMERIC; } - return Mode.BYTE; + return Mode::BYTE; } pub fn isOnlyDoubleByteKanji( content:&str) -> bool{ - let bytes = content.getBytes(StringUtils::SHIFT_JIS_CHARSET); + let bytes = SHIFT_JIS_CHARSET.encode(content,encoding::EncoderTrap::Strict).expect("encode"); let length = bytes.len(); if length % 2 != 0 { return false; @@ -302,25 +349,25 @@ use super::{mask_util, ByteMatrix, QRCode, matrix_util}; // 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, matrix); + matrix_util::buildMatrix(bits, ecLevel, version, maskPattern, &mut matrix); let penalty = calculateMaskPenalty(matrix); - if (penalty < minPenalty) { + if penalty < minPenalty { minPenalty = penalty; bestMaskPattern = maskPattern; } } - return bestMaskPattern; + Ok( bestMaskPattern as u32 ) } fn chooseVersion( numInputBits:u32, ecLevel:&ErrorCorrectionLevel) -> Result { for versionNum in 1..=40 { // for (int versionNum = 1; versionNum <= 40; versionNum++) { - let version = Version::getVersionForNumber(versionNum); + let version = Version::getVersionForNumber(versionNum)?; if willFit(numInputBits, version, ecLevel) { - return version; + return Ok(version); } } - Err(Exceptions::WriterException("Data too big".to_owned())); + Err(Exceptions::WriterException("Data too big".to_owned())) } /** @@ -332,7 +379,7 @@ use super::{mask_util, ByteMatrix, QRCode, matrix_util}; // numBytes = 196 let numBytes = version.getTotalCodewords(); // getNumECBytes = 130 - let ecBlocks = version.getECBlocksForLevel(ecLevel); + let ecBlocks = version.getECBlocksForLevel(*ecLevel); let numEcBytes = ecBlocks.getTotalECCodewords(); // getNumDataBytes = 196 - 130 = 66 let numDataBytes = numBytes - numEcBytes; @@ -345,30 +392,39 @@ use super::{mask_util, ByteMatrix, QRCode, matrix_util}; */ pub fn terminateBits( numDataBytes:u32, bits:&BitArray) -> Result<(),Exceptions> { let capacity = numDataBytes * 8; - if (bits.getSize() > capacity) { - throw new WriterException("data bits cannot fit in the QR Code" + bits.getSize() + " > " + - capacity); + 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); } // Append Mode.TERMINATE if there is enough space (value is 0000) - for (int i = 0; i < 4 && bits.getSize() < capacity; ++i) { + for i in 0..4 { + 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. - int numBitsInLastByte = bits.getSize() & 0x07; - if (numBitsInLastByte > 0) { - for (int i = numBitsInLastByte; i < 8; i++) { + let numBitsInLastByte = bits.getSize() & 0x07; + if numBitsInLastByte > 0 { + 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). - int numPaddingBytes = numDataBytes - bits.getSizeInBytes(); - for (int i = 0; i < numPaddingBytes; ++i) { - bits.appendBits((i & 0x01) == 0 ? 0xEC : 0x11, 8); + 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); } - if (bits.getSize() != capacity) { - throw new WriterException("Bits size does not equal capacity"); + 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"); } + Ok(()) } /** @@ -383,32 +439,36 @@ use super::{mask_util, ByteMatrix, QRCode, matrix_util}; numDataBytesInBlock:&[u32], numECBytesInBlock:&[u32]) -> Result<(),Exceptions> { if blockID >= numRSBlocks { - 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 - int numRsBlocksInGroup2 = numTotalBytes % numRSBlocks; + let numRsBlocksInGroup2 = numTotalBytes % numRSBlocks; // numRsBlocksInGroup1 = 5 - 1 = 4 - int numRsBlocksInGroup1 = numRSBlocks - numRsBlocksInGroup2; + let numRsBlocksInGroup1 = numRSBlocks - numRsBlocksInGroup2; // numTotalBytesInGroup1 = 196 / 5 = 39 - int numTotalBytesInGroup1 = numTotalBytes / numRSBlocks; + let numTotalBytesInGroup1 = numTotalBytes / numRSBlocks; // numTotalBytesInGroup2 = 39 + 1 = 40 - int numTotalBytesInGroup2 = numTotalBytesInGroup1 + 1; + let numTotalBytesInGroup2 = numTotalBytesInGroup1 + 1; // numDataBytesInGroup1 = 66 / 5 = 13 - int numDataBytesInGroup1 = numDataBytes / numRSBlocks; + let numDataBytesInGroup1 = numDataBytes / numRSBlocks; // numDataBytesInGroup2 = 13 + 1 = 14 - int numDataBytesInGroup2 = numDataBytesInGroup1 + 1; + let numDataBytesInGroup2 = numDataBytesInGroup1 + 1; // numEcBytesInGroup1 = 39 - 13 = 26 - int numEcBytesInGroup1 = numTotalBytesInGroup1 - numDataBytesInGroup1; + let numEcBytesInGroup1 = numTotalBytesInGroup1 - numDataBytesInGroup1; // numEcBytesInGroup2 = 40 - 14 = 26 - int numEcBytesInGroup2 = numTotalBytesInGroup2 - numDataBytesInGroup2; + let numEcBytesInGroup2 = numTotalBytesInGroup2 - numDataBytesInGroup2; // Sanity checks. // 26 = 26 if (numEcBytesInGroup1 != numEcBytesInGroup2) { - 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) { - throw new WriterException("RS blocks mismatch"); + return Err(Exceptions::WriterException("RS blocks mismatch".to_owned())) + + // throw new WriterException("RS blocks mismatch"); } // 196 = (13 + 26) * 4 + (14 + 26) * 1 if (numTotalBytes != @@ -416,7 +476,9 @@ use super::{mask_util, ByteMatrix, QRCode, matrix_util}; numRsBlocksInGroup1) + ((numDataBytesInGroup2 + numEcBytesInGroup2) * numRsBlocksInGroup2)) { - throw new WriterException("Total bytes mismatch"); + return Err(Exceptions::WriterException("Total bytes mismatch".to_owned())) + + // throw new WriterException("Total bytes mismatch"); } if (blockID < numRsBlocksInGroup1) { @@ -426,6 +488,7 @@ use super::{mask_util, ByteMatrix, QRCode, matrix_util}; numDataBytesInBlock[0] = numDataBytesInGroup2; numECBytesInBlock[0] = numEcBytesInGroup2; } + Ok(()) } /** @@ -438,7 +501,7 @@ use super::{mask_util, ByteMatrix, QRCode, matrix_util}; numRSBlocks:u32) -> Result { // "bits" must have "getNumDataBytes" bytes of data. - if bits.getSizeInBytes() != numDataBytes { + if bits.getSizeInBytes() as u32 != numDataBytes { return Err(Exceptions::WriterException("Number of bits and data bytes does not match".to_owned())) } @@ -453,60 +516,72 @@ use super::{mask_util, ByteMatrix, QRCode, matrix_util}; for i in 0..numRSBlocks { // for (int i = 0; i < numRSBlocks; ++i) { - let numDataBytesInBlock = new int[1]; - let numEcBytesInBlock = new int[1]; + let numDataBytesInBlock = vec![0;1];//new int[1]; + let numEcBytesInBlock = vec![0;1];//new int[1]; getNumDataBytesAndNumECBytesForBlockID( numTotalBytes, numDataBytes, numRSBlocks, i, - numDataBytesInBlock, numEcBytesInBlock); + &numDataBytesInBlock, &numEcBytesInBlock); let size = numDataBytesInBlock[0]; - let dataBytes = new byte[size]; - bits.toBytes(8 * dataBytesOffset, dataBytes, 0, size); - let ecBytes = generateECBytes(dataBytes, numEcBytesInBlock[0]); - blocks.add(new BlockPair(dataBytes, ecBytes)); + 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)); - maxNumDataBytes = Math.max(maxNumDataBytes, size); - maxNumEcBytes = Math.max(maxNumEcBytes, ecBytes.length); - dataBytesOffset += numDataBytesInBlock[0]; + maxNumDataBytes = maxNumDataBytes.max( size); + maxNumEcBytes = maxNumEcBytes.max( ecBytes.len()); + dataBytesOffset += numDataBytesInBlock[0] as usize; } - if (numDataBytes != dataBytesOffset) { + if numDataBytes != dataBytesOffset as u32 { return Err(Exceptions::WriterException("Data bytes does not match offset".to_owned())) } let result = BitArray::new(); // First, place data blocks. - for (int i = 0; i < maxNumDataBytes; ++i) { - for (BlockPair block : blocks) { - byte[] dataBytes = block.getDataBytes(); - if (i < dataBytes.length) { - result.appendBits(dataBytes[i], 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 (int i = 0; i < maxNumEcBytes; ++i) { - for (BlockPair block : blocks) { - byte[] ecBytes = block.getErrorCorrectionBytes(); - if (i < ecBytes.length) { - result.appendBits(ecBytes[i], 8); + 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); } } } - if (numTotalBytes != result.getSizeInBytes()) { // Should be same. - throw new WriterException("Interleaving error: " + numTotalBytes + " and " + - result.getSizeInBytes() + " differ."); + 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."); } - return result; + Ok(result) } - pub fn generateECBytes( dataBytes:&[u8], numEcBytesInBlock:u32) -> Vec { - let numDataBytes = dataBytes.length; + pub fn generateECBytes( dataBytes:&[u8], numEcBytesInBlock:usize) -> Vec { + let numDataBytes = dataBytes.len(); let toEncode = vec![0;numDataBytes + numEcBytesInBlock]; for i in 0..numDataBytes { // for (int i = 0; i < numDataBytes; i++) { - toEncode[i] = dataBytes[i] & 0xFF; + toEncode[i] = dataBytes[i] as i32; } ReedSolomonEncoder::new(get_predefined_genericgf(PredefinedGenericGF::QrCodeField256)).encode(&mut toEncode, numEcBytesInBlock); @@ -514,7 +589,7 @@ use super::{mask_util, ByteMatrix, QRCode, matrix_util}; let ecBytes = vec![0u8;numEcBytesInBlock]; for i in 0..numEcBytesInBlock { // for (int i = 0; i < numEcBytesInBlock; i++) { - ecBytes[i] = toEncode[numDataBytes + i]; + ecBytes[i] = toEncode[numDataBytes + i] as u8; } return ecBytes; } @@ -523,7 +598,7 @@ use super::{mask_util, ByteMatrix, QRCode, matrix_util}; * Append mode info. On success, store the result in "bits". */ pub fn appendModeInfo( mode:Mode, bits:&BitArray) { - bits.appendBits(mode.getBits(), 4); + bits.appendBits(mode.getBits() as u32, 4); } @@ -533,9 +608,9 @@ use super::{mask_util, ByteMatrix, QRCode, matrix_util}; pub fn appendLengthInfo( numLetters:u32, version:VersionRef, mode:Mode, bits:&BitArray) -> Result<(),Exceptions> { let numBits = mode.getCharacterCountBits(version); if numLetters >= (1 << numBits) { - return Err(Exceptions::WriterExceptin(format!("{} is bigger than {}" ,numLetters , ((1 << numBits) - 1)))) + return Err(Exceptions::WriterException(format!("{} is bigger than {}" ,numLetters , ((1 << numBits) - 1)))) } - bits.appendBits(numLetters, numBits); + bits.appendBits(numLetters, numBits as usize); Ok(()) } @@ -547,12 +622,13 @@ use super::{mask_util, ByteMatrix, QRCode, matrix_util}; bits:&BitArray, encoding:EncodingRef) -> Result<(),Exceptions>{ match mode { - Mode::NUMERIC => Ok(appendNumericBytes(content, bits)), - Mode::ALPHANUMERIC => Ok(appendAlphanumericBytes(content, bits)), - Mode::BYTE => Ok(append8BitBytes(content, bits, encoding)), - Mode::KANJI => Ok(appendKanjiBytes(content, bits)), - _=> Err(Exceptions::WriterException(format!("Invalid mode: {}" , 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); @@ -572,24 +648,24 @@ use super::{mask_util, ByteMatrix, QRCode, matrix_util}; } pub fn appendNumericBytes( content:&str, bits:&BitArray) { - let length = content.length(); + let length = content.len(); let i = 0; while i < length { - let num1 = content.charAt(i) - '0'; + 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.charAt(i + 1) - '0'; - let num3 = content.charAt(i + 2) - '0'; - bits.appendBits(num1 * 100 + num2 * 10 + num3, 10); + 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.charAt(i + 1) - '0'; - bits.appendBits(num1 * 10 + num2, 7); + 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, 4); + bits.appendBits(num1 as u32, 4); i+=1; } } @@ -599,22 +675,22 @@ use super::{mask_util, ByteMatrix, QRCode, matrix_util}; let length = content.len(); let i = 0; while i < length { - let code1 = getAlphanumericCode(content.charAt(i)); + 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.charAt(i + 1)); + 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, 11); + bits.appendBits((code1 * 45 + code2) as u32, 11); i += 2; } else { // Encode one alphanumeric letter in six bits. - bits.appendBits(code1, 6); + bits.appendBits(code1 as u32, 6); i+=1; } } @@ -622,36 +698,40 @@ use super::{mask_util, ByteMatrix, QRCode, matrix_util}; } fn append8BitBytes( content:&str, bits:&BitArray, encoding:EncodingRef) { - let bytes = content.getBytes(encoding); + 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, 8); + bits.appendBits(b as u32, 8); } } fn appendKanjiBytes( content:&str, bits:&BitArray) -> Result<(),Exceptions> { - let bytes = content.getBytes(StringUtils::SHIFT_JIS_CHARSET); - if bytes.length % 2 != 0 { + 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())) } - let maxI = bytes.length - 1; // bytes.length must be even + 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 = -1; + let subtracted:i32 = -1; if code >= 0x8140 && code <= 0x9ffc { - subtracted = code - 0x8140; - } else if (code >= 0xe040 && code <= 0xebbf) { - subtracted = code - 0xc140; + 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, 13); + bits.appendBits(encoded as u32, 13); i+=2; } @@ -659,8 +739,8 @@ use super::{mask_util, ByteMatrix, QRCode, matrix_util}; } fn appendECI( eci:&CharacterSetECI, bits:&BitArray) { - bits.appendBits(Mode::ECI.getBits(), 4); + 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.getValue(), 8); + bits.appendBits(eci.getValueSelf(), 8); } diff --git a/src/qrcode/encoder/minimal_encoder.rs b/src/qrcode/encoder/minimal_encoder.rs index 5f71626..b4692a3 100644 --- a/src/qrcode/encoder/minimal_encoder.rs +++ b/src/qrcode/encoder/minimal_encoder.rs @@ -77,15 +77,15 @@ impl fmt::Display for VersionSize { * * @author Alex Geller */ -pub struct MinimalEncoder { +pub struct MinimalEncoder<'a> { - stringToEncode: String, + stringToEncode: &'a str, isGS1: bool, encoders: ECIEncoderSet, ecLevel:ErrorCorrectionLevel, } -impl MinimalEncoder { +impl MinimalEncoder<'_> { /** * Creates a MinimalEncoder @@ -99,7 +99,7 @@ impl MinimalEncoder { * @param ecLevel The error correction level. * @see RXingResultList#getVersion */ - pub fn new( stringToEncode:String, priorityCharset: EncodingRef, isGS1:bool, ecLevel:ErrorCorrectionLevel) -> Self { + pub fn new( stringToEncode:&str, priorityCharset: EncodingRef, isGS1:bool, ecLevel:ErrorCorrectionLevel) -> Self { Self { stringToEncode, isGS1, @@ -128,9 +128,9 @@ impl MinimalEncoder { * @see RXingResultList#getVersion * @see RXingResultList#getSize */ - pub fn encode_with_details<'a>( stringToEncode:String, version:VersionRef, priorityCharset:EncodingRef, isGS1:bool, + 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(Some(version)) + MinimalEncoder::new(stringToEncode, priorityCharset, isGS1, ecLevel).encode(version) } pub fn encode(&self, version:Option) -> Result { @@ -138,29 +138,30 @@ impl MinimalEncoder { 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 results = [ self.encodeSpecificVersion(&versions[0])?, + self.encodeSpecificVersion(&versions[1])?, + self.encodeSpecificVersion(&versions[2])? ]; let smallestSize = u32::MAX; - let smallestRXingResult = -1; + 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 { + if encoder::willFit(size, versions[i], &self.ecLevel) && size < smallestSize { smallestSize = size; - smallestRXingResult = i; + smallestRXingResult = i as i32; } } if smallestRXingResult < 0 { return Err(Exceptions::WriterException("Data too big for any version".to_owned())); } - return results[smallestRXingResult]; + Ok(results[smallestRXingResult as usize]) } else { // compute minimal encoding for a given version - let result = self.encodeSpecificVersion(version); - if !encoder::willFit(result.getSize(), Self::getVersion(Self::getVersionSize(result.getVersion())), self.ecLevel) { + 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))); } - return result; + Ok(result) } } @@ -191,11 +192,11 @@ impl MinimalEncoder { } pub fn isDoubleByteKanji( c:char) -> bool{ - return encoder::isOnlyDoubleByteKanji(String.valueOf(c)); + return encoder::isOnlyDoubleByteKanji(&String::from(c)); } pub fn isAlphanumeric( c: char) -> bool{ - return encoder::getAlphanumericCode(c) != -1; + return encoder::getAlphanumericCode(c as u8 as u32) != -1; } pub fn canEncode(&self, mode:&Mode, c:char) -> bool { @@ -235,45 +236,45 @@ impl MinimalEncoder { // } } - pub fn addEdge(&self, edges:&Vec>>, position:u32, edge:Option<&Edge>) { - let vertexIndex = position + edge.as_ref().unwrap().characterLength; + 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] == null ||*/ modeEdges[modeOrdinal].cachedTotalSize > (&edge).unwrap().cachedTotalSize { - modeEdges[modeOrdinal] = edge.unwrap(); + if modeEdges[modeOrdinal].is_none() || modeEdges[modeOrdinal].as_ref().unwrap().cachedTotalSize > (&edge).unwrap().cachedTotalSize { + modeEdges[modeOrdinal] = edge; } } - pub fn addEdges( &self, version:&Edge, edges:&Vec>>, from:u32, previous:&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).unwrap(),priorityEncoderIndex) { + 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(), i) { - self.addEdge(edges, from, Edge::new(Mode::BYTE, from, i, 1, previous, version)); + 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, Edge::(Mode::KANJI, from, 0, 1, previous, version)); + 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.charAt(from)) { - self.addEdge(edges, from, Edge::new(Mode::ALPHANUMERIC, from, 0, from + 1 >= inputLength || - !self.canEncode(Mode::ALPHANUMERIC, self.stringToEncode.charAt(from + 1)) ? 1 : 2, previous, version)); + 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, Edge::new(Mode::NUMERIC, from, 0, from + 1 >= inputLength || - !self.canEncode(Mode::NUMERIC, self.stringToEncode.charAt(from + 1)) ? 1 : from + 2 >= inputLength || - !self.canEncode(Mode::NUMERIC, self.stringToEncode.charAt(from + 2)) ? 2 : 3, previous, version)); + 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 >{ @@ -396,7 +397,7 @@ impl MinimalEncoder { // 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![]]];//new Edge[inputLength + 1][encoders.length()][4]; + 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 { @@ -405,42 +406,42 @@ impl MinimalEncoder { // 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] != null && i < inputLength { - self.addEdges(version, edges, i, edges[i][j][k]); + if edges[i][j][k].is_some() && i < inputLength { + self.addEdges(version, &edges, i, edges[i][j][k]); } } } } - let minimalJ = -1; - let minimalK = -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] != null { - let edge = edges[inputLength][j][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 = j; - minimalK = k; + minimalJ = Some(j); + minimalK = Some(k); } } } } - if minimalJ < 0 { + if minimalJ.is_none() { return Err(Exceptions::WriterException(format!(r#"Internal error: failed to encode "{}"#,self.stringToEncode))); } - Ok(RXingResultList::new(version, edges[inputLength][minimalJ][minimalK])) + Ok(RXingResultList::new(version, edges[inputLength][minimalJ.unwrap()][minimalK.unwrap()].unwrap(),self.isGS1,&self.ecLevel, &self.encoders, &self.stringToEncode)) } } struct Edge<'a> { - mode:Mode, - fromPosition:u32, - charsetEncoderIndex:u32, + pub mode:Mode, + fromPosition:usize, + charsetEncoderIndex:usize, characterLength:u32, previous:Option<&'a Edge<'a>>, cachedTotalSize:u32, @@ -449,7 +450,7 @@ struct Edge<'a> { } impl Edge<'_> { - pub fn new( mode:Mode, fromPosition:u32, charsetEncoderIndex:u32, characterLength:u32, previous:Option<&'_ Edge>, + 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}; @@ -474,7 +475,7 @@ impl Edge<'_> { 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], + 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 @@ -549,7 +550,7 @@ struct RXingResultList<'a> { } impl RXingResultList<'_> { - pub fn new( version:VersionRef, solution:&'_ Edge, isGS1:bool, ecLevel: &ErrorCorrectionLevel) -> Self { + 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; @@ -568,12 +569,12 @@ impl RXingResultList<'_> { } if previous.is_none() || previous.as_ref().unwrap().mode != current.as_ref().unwrap().mode || needECI { - list.push( RXingResultNode::new(current.mode, current.fromPosition, current.charsetEncoderIndex, length)); + 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 needECI { - list.push( RXingResultNode::new(Mode::ECI, current.fromPosition, current.charsetEncoderIndex, 0)); + list.push( RXingResultNode::new(Mode::ECI, current.as_ref().unwrap().fromPosition, current.as_ref().unwrap().charsetEncoderIndex, 0,encoders,stringToEncode, version)); } current = previous; } @@ -585,12 +586,12 @@ impl RXingResultList<'_> { // if first != null && first.mode != Mode.ECI && containsECI { if first.mode != Mode::ECI && containsECI { // prepend a default character set ECI - list.push(0, RXingResultNode::new(Mode::ECI, 0, 0, 0)); + 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)); + list.push( RXingResultNode::new(Mode::FNC1_FIRST_POSITION, 0, 0, 0,encoders,stringToEncode,version)); } // set version to smallest version into which the bits fit. @@ -621,12 +622,12 @@ impl RXingResultList<'_> { // } let size = Self::internal_static_get_size(version,&list); // increase version if needed - while versionNumber < upperLimit && !encoder::willFit(size, Version::getVersionForNumber(versionNumber), + 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), + while versionNumber > lowerLimit && encoder::willFit(size, Version::getVersionForNumber(versionNumber - 1).unwrap(), ecLevel) { versionNumber-=1; } @@ -652,7 +653,7 @@ impl RXingResultList<'_> { return result; } - fn internal_static_get_size(version:VersionRef, list: &Vec) { + fn internal_static_get_size(version:VersionRef, list: &Vec) -> u32 { let result = 0; for resultNode in list { result += resultNode.getSize(version); @@ -694,8 +695,8 @@ impl fmt::Display for RXingResultList<'_> { struct RXingResultNode<'a> { mode:Mode, - fromPosition:u32, - charsetEncoderIndex:u32, + fromPosition:usize, + charsetEncoderIndex:usize, characterLength:u32, encoders:&'a ECIEncoderSet, version: VersionRef, @@ -704,7 +705,7 @@ struct RXingResultNode<'a> { impl RXingResultNode<'_> { - pub fn new( mode:Mode, fromPosition:u32, charsetEncoderIndex:u32, characterLength:u32, encoders:&'_ ECIEncoderSet, stringToEncode: &'_ str, version: &'_ Version) -> Self { + pub fn new( mode:Mode, fromPosition:usize, charsetEncoderIndex:usize, characterLength:u32, encoders:&'_ ECIEncoderSet, stringToEncode: &'_ str, version: &'_ Version) -> Self { Self { mode, fromPosition, @@ -764,7 +765,7 @@ impl RXingResultNode<'_> { */ 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.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} } @@ -775,13 +776,13 @@ impl RXingResultNode<'_> { 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_ref()) as usize); + 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, + 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(()) @@ -809,7 +810,7 @@ impl RXingResultNode<'_> { 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_str(&Self::makePrintable(&self.stringToEncode[self.fromPosition as usize..(self.fromPosition + self.characterLength as usize)])); } result.push(')'); diff --git a/src/qrcode/encoder/qr_code.rs b/src/qrcode/encoder/qr_code.rs index e0a4181..0ecc1b2 100644 --- a/src/qrcode/encoder/qr_code.rs +++ b/src/qrcode/encoder/qr_code.rs @@ -20,7 +20,6 @@ use crate::qrcode::decoder::{Mode, ErrorCorrectionLevel, Version}; use super::ByteMatrix; - const NUM_MASK_PATTERNS : i32 = 8; /** * @author satorux@google.com (Satoru Takabayashi) - creator @@ -39,6 +38,8 @@ pub struct QRCode { } impl QRCode { + pub const NUM_MASK_PATTERNS : i32 = 8; + pub fn new()->Self { Self { mode: None, @@ -96,7 +97,7 @@ impl QRCode { // Check if "mask_pattern" is valid. pub fn isValidMaskPattern( maskPattern:i32) -> bool{ - maskPattern >= 0 && maskPattern < NUM_MASK_PATTERNS + maskPattern >= 0 && maskPattern < Self::NUM_MASK_PATTERNS } }