From 2a80c95d754622580f4681667a195d7f88ec15e8 Mon Sep 17 00:00:00 2001 From: Henry Schimke Date: Fri, 30 Sep 2022 18:10:04 -0500 Subject: [PATCH] steady updates for qr --- src/qrcode/decoder/error_correction_level.rs | 16 +- src/qrcode/decoder/mode.rs | 189 +++++----- src/qrcode/decoder/version.rs | 345 ++++++++++--------- 3 files changed, 314 insertions(+), 236 deletions(-) diff --git a/src/qrcode/decoder/error_correction_level.rs b/src/qrcode/decoder/error_correction_level.rs index ab25d70..79064d6 100644 --- a/src/qrcode/decoder/error_correction_level.rs +++ b/src/qrcode/decoder/error_correction_level.rs @@ -39,7 +39,7 @@ impl ErrorCorrectionLevel { * @param bits int containing the two bits encoding a QR Code's error correction level * @return ErrorCorrectionLevel representing the encoded error correction level */ - pub fn forBits(bits: u32) -> Result { + pub fn forBits(bits: u8) -> Result { match bits { 0 => Ok(Self::M), 1 => Ok(Self::L), @@ -62,6 +62,20 @@ impl ErrorCorrectionLevel { } } +impl TryFrom for ErrorCorrectionLevel { + type Error = Exceptions; + + fn try_from(value: u8) -> Result { + ErrorCorrectionLevel::forBits(value) + } +} + +impl From for u8 { + fn from(value: ErrorCorrectionLevel) -> Self { + value.get_value() + } +} + //private static final ErrorCorrectionLevel[] FOR_BITS = {M, L, H, Q}; //private final int bits; diff --git a/src/qrcode/decoder/mode.rs b/src/qrcode/decoder/mode.rs index 9ad741c..cce25e3 100644 --- a/src/qrcode/decoder/mode.rs +++ b/src/qrcode/decoder/mode.rs @@ -14,87 +14,122 @@ * limitations under the License. */ +use crate::Exceptions; + /** *

See ISO 18004:2006, 6.4.1, Tables 2 and 3. This enum encapsulates the various modes in which * data can be encoded to bits in the QR code standard.

* * @author Sean Owen */ -public enum Mode { - - TERMINATOR(new int[]{0, 0, 0}, 0x00), // Not really a mode... - NUMERIC(new int[]{10, 12, 14}, 0x01), - ALPHANUMERIC(new int[]{9, 11, 13}, 0x02), - STRUCTURED_APPEND(new int[]{0, 0, 0}, 0x03), // Not supported - BYTE(new int[]{8, 16, 16}, 0x04), - ECI(new int[]{0, 0, 0}, 0x07), // character counts don't apply - KANJI(new int[]{8, 10, 12}, 0x08), - FNC1_FIRST_POSITION(new int[]{0, 0, 0}, 0x05), - FNC1_SECOND_POSITION(new int[]{0, 0, 0}, 0x09), - /** See GBT 18284-2000; "Hanzi" is a transliteration of this mode name. */ - HANZI(new int[]{8, 10, 12}, 0x0D); - - private final int[] characterCountBitsForVersions; - private final int bits; - - Mode(int[] characterCountBitsForVersions, int bits) { - this.characterCountBitsForVersions = characterCountBitsForVersions; - this.bits = bits; - } - - /** - * @param bits four bits encoding a QR Code data mode - * @return Mode encoded by these bits - * @throws IllegalArgumentException if bits do not correspond to a known mode - */ - public static Mode forBits(int bits) { - switch (bits) { - case 0x0: - return TERMINATOR; - case 0x1: - return NUMERIC; - case 0x2: - return ALPHANUMERIC; - case 0x3: - return STRUCTURED_APPEND; - case 0x4: - return BYTE; - case 0x5: - return FNC1_FIRST_POSITION; - case 0x7: - return ECI; - case 0x8: - return KANJI; - case 0x9: - return FNC1_SECOND_POSITION; - case 0xD: - // 0xD is defined in GBT 18284-2000, may not be supported in foreign country - return HANZI; - default: - throw new IllegalArgumentException(); - } - } - - /** - * @param version version in question - * @return number of bits used, in this QR Code symbol {@link Version}, to encode the - * count of characters that will follow encoded in this Mode - */ - public int getCharacterCountBits(Version version) { - int number = version.getVersionNumber(); - int offset; - if (number <= 9) { - offset = 0; - } else if (number <= 26) { - offset = 1; - } else { - offset = 2; - } - return characterCountBitsForVersions[offset]; - } - - public int getBits() { - return bits; - } - +pub enum Mode { + TERMINATOR, //(new int[]{0, 0, 0}, 0x00), // Not really a mode... + NUMERIC, //(new int[]{10, 12, 14}, 0x01), + ALPHANUMERIC, //(new int[]{9, 11, 13}, 0x02), + STRUCTURED_APPEND, //(new int[]{0, 0, 0}, 0x03), // Not supported + BYTE, //(new int[]{8, 16, 16}, 0x04), + ECI, //(new int[]{0, 0, 0}, 0x07), // character counts don't apply + KANJI, //(new int[]{8, 10, 12}, 0x08), + FNC1_FIRST_POSITION, //(new int[]{0, 0, 0}, 0x05), + FNC1_SECOND_POSITION, //(new int[]{0, 0, 0}, 0x09), + /** See GBT 18284-2000; "Hanzi" is a transliteration of this mode name. */ + HANZI, //(new int[]{8, 10, 12}, 0x0D); +} +// private final int[] characterCountBitsForVersions; +// private final int bits; + +// Mode(int[] characterCountBitsForVersions, int bits) { +// this.characterCountBitsForVersions = characterCountBitsForVersions; +// this.bits = bits; +// } + +impl Mode { + /** + * @param bits four bits encoding a QR Code data mode + * @return Mode encoded by these bits + * @throws IllegalArgumentException if bits do not correspond to a known mode + */ + pub fn forBits(bits: u8) -> Result { + match bits { + 0x0 => Ok(Self::TERMINATOR), + 0x1 => Ok(Self::NUMERIC), + 0x2 => Ok(Self::ALPHANUMERIC), + 0x3 => Ok(Self::STRUCTURED_APPEND), + 0x4 => Ok(Self::BYTE), + 0x5 => Ok(Self::FNC1_FIRST_POSITION), + 0x7 => Ok(Self::ECI), + 0x8 => Ok(Self::KANJI), + 0x9 => Ok(Self::FNC1_SECOND_POSITION), + 0xD => + // 0xD is defined in GBT 18284-2000, may not be supported in foreign country + { + Ok(Self::HANZI) + } + _ => Err(Exceptions::IllegalArgumentException(format!( + "{} is not valid", + bits + ))), + } + } + + /** + * @param version version in question + * @return number of bits used, in this QR Code symbol {@link Version}, to encode the + * count of characters that will follow encoded in this Mode + */ + pub fn getCharacterCountBits(&self, version: &Version) -> u8 { + let number = version.getVersionNumber(); + let offset = if number <= 9 { + 0 + } else if number <= 26 { + 1 + } else { + 2 + }; + self.get_character_counts()[offset] + } + + fn get_character_counts(&self) -> &[u8] { + match self { + Mode::TERMINATOR => &[0, 0, 0], + Mode::NUMERIC => &[10, 12, 14], + Mode::ALPHANUMERIC => &[9, 11, 13], + Mode::STRUCTURED_APPEND => &[0, 0, 0], + Mode::BYTE => &[8, 16, 16], + Mode::ECI => &[0, 0, 0], + Mode::KANJI => &[8, 10, 12], + Mode::FNC1_FIRST_POSITION => &[0, 0, 0], + Mode::FNC1_SECOND_POSITION => &[0, 0, 0], + Mode::HANZI => &[8, 10, 12], + } + } + + pub fn getBits(&self) -> u8 { + match self { + Mode::TERMINATOR => 0x00, + Mode::NUMERIC => 0x01, + Mode::ALPHANUMERIC => 0x02, + Mode::STRUCTURED_APPEND => 0x03, + Mode::BYTE => 0x04, + Mode::ECI => 0x07, + Mode::KANJI => 0x08, + Mode::FNC1_FIRST_POSITION => 0x05, + Mode::FNC1_SECOND_POSITION => 0x09, + Mode::HANZI => 0x0D, + } + } +} + +impl From for u8 { + fn from(value: Mode) -> Self { + value.getBits() + } +} + +impl TryFrom for Mode { + type Error = Exceptions; + + fn try_from(value: u8) -> Result { + Self::forBits(value) + } } diff --git a/src/qrcode/decoder/version.rs b/src/qrcode/decoder/version.rs index 93cdd57..e1ea522 100755 --- a/src/qrcode/decoder/version.rs +++ b/src/qrcode/decoder/version.rs @@ -14,6 +14,27 @@ * limitations under the License. */ +use std::fmt; + +use crate::{Exceptions, common::BitMatrix}; + +use super::ErrorCorrectionLevel; + + /** + * See ISO 18004:2006 Annex D. + * Element i represents the raw version bits that specify version i + 7 + */ + const VERSION_DECODE_INFO : [u32;34] = [ + 0x07C94, 0x085BC, 0x09A99, 0x0A4D3, 0x0BBF6, + 0x0C762, 0x0D847, 0x0E60D, 0x0F928, 0x10B78, + 0x1145D, 0x12A17, 0x13532, 0x149A6, 0x15683, + 0x168C9, 0x177EC, 0x18EC4, 0x191E1, 0x1AFAB, + 0x1B08E, 0x1CC1A, 0x1D33F, 0x1ED75, 0x1F250, + 0x209D5, 0x216F0, 0x228BA, 0x2379F, 0x24B0B, + 0x2542E, 0x26A64, 0x27541, 0x28C69 + ]; + + const VERSIONS :Vec = Version::buildVersions(); /** * See ISO 18004:2006 Annex D * @@ -21,62 +42,56 @@ */ pub struct Version { - /** - * See ISO 18004:2006 Annex D. - * Element i represents the raw version bits that specify version i + 7 - */ - private static final int[] VERSION_DECODE_INFO = { - 0x07C94, 0x085BC, 0x09A99, 0x0A4D3, 0x0BBF6, - 0x0C762, 0x0D847, 0x0E60D, 0x0F928, 0x10B78, - 0x1145D, 0x12A17, 0x13532, 0x149A6, 0x15683, - 0x168C9, 0x177EC, 0x18EC4, 0x191E1, 0x1AFAB, - 0x1B08E, 0x1CC1A, 0x1D33F, 0x1ED75, 0x1F250, - 0x209D5, 0x216F0, 0x228BA, 0x2379F, 0x24B0B, - 0x2542E, 0x26A64, 0x27541, 0x28C69 - }; +// private static final Version[] VERSIONS = buildVersions(); - private static final Version[] VERSIONS = buildVersions(); - - private final int versionNumber; - private final int[] alignmentPatternCenters; - private final ECBlocks[] ecBlocks; - private final int totalCodewords; + versionNumber: u32, + alignmentPatternCenters: Vec, + ecBlocks:Vec, + totalCodewords:u32, } impl Version { - private Version(int versionNumber, - int[] alignmentPatternCenters, - ECBlocks... ecBlocks) { - this.versionNumber = versionNumber; - this.alignmentPatternCenters = alignmentPatternCenters; - this.ecBlocks = ecBlocks; - int total = 0; - int ecCodewords = ecBlocks[0].getECCodewordsPerBlock(); - ECB[] ecbArray = ecBlocks[0].getECBlocks(); - for (ECB ecBlock : ecbArray) { - total += ecBlock.getCount() * (ecBlock.getDataCodewords() + ecCodewords); + const fn new( versionNumber:u32, + alignmentPatternCenters:Vec, + ecBlocks:Vec) -> Self { + + let total = 0; + let ecCodewords = ecBlocks[0].getECCodewordsPerBlock(); + let ecbArray = ecBlocks[0].getECBlocks(); + let mut i = 0; + while i < ecbArray.len() { +// for ecBlock in ecbArray { + // for (ECB ecBlock : ecbArray) { + total += ecbArray[i].getCount() * (ecbArray[i].getDataCodewords() + ecCodewords); + i+=1; + } + + Self { + versionNumber, + alignmentPatternCenters, + ecBlocks, + totalCodewords: total, } - this.totalCodewords = total; } - public int getVersionNumber() { - return versionNumber; + pub fn getVersionNumber(&self) -> u32 { + self. versionNumber } - public int[] getAlignmentPatternCenters() { - return alignmentPatternCenters; + pub fn getAlignmentPatternCenters(&self) -> &[u32]{ + &self. alignmentPatternCenters } - public int getTotalCodewords() { - return totalCodewords; + pub fn getTotalCodewords(&self) -> u32{ + self.totalCodewords } - public int getDimensionForVersion() { - return 17 + 4 * versionNumber; + pub fn getDimensionForVersion(&self)->u32 { + 17 + 4 * self.versionNumber } - public ECBlocks getECBlocksForLevel(ErrorCorrectionLevel ecLevel) { - return ecBlocks[ecLevel.ordinal()]; + pub fn getECBlocksForLevel(&self, ecLevel:ErrorCorrectionLevel) -> &ECBlocks{ + &self.ecBlocks[ecLevel.get_value() as usize] } /** @@ -86,56 +101,58 @@ impl Version { * @return Version for a QR Code of that dimension * @throws FormatException if dimension is not 1 mod 4 */ - public static Version getProvisionalVersionForDimension(int dimension) throws FormatException { - if (dimension % 4 != 1) { - throw FormatException.getFormatInstance(); - } - try { - return getVersionForNumber((dimension - 17) / 4); - } catch (IllegalArgumentException ignored) { - throw FormatException.getFormatInstance(); + pub fn getProvisionalVersionForDimension( dimension:u32) -> Result { + if dimension % 4 != 1 { + return Err(Exceptions::FormatException("dimension incorrect".to_owned())); } + Self::getVersionForNumber((dimension - 17) / 4) + // try { + // return getVersionForNumber((dimension - 17) / 4); + // } catch (IllegalArgumentException ignored) { + // throw FormatException.getFormatInstance(); + // } } - public static Version getVersionForNumber(int versionNumber) { - if (versionNumber < 1 || versionNumber > 40) { - throw new IllegalArgumentException(); + pub fn getVersionForNumber( versionNumber:u32) -> Result{ + if versionNumber < 1 || versionNumber > 40 { + return Err(Exceptions::IllegalArgumentException("version out of spec".to_owned())) } - return VERSIONS[versionNumber - 1]; + Ok(VERSIONS[versionNumber as usize - 1]) } - static Version decodeVersionInformation(int versionBits) { - int bestDifference = Integer.MAX_VALUE; - int bestVersion = 0; - for (int i = 0; i < VERSION_DECODE_INFO.length; i++) { - int targetVersion = VERSION_DECODE_INFO[i]; + pub fn decodeVersionInformation( versionBits:u32) -> Result { + let bestDifference = u32::MAX; + let bestVersion = 0; + for i in 0..VERSION_DECODE_INFO.len() as u32 { + // for (int i = 0; i < VERSION_DECODE_INFO.length; i++) { + let targetVersion = VERSION_DECODE_INFO[i]; // Do the version info bits match exactly? done. if (targetVersion == versionBits) { - return getVersionForNumber(i + 7); + return Self::getVersionForNumber(i + 7); } // Otherwise see if this is the closest to a real version info bit string // we have seen so far - int bitsDifference = FormatInformation.numBitsDiffering(versionBits, targetVersion); - if (bitsDifference < bestDifference) { + let bitsDifference = FormatInformation.numBitsDiffering(versionBits, targetVersion); + if bitsDifference < bestDifference { bestVersion = i + 7; bestDifference = bitsDifference; } } // We can tolerate up to 3 bits of error since no two version info codewords will // differ in less than 8 bits. - if (bestDifference <= 3) { - return getVersionForNumber(bestVersion); + if bestDifference <= 3 { + return Self::getVersionForNumber(bestVersion); } // If we didn't find a close enough match, fail - return null; + Err(Exceptions::NotFoundException("could not find".to_owned())) } /** * See ISO 18004:2006 Annex E */ - BitMatrix buildFunctionPattern() { - int dimension = getDimensionForVersion(); - BitMatrix bitMatrix = new BitMatrix(dimension); + pub fn buildFunctionPattern(&self) -> BitMatrix{ + let dimension = self.getDimensionForVersion(); + let bitMatrix = BitMatrix::with_single_dimension(dimension); // Top left finder pattern + separator + format bitMatrix.setRegion(0, 0, 9, 9); @@ -145,12 +162,14 @@ impl Version { bitMatrix.setRegion(0, dimension - 8, 9, 8); // Alignment patterns - int max = alignmentPatternCenters.length; - for (int x = 0; x < max; x++) { - int i = alignmentPatternCenters[x] - 2; - for (int y = 0; y < max; y++) { - if ((x != 0 || (y != 0 && y != max - 1)) && (x != max - 1 || y != 0)) { - bitMatrix.setRegion(alignmentPatternCenters[y] - 2, i, 5, 5); + let max = self.alignmentPatternCenters.len(); + for x in 0..max { + // for (int x = 0; x < max; x++) { + let i = self.alignmentPatternCenters[x] - 2; + for y in 0..max { + // for (int y = 0; y < max; y++) { + if (x != 0 || (y != 0 && y != max - 1)) && (x != max - 1 || y != 0) { + bitMatrix.setRegion(self.alignmentPatternCenters[y] - 2, i, 5, 5); } // else no o alignment patterns near the three finder patterns } @@ -161,100 +180,36 @@ impl Version { // Horizontal timing pattern bitMatrix.setRegion(9, 6, dimension - 17, 1); - if (versionNumber > 6) { + if self.versionNumber > 6 { // Version info, top right bitMatrix.setRegion(dimension - 11, 0, 3, 6); // Version info, bottom left bitMatrix.setRegion(0, dimension - 11, 6, 3); } - return bitMatrix; - } - - /** - *

Encapsulates a set of error-correction blocks in one symbol version. Most versions will - * use blocks of differing sizes within one version, so, this encapsulates the parameters for - * each set of blocks. It also holds the number of error-correction codewords per block since it - * will be the same across all blocks within one version.

- */ - public static final class ECBlocks { - private final int ecCodewordsPerBlock; - private final ECB[] ecBlocks; - - ECBlocks(int ecCodewordsPerBlock, ECB... ecBlocks) { - this.ecCodewordsPerBlock = ecCodewordsPerBlock; - this.ecBlocks = ecBlocks; - } - - public int getECCodewordsPerBlock() { - return ecCodewordsPerBlock; - } - - public int getNumBlocks() { - int total = 0; - for (ECB ecBlock : ecBlocks) { - total += ecBlock.getCount(); - } - return total; - } - - public int getTotalECCodewords() { - return ecCodewordsPerBlock * getNumBlocks(); - } - - public ECB[] getECBlocks() { - return ecBlocks; - } - } - - /** - *

Encapsulates the parameters for one error-correction block in one symbol version. - * This includes the number of data codewords, and the number of times a block with these - * parameters is used consecutively in the QR code version's format.

- */ - public static final class ECB { - private final int count; - private final int dataCodewords; - - ECB(int count, int dataCodewords) { - this.count = count; - this.dataCodewords = dataCodewords; - } - - public int getCount() { - return count; - } - - public int getDataCodewords() { - return dataCodewords; - } - } - - @Override - public String toString() { - return String.valueOf(versionNumber); + bitMatrix } /** * See ISO 18004:2006 6.5.1 Table 9 */ - private static Version[] buildVersions() { - return new Version[]{ - new Version(1, new int[]{}, - new ECBlocks(7, new ECB(1, 19)), - new ECBlocks(10, new ECB(1, 16)), - new ECBlocks(13, new ECB(1, 13)), - new ECBlocks(17, new ECB(1, 9))), - new Version(2, new int[]{6, 18}, - new ECBlocks(10, new ECB(1, 34)), - new ECBlocks(16, new ECB(1, 28)), - new ECBlocks(22, new ECB(1, 22)), - new ECBlocks(28, new ECB(1, 16))), - new Version(3, new int[]{6, 22}, - new ECBlocks(15, new ECB(1, 55)), - new ECBlocks(26, new ECB(1, 44)), - new ECBlocks(18, new ECB(2, 17)), - new ECBlocks(22, new ECB(2, 13))), + pub const fn buildVersions() -> Vec { + [ + Version::new(1, [],Vec::from([ + ECBlocks::new(7, ECB::new(1, 19)), + ECBlocks::new(10, ECB::new(1, 16)), + ECBlocks::new(13, ECB::new(1, 13)), + ECBlocks::new(17, ECB::new(1, 9))])), + Version::new(2, [6, 18], Vec::from([ + ECBlocks::new(10, ECB::new(1, 34)), + ECBlocks::new(16, ECB::new(1, 28)), + ECBlocks::new(22, ECB::new(1, 22)), + ECBlocks::new(28, ECB::new(1, 16))])), + Version::new(3, [6, 22], Vec::from([ + ECBlocks::new(15, ECB::new(1, 55)), + ECBlocks::new(26, ECB::new(1, 44)), + ECBlocks::new(18, ECB::new(2, 17)), + ECBlocks::new(22, ECB::new(2, 13))])), new Version(4, new int[]{6, 26}, new ECBlocks(20, new ECB(1, 80)), new ECBlocks(18, new ECB(2, 32)), @@ -568,7 +523,81 @@ impl Version { new ECB(34, 25)), new ECBlocks(30, new ECB(20, 15), new ECB(61, 16))) - }; + ] + } +} + +impl fmt::Display for Version { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.versionNumber) + } +} + + /** + *

Encapsulates a set of error-correction blocks in one symbol version. Most versions will + * use blocks of differing sizes within one version, so, this encapsulates the parameters for + * each set of blocks. It also holds the number of error-correction codewords per block since it + * will be the same across all blocks within one version.

+ */ + pub struct ECBlocks { + ecCodewordsPerBlock:u32, + ecBlocks:Vec, } -} + impl ECBlocks { + + pub const fn new(ecCodewordsPerBlock:u32, ecBlocks:Vec)-> Self { + Self{ + ecCodewordsPerBlock, + ecBlocks + } + } + + pub fn getECCodewordsPerBlock(&self) -> u32 { + self. ecCodewordsPerBlock + } + + pub fn getNumBlocks(&self) -> u32 { + let total = 0; + for ecBlock in self.ecBlocks { + // for (ECB ecBlock : ecBlocks) { + total += ecBlock.getCount(); + } + total + } + + pub fn getTotalECCodewords(&self) -> u32{ + self.ecCodewordsPerBlock * self.getNumBlocks() + } + + pub fn getECBlocks(&self) -> &[ECB] { + &self.ecBlocks + } + } + + /** + *

Encapsulates the parameters for one error-correction block in one symbol version. + * This includes the number of data codewords, and the number of times a block with these + * parameters is used consecutively in the QR code version's format.

+ */ + pub struct ECB { + count:u32, + dataCodewords:u32, + } + + impl ECB { + pub const fn new(count:u32, dataCodewords:u32) -> Self { + Self{ + count, + dataCodewords + } + } + + pub fn getCount(&self) -> u32 { + self.count + } + + pub fn getDataCodewords(&self) -> u32 { + self.dataCodewords + } + } \ No newline at end of file