From 4ed49e10ff5eef773964bfe347f3ec3a8da8b1a3 Mon Sep 17 00:00:00 2001 From: Henry Schimke Date: Thu, 11 Jan 2024 12:53:04 -0600 Subject: [PATCH] feat: ported qr-model1 support from zxing-cpp --- src/barcode_format.rs | 6 + .../base_extentions/qr_formatinformation.rs | 26 +- .../base_extentions/qrcode_version.rs | 28 +- src/common/cpp_essentials/decoder_result.rs | 7 + src/multi_format_reader.rs | 3 + src/qrcode/cpp_port/bitmatrix_parser.rs | 109 +++++++- src/qrcode/cpp_port/decoder.rs | 18 +- .../test/QRDecodedBitStreamParserTest.rs | 12 +- .../cpp_port/test/QRFormatInformationTest.rs | 2 +- src/qrcode/cpp_port/test/QRModeTest.rs | 39 ++- src/qrcode/cpp_port/test/QRVersionTest.rs | 15 +- src/qrcode/decoder/format_information.rs | 3 + src/qrcode/decoder/version.rs | 247 ++++++++++++++++++ .../cpp/qrcode-2/qr-model-1.metadata.txt | 2 + .../blackbox/cpp/qrcode-2/qr-model-1.png | Bin 0 -> 230 bytes .../blackbox/cpp/qrcode-2/qr-model-1.txt | 1 + tests/cpp_qr_code_blackbox_tests.rs | 8 +- 17 files changed, 485 insertions(+), 41 deletions(-) create mode 100644 test_resources/blackbox/cpp/qrcode-2/qr-model-1.metadata.txt create mode 100644 test_resources/blackbox/cpp/qrcode-2/qr-model-1.png create mode 100644 test_resources/blackbox/cpp/qrcode-2/qr-model-1.txt diff --git a/src/barcode_format.rs b/src/barcode_format.rs index 2946991..70fd962 100644 --- a/src/barcode_format.rs +++ b/src/barcode_format.rs @@ -67,6 +67,8 @@ pub enum BarcodeFormat { MICRO_QR_CODE, + RECTANGULAR_MICRO_QR_CODE, + /** RSS 14 */ RSS_14, @@ -108,6 +110,7 @@ impl Display for BarcodeFormat { BarcodeFormat::PDF_417 => "pdf 417", BarcodeFormat::QR_CODE => "qrcode", BarcodeFormat::MICRO_QR_CODE => "mqr", + BarcodeFormat::RECTANGULAR_MICRO_QR_CODE => "rmqr", BarcodeFormat::RSS_14 => "rss 14", BarcodeFormat::RSS_EXPANDED => "rss expanded", BarcodeFormat::TELEPEN => "telepen", @@ -150,6 +153,9 @@ impl From<&str> for BarcodeFormat { "mqr" | "microqr" | "micro_qr" | "micro_qrcode" | "micro_qr_code" | "mqr_code" => { BarcodeFormat::MICRO_QR_CODE } + "rmqr" | "rectangular_mqr" | "rectangular_micro_qr" | "rmqr_code" => { + BarcodeFormat::RECTANGULAR_MICRO_QR_CODE + } "rss 14" | "rss_14" | "rss14" | "gs1 databar" | "gs1 databar coupon" | "gs1_databar_coupon" => BarcodeFormat::RSS_14, "rss expanded" | "expanded rss" | "rss_expanded" => BarcodeFormat::RSS_EXPANDED, diff --git a/src/common/cpp_essentials/base_extentions/qr_formatinformation.rs b/src/common/cpp_essentials/base_extentions/qr_formatinformation.rs index 398a54b..30919e2 100644 --- a/src/common/cpp_essentials/base_extentions/qr_formatinformation.rs +++ b/src/common/cpp_essentials/base_extentions/qr_formatinformation.rs @@ -2,6 +2,8 @@ use crate::qrcode::decoder::{ ErrorCorrectionLevel, FormatInformation, FORMAT_INFO_DECODE_LOOKUP, FORMAT_INFO_MASK_QR, }; +pub const FORMAT_INFO_MASK_QR_MODEL1: u32 = 0x2825; + pub const FORMAT_INFO_DECODE_LOOKUP_MICRO: [[u32; 2]; 32] = [ [0x4445, 0x00], [0x4172, 0x01], @@ -50,7 +52,7 @@ impl FormatInformation { let formatInfoBits2 = ((formatInfoBits2 >> 1) & 0b111111100000000) | (formatInfoBits2 & 0b11111111); let mut fi = Self::FindBestFormatInfo( - FORMAT_INFO_MASK_QR, + &[0, FORMAT_INFO_MASK_QR], FORMAT_INFO_DECODE_LOOKUP, &[ formatInfoBits1, @@ -60,6 +62,22 @@ impl FormatInformation { ], ); + let mut fi_model1 = Self::FindBestFormatInfo( + &[FORMAT_INFO_MASK_QR ^ FORMAT_INFO_MASK_QR_MODEL1], + FORMAT_INFO_DECODE_LOOKUP, + &[ + formatInfoBits1, + formatInfoBits2, + Self::MirrorBits(formatInfoBits1), + mirroredFormatInfoBits2, + ], + ); + + if fi_model1.hammingDistance < fi.hammingDistance { + fi_model1.isModel1 = true; + fi = fi_model1; + } + // Use bits 3/4 for error correction, and 0-2 for mask. fi.error_correction_level = ErrorCorrectionLevel::ECLevelFromBits((fi.index >> 3) & 0x03, false); @@ -72,7 +90,7 @@ impl FormatInformation { pub fn DecodeMQR(formatInfoBits: u32) -> Self { // We don't use the additional masking (with 0x4445) to work around potentially non complying MicroQRCode encoders let mut fi = Self::FindBestFormatInfo( - 0, + &[0], FORMAT_INFO_DECODE_LOOKUP_MICRO, &[formatInfoBits, Self::MirrorBits(formatInfoBits)], ); @@ -94,11 +112,11 @@ impl FormatInformation { (bits.reverse_bits()) >> 17 } - pub fn FindBestFormatInfo(mask: u32, lookup: [[u32; 2]; 32], bits: &[u32]) -> Self { + pub fn FindBestFormatInfo(masks: &[u32], lookup: [[u32; 2]; 32], bits: &[u32]) -> Self { let mut fi = FormatInformation::default(); // Some QR codes apparently do not apply the XOR mask. Try without and with additional masking. - for mask in [0, mask] { + for mask in masks { // for (auto mask : {0, mask}) for (bitsIndex, bit_set) in bits.iter().enumerate() { // for (int bitsIndex = 0; bitsIndex < Size(bits); ++bitsIndex) diff --git a/src/common/cpp_essentials/base_extentions/qrcode_version.rs b/src/common/cpp_essentials/base_extentions/qrcode_version.rs index 892352f..d11cc56 100644 --- a/src/common/cpp_essentials/base_extentions/qrcode_version.rs +++ b/src/common/cpp_essentials/base_extentions/qrcode_version.rs @@ -1,5 +1,7 @@ use crate::common::Result; -use crate::qrcode::decoder::{Version, VersionRef, MICRO_VERSIONS, VERSIONS, VERSION_DECODE_INFO}; +use crate::qrcode::decoder::{ + Version, VersionRef, MICRO_VERSIONS, MODEL1_VERSIONS, VERSIONS, VERSION_DECODE_INFO, +}; use crate::Exceptions; // const Version* Version::AllMicroVersions() @@ -16,7 +18,7 @@ use crate::Exceptions; // } impl Version { - pub fn FromDimension(dimension: u32) -> Result { + pub fn FromDimension(dimension: u32, is_model1: bool) -> Result { let isMicro = dimension < 21; if dimension % Self::DimensionStep(isMicro) != 1 { //throw std::invalid_argument("Unexpected dimension"); @@ -25,17 +27,31 @@ impl Version { Self::FromNumber( (dimension - Self::DimensionOffset(isMicro)) / Self::DimensionStep(isMicro), isMicro, + is_model1, ) } - pub fn FromNumber(versionNumber: u32, is_micro: bool) -> Result { - if versionNumber < 1 || versionNumber > (if is_micro { 4 } else { 40 }) { + pub fn FromNumber(versionNumber: u32, is_micro: bool, is_model1: bool) -> Result { + if versionNumber < 1 + || versionNumber + > (if is_micro { + 4 + } else { + if is_model1 { + 14 + } else { + 40 + } + }) + { //throw std::invalid_argument("Version should be in range [1-40]."); return Err(Exceptions::ILLEGAL_ARGUMENT); } Ok(if is_micro { &MICRO_VERSIONS[versionNumber as usize - 1] + } else if is_model1 { + &MODEL1_VERSIONS[versionNumber as usize - 1] } else { &VERSIONS[versionNumber as usize - 1] }) @@ -92,4 +108,8 @@ impl Version { pub const fn isMicroQRCode(&self) -> bool { self.is_micro } + + pub const fn isQRCodeModel1(&self) -> bool { + self.is_model1 + } } diff --git a/src/common/cpp_essentials/decoder_result.rs b/src/common/cpp_essentials/decoder_result.rs index bd0967c..3504747 100644 --- a/src/common/cpp_essentials/decoder_result.rs +++ b/src/common/cpp_essentials/decoder_result.rs @@ -157,6 +157,13 @@ where self } + pub fn withIsModel1(mut self, is_model_1: bool) -> DecoderResult { + if is_model_1 { + self.content.symbology.modifier = 48 + } + self + } + // pub fn build(self) -> DecoderResult { // } diff --git a/src/multi_format_reader.rs b/src/multi_format_reader.rs index 2d3e99e..9583d6c 100644 --- a/src/multi_format_reader.rs +++ b/src/multi_format_reader.rs @@ -183,6 +183,9 @@ impl MultiFormatReader { } } BarcodeFormat::MICRO_QR_CODE => QrReader.decode_with_hints(image, &self.hints), + BarcodeFormat::RECTANGULAR_MICRO_QR_CODE => { + QrReader.decode_with_hints(image, &self.hints) + } BarcodeFormat::DATA_MATRIX => { DataMatrixReader.decode_with_hints(image, &self.hints) } diff --git a/src/qrcode/cpp_port/bitmatrix_parser.rs b/src/qrcode/cpp_port/bitmatrix_parser.rs index 58a92bc..15c3d7b 100644 --- a/src/qrcode/cpp_port/bitmatrix_parser.rs +++ b/src/qrcode/cpp_port/bitmatrix_parser.rs @@ -33,7 +33,7 @@ pub fn hasValidDimension(bitMatrix: &BitMatrix, isMicro: bool) -> bool { pub fn ReadVersion(bitMatrix: &BitMatrix) -> Result { let dimension = bitMatrix.height(); - let mut version = Version::FromDimension(dimension)?; + let mut version = Version::FromDimension(dimension, false)?; if version.getVersionNumber() < 7 { return Ok(version); @@ -235,6 +235,111 @@ pub fn ReadMQRCodewords( Ok(result.iter().copied().map(|x| x as u8).collect()) } +pub fn ReadQRCodewordsModel1( + bitMatrix: &BitMatrix, + version: VersionRef, + formatInfo: &FormatInformation, +) -> Result> { + let mut result = Vec::with_capacity(version.getTotalCodewords() as usize); + let dimension = bitMatrix.height(); + let columns = dimension / 4 + 1 + 2; + for j in 0..columns { + // for (int j = 0; j < columns; j++) { + if j <= 1 { + // vertical symbols on the right side + let rows = (dimension - 8) / 4; + for i in 0..rows { + // for (int i = 0; i < rows; i++) { + if j == 0 && i % 2 == 0 && i > 0 && i < rows - 1 + // extension + { + continue; + } + let x = (dimension - 1) - (j * 2); + let y = (dimension - 1) - (i * 4); + let mut currentByte = 0; + for b in 0..8 { + // for (int b = 0; b < 8; b++) { + AppendBit( + &mut currentByte, + GetDataMaskBit(formatInfo.data_mask as u32, x - b % 2, y - (b / 2), None)? + != getBit( + bitMatrix, + x - b % 2, + y - (b / 2), + Some(formatInfo.isMirrored), + ), + ); + } + result.push(currentByte); + } + } else if columns - j <= 4 { + // vertical symbols on the left side + let rows = (dimension - 16) / 4; + for i in 0..rows { + // for (int i = 0; i < rows; i++) { + let x = (columns - j - 1) * 2 + 1 + (if columns - j == 4 { 1 } else { 0 }); // timing + let y = (dimension - 1) - 8 - (i * 4); + let mut currentByte = 0; + for b in 0..8 { + // for (int b = 0; b < 8; b++) { + AppendBit( + &mut currentByte, + GetDataMaskBit(formatInfo.data_mask as u32, x - b % 2, y - (b / 2), None)? + != getBit( + bitMatrix, + x - b % 2, + y - (b / 2), + Some(formatInfo.isMirrored), + ), + ); + } + result.push(currentByte); + } + } else { + // horizontal symbols + let rows = dimension / 2; + for i in 0..rows { + // for (int i = 0; i < rows; i++) { + if j == 2 && i >= rows - 4 + // alignment & finder + { + continue; + } + if i == 0 && j % 2 == 1 && j + 1 != columns - 4 + // extension + { + continue; + } + let x = (dimension - 1) - (2 * 2) - (j - 2) * 4; + let y = (dimension - 1) - (i * 2) - (if i >= rows - 3 { 1 } else { 0 }); // timing + let mut currentByte = 0; + for b in 0..8 { + // for (int b = 0; b < 8; b++) { + AppendBit( + &mut currentByte, + GetDataMaskBit(formatInfo.data_mask as u32, x - b % 4, y - (b / 4), None)? + != getBit( + bitMatrix, + x - b % 4, + y - (b / 4), + Some(formatInfo.isMirrored), + ), + ); + } + result.push(currentByte); + } + } + } + + result[0] &= 0xf; // ignore corner + if (result.len()) != version.getTotalCodewords() as usize { + return Err(Exceptions::FORMAT); + } + + Ok(result.iter().copied().map(|x| x as u8).collect()) +} + pub fn ReadCodewords( bitMatrix: &BitMatrix, version: VersionRef, @@ -246,6 +351,8 @@ pub fn ReadCodewords( if version.isMicroQRCode() { ReadMQRCodewords(bitMatrix, version, formatInfo) + } else if formatInfo.isModel1 { + ReadQRCodewordsModel1(bitMatrix, version, formatInfo) } else { ReadQRCodewords(bitMatrix, version, formatInfo) } diff --git a/src/qrcode/cpp_port/decoder.rs b/src/qrcode/cpp_port/decoder.rs index 12aab65..0e0fceb 100644 --- a/src/qrcode/cpp_port/decoder.rs +++ b/src/qrcode/cpp_port/decoder.rs @@ -299,6 +299,10 @@ pub fn DecodeBitStream( let mut structuredAppend = StructuredAppendInfo::default(); let modeBitLength = Mode::get_codec_mode_bits_length(version); + if version.isQRCodeModel1() { + bits.readBits(4)?; /* Model 1 is leading with 4 0-bits -> drop them */ + } + let res = (|| { while !IsEndOfStream(&mut bits, version)? { let mode: Mode = if modeBitLength == 0 { @@ -387,11 +391,21 @@ pub fn DecodeBitStream( .withError(res.err()) .withEcLevel(ecLevel.to_string()) .withVersionNumber(version.getVersionNumber()) - .withStructuredAppend(structuredAppend)) + .withStructuredAppend(structuredAppend) + .withIsModel1(version.isQRCodeModel1())) } pub fn Decode(bits: &BitMatrix) -> Result> { - let Ok(pversion) = ReadVersion(bits) else { + let isMicroQRCode = bits.height() < 21; + let Ok(formatInfo) = ReadFormatInformation(bits, isMicroQRCode) else { + return Err(Exceptions::format_with("Invalid format information")); + }; + + let Ok(pversion) = (if formatInfo.isModel1 { + Version::FromDimension(bits.height(), true) + } else { + ReadVersion(bits) + }) else { return Err(Exceptions::format_with("Invalid version")); }; let version = pversion; diff --git a/src/qrcode/cpp_port/test/QRDecodedBitStreamParserTest.rs b/src/qrcode/cpp_port/test/QRDecodedBitStreamParserTest.rs index 68c19b1..e777dc5 100644 --- a/src/qrcode/cpp_port/test/QRDecodedBitStreamParserTest.rs +++ b/src/qrcode/cpp_port/test/QRDecodedBitStreamParserTest.rs @@ -42,7 +42,7 @@ fn SimpleByteMode() { let bytes: Vec = ba.into(); let result = DecodeBitStream( &bytes, - Version::FromNumber(1, false).expect("find_version"), + Version::FromNumber(1, false, false).expect("find_version"), ErrorCorrectionLevel::M, ) .expect("Decode") @@ -64,7 +64,7 @@ fn SimpleSJIS() { let bytes: Vec = ba.into(); let result = DecodeBitStream( &bytes, - Version::FromNumber(1, false).unwrap(), + Version::FromNumber(1, false, false).unwrap(), ErrorCorrectionLevel::M, ) .unwrap() @@ -86,7 +86,7 @@ fn ECI() { let bytes: Vec = ba.into(); let result = DecodeBitStream( &bytes, - Version::FromNumber(1, false).unwrap(), + Version::FromNumber(1, false, false).unwrap(), ErrorCorrectionLevel::M, ) .unwrap() @@ -105,7 +105,7 @@ fn Hanzi() { let bytes: Vec = ba.into(); let result = DecodeBitStream( &bytes, - Version::FromNumber(1, false).unwrap(), + Version::FromNumber(1, false, false).unwrap(), ErrorCorrectionLevel::M, ) .unwrap() @@ -127,7 +127,7 @@ fn HanziLevel1() { let result = DecodeBitStream( &bytes, - Version::FromNumber(1, false).unwrap(), + Version::FromNumber(1, false, false).unwrap(), ErrorCorrectionLevel::M, ) .unwrap() @@ -138,7 +138,7 @@ fn HanziLevel1() { #[test] fn SymbologyIdentifier() { - let version = Version::FromNumber(1, false).unwrap(); + let version = Version::FromNumber(1, false, false).unwrap(); let ecLevel = ErrorCorrectionLevel::M; // Plain "ANUM(1) A" diff --git a/src/qrcode/cpp_port/test/QRFormatInformationTest.rs b/src/qrcode/cpp_port/test/QRFormatInformationTest.rs index 67f326a..66b9c5f 100644 --- a/src/qrcode/cpp_port/test/QRFormatInformationTest.rs +++ b/src/qrcode/cpp_port/test/QRFormatInformationTest.rs @@ -63,7 +63,7 @@ fn DecodeWithBitDifference() { MASKED_TEST_FORMAT_INFO2 ^ 0x07, ), ); - assert!(!FormatInformation::DecodeQR( + assert!(FormatInformation::DecodeQR( MASKED_TEST_FORMAT_INFO ^ 0x0F, MASKED_TEST_FORMAT_INFO2 ^ 0x0F ) diff --git a/src/qrcode/cpp_port/test/QRModeTest.rs b/src/qrcode/cpp_port/test/QRModeTest.rs index fa356a0..03a3bae 100644 --- a/src/qrcode/cpp_port/test/QRModeTest.rs +++ b/src/qrcode/cpp_port/test/QRModeTest.rs @@ -27,27 +27,39 @@ fn CharacterCount() { // Spot check a few values assert_eq!( 10, - Mode::CharacterCountBits(&Mode::NUMERIC, Version::FromNumber(5, false).unwrap()) + Mode::CharacterCountBits( + &Mode::NUMERIC, + Version::FromNumber(5, false, false).unwrap() + ) ); assert_eq!( 12, - Mode::CharacterCountBits(&Mode::NUMERIC, Version::FromNumber(26, false).unwrap()) + Mode::CharacterCountBits( + &Mode::NUMERIC, + Version::FromNumber(26, false, false).unwrap() + ) ); assert_eq!( 14, - Mode::CharacterCountBits(&Mode::NUMERIC, Version::FromNumber(40, false).unwrap()) + Mode::CharacterCountBits( + &Mode::NUMERIC, + Version::FromNumber(40, false, false).unwrap() + ) ); assert_eq!( 9, - Mode::CharacterCountBits(&Mode::ALPHANUMERIC, Version::FromNumber(6, false).unwrap()) + Mode::CharacterCountBits( + &Mode::ALPHANUMERIC, + Version::FromNumber(6, false, false).unwrap() + ) ); assert_eq!( 8, - Mode::CharacterCountBits(&Mode::BYTE, Version::FromNumber(7, false).unwrap()) + Mode::CharacterCountBits(&Mode::BYTE, Version::FromNumber(7, false, false).unwrap()) ); assert_eq!( 8, - Mode::CharacterCountBits(&Mode::KANJI, Version::FromNumber(8, false).unwrap()) + Mode::CharacterCountBits(&Mode::KANJI, Version::FromNumber(8, false, false).unwrap()) ); } @@ -110,26 +122,29 @@ fn MicroCharacterCount() { // Spot check a few values assert_eq!( 3, - Mode::CharacterCountBits(&Mode::NUMERIC, Version::FromNumber(1, true).unwrap()) + Mode::CharacterCountBits(&Mode::NUMERIC, Version::FromNumber(1, true, false).unwrap()) ); assert_eq!( 4, - Mode::CharacterCountBits(&Mode::NUMERIC, Version::FromNumber(2, true).unwrap()) + Mode::CharacterCountBits(&Mode::NUMERIC, Version::FromNumber(2, true, false).unwrap()) ); assert_eq!( 6, - Mode::CharacterCountBits(&Mode::NUMERIC, Version::FromNumber(4, true).unwrap()) + Mode::CharacterCountBits(&Mode::NUMERIC, Version::FromNumber(4, true, false).unwrap()) ); assert_eq!( 3, - Mode::CharacterCountBits(&Mode::ALPHANUMERIC, Version::FromNumber(2, true).unwrap()) + Mode::CharacterCountBits( + &Mode::ALPHANUMERIC, + Version::FromNumber(2, true, false).unwrap() + ) ); assert_eq!( 4, - Mode::CharacterCountBits(&Mode::BYTE, Version::FromNumber(3, true).unwrap()) + Mode::CharacterCountBits(&Mode::BYTE, Version::FromNumber(3, true, false).unwrap()) ); assert_eq!( 4, - Mode::CharacterCountBits(&Mode::KANJI, Version::FromNumber(4, true).unwrap()) + Mode::CharacterCountBits(&Mode::KANJI, Version::FromNumber(4, true, false).unwrap()) ); } diff --git a/src/qrcode/cpp_port/test/QRVersionTest.rs b/src/qrcode/cpp_port/test/QRVersionTest.rs index 6decf1b..c02f348 100644 --- a/src/qrcode/cpp_port/test/QRVersionTest.rs +++ b/src/qrcode/cpp_port/test/QRVersionTest.rs @@ -26,13 +26,13 @@ fn DoTestVersion(expectedVersion: u32, mask: i32) { #[test] fn VersionForNumber() { - let version = Version::FromNumber(0, false); + let version = Version::FromNumber(0, false, false); assert!(version.is_err(), "There is version with number 0"); for i in 1..=40 { // for (int i = 1; i <= 40; i++) { CheckVersion( - Version::FromNumber(i, false).expect("version number found"), + Version::FromNumber(i, false, false).expect("version number found"), i, 4 * i + 17, ); @@ -43,7 +43,7 @@ fn VersionForNumber() { fn GetProvisionalVersionForDimension() { for i in 1..=40 { // for (int i = 1; i <= 40; i++) { - let prov = Version::FromDimension(4 * i + 17) + let prov = Version::FromDimension(4 * i + 17, false) .unwrap_or_else(|_| panic!("version should exist for {i}")); // assert_ne!(prov, nullptr); assert_eq!(i, prov.getVersionNumber()); @@ -63,13 +63,14 @@ fn DecodeVersionInformation() { #[test] fn MicroVersionForNumber() { - let version = Version::FromNumber(0, true); + let version = Version::FromNumber(0, true, false); assert!(version.is_err(), "There is version with number 0"); for i in 1..=4 { // for (int i = 1; i <= 4; i++) { CheckVersion( - Version::FromNumber(i, true).unwrap_or_else(|_| panic!("version for {i} should exist")), + Version::FromNumber(i, true, false) + .unwrap_or_else(|_| panic!("version for {i} should exist")), i, 2 * i + 9, ); @@ -80,7 +81,7 @@ fn MicroVersionForNumber() { fn GetProvisionalMicroVersionForDimension() { for i in 1..=4 { // for (int i = 1; i <= 4; i++) { - let prov = Version::FromDimension(2 * i + 9) + let prov = Version::FromDimension(2 * i + 9, false) .unwrap_or_else(|_| panic!("version for micro {i} should exist")); // assert_ne!(prov, nullptr); assert_eq!(i, prov.getVersionNumber()); @@ -100,7 +101,7 @@ fn FunctionPattern() { }; for i in 1..=4 { // for (int i = 1; i <= 4; i++) { - let version = Version::FromNumber(i, true).expect("version must be found"); + let version = Version::FromNumber(i, true, false).expect("version must be found"); let functionPattern = version .buildFunctionPattern() .expect("function pattern must be found"); diff --git a/src/qrcode/decoder/format_information.rs b/src/qrcode/decoder/format_information.rs index 492b445..8e7281c 100644 --- a/src/qrcode/decoder/format_information.rs +++ b/src/qrcode/decoder/format_information.rs @@ -73,6 +73,7 @@ pub struct FormatInformation { pub data_mask: u8, pub microVersion: u32, pub isMirrored: bool, + pub isModel1: bool, pub index: u8, // = 255; pub bitsIndex: u8, // = 255; @@ -86,6 +87,7 @@ impl Default for FormatInformation { data_mask: Default::default(), microVersion: 0, isMirrored: false, + isModel1: false, index: 255, bitsIndex: 255, } @@ -104,6 +106,7 @@ impl FormatInformation { error_correction_level: errorCorrectionLevel, data_mask: dataMask, isMirrored: false, + isModel1: false, index: 255, bitsIndex: 255, }) diff --git a/src/qrcode/decoder/version.rs b/src/qrcode/decoder/version.rs index aeb58c7..e580a50 100755 --- a/src/qrcode/decoder/version.rs +++ b/src/qrcode/decoder/version.rs @@ -29,6 +29,7 @@ pub type VersionRef = &'static Version; pub static VERSIONS: Lazy> = Lazy::new(Version::buildVersions); pub static MICRO_VERSIONS: Lazy> = Lazy::new(Version::build_micro_versions); +pub static MODEL1_VERSIONS: Lazy> = Lazy::new(Version::build_model1_versions); /** * See ISO 18004:2006 Annex D. @@ -55,6 +56,7 @@ pub struct Version { ecBlocks: Vec, totalCodewords: u32, pub(crate) is_micro: bool, + pub(crate) is_model1: bool, } impl Version { fn new(versionNumber: u32, alignmentPatternCenters: Vec, ecBlocks: [ECBlocks; 4]) -> Self { @@ -73,6 +75,7 @@ impl Version { ecBlocks: ecBlocks.to_vec(), totalCodewords: total, is_micro: false, + is_model1: false, } } @@ -92,9 +95,35 @@ impl Version { ecBlocks, totalCodewords: total, is_micro: true, + is_model1: false, } } + fn new_model1(versionNumber: u32, ecBlocks: Vec) -> Self { + let mut total = 0; + let ecCodewords = ecBlocks[0].getECCodewordsPerBlock(); + let ecbArray = ecBlocks[0].getECBlocks(); + let mut i = 0; + while i < ecbArray.len() { + total += ecbArray[i].getCount() * (ecbArray[i].getDataCodewords() + ecCodewords); + i += 1; + } + + Self { + versionNumber, + alignmentPatternCenters: Vec::default(), + ecBlocks, + totalCodewords: total, + is_micro: false, + is_model1: true, + } + } + + // #[inline(always)] + // pub(crate) const fn isMicro(ecBlocks: &[ECBlocks; 4]) -> bool { + // ecBlocks[0].ecCodewordsPerBlock < 7 || ecBlocks[0].ecCodewordsPerBlock == 8 + // } + pub const fn getVersionNumber(&self) -> u32 { self.versionNumber } @@ -970,6 +999,224 @@ impl Version { new ECB::new(61, 16))) ]*/ } + + /* + * {1, { + 7 , 1, 19, 0, 0, + 10, 1, 16, 0, 0, + 13, 1, 13, 0, 0, + 17, 1, 9 , 0, 0 + }}, + {2, { + 10, 1, 36, 0, 0, + 16, 1, 30, 0, 0, + 22, 1, 24, 0, 0, + 30, 1, 16, 0, 0, + }}, + {3, { + 15, 1, 57, 0, 0, + 28, 1, 44, 0, 0, + 36, 1, 36, 0, 0, + 48, 1, 24, 0, 0, + }}, + {4, { + 20, 1, 80, 0, 0, + 40, 1, 60, 0, 0, + 50, 1, 50, 0, 0, + 66, 1, 34, 0, 0, + }}, + {5, { + 26, 1, 108, 0, 0, + 52, 1, 82 , 0, 0, + 66, 1, 68 , 0, 0, + 88, 2, 46 , 0, 0, + }}, + {6, { + 34 , 1, 136, 0, 0, + 63 , 2, 106, 0, 0, + 84 , 2, 86 , 0, 0, + 112, 2, 58 , 0, 0, + }}, + {7, { + 42 , 1, 170, 0, 0, + 80 , 2, 132, 0, 0, + 104, 2, 108, 0, 0, + 138, 3, 72 , 0, 0, + }}, + {8, { + 48 , 2, 208, 0, 0, + 96 , 2, 160, 0, 0, + 128, 2, 128, 0, 0, + 168, 3, 87 , 0, 0, + }}, + {9, { + 60 , 2, 246, 0, 0, + 120, 2, 186, 0, 0, + 150, 3, 156, 0, 0, + 204, 3, 102, 0, 0, + }}, + {10, { + 68 , 2, 290, 0, 0, + 136, 2, 222, 0, 0, + 174, 3, 183, 0, 0, + 232, 4, 124, 0, 0, + }}, + {11, { + 80 , 2, 336, 0, 0, + 160, 4, 256, 0, 0, + 208, 4, 208, 0, 0, + 270, 5, 145, 0, 0, + }}, + {12, { + 92 , 2, 384, 0, 0, + 184, 4, 292, 0, 0, + 232, 4, 244, 0, 0, + 310, 5, 165, 0, 0, + }}, + {13, { + 108, 3, 432, 0, 0, + 208, 4, 332, 0, 0, + 264, 4, 276, 0, 0, + 348, 6, 192, 0, 0, + }}, + {14, { + 120, 3, 489, 0, 0, + 240, 4, 368, 0, 0, + 300, 5, 310, 0, 0, + 396, 6, 210, 0, 0, + }}, + }; + */ + pub fn build_model1_versions() -> Vec { + Vec::from([ + Version::new_model1( + 1, + vec![ + ECBlocks::new(7, vec![ECB::new(1, 19)]), + ECBlocks::new(10, vec![ECB::new(1, 16)]), + ECBlocks::new(13, vec![ECB::new(1, 13)]), + ECBlocks::new(17, vec![ECB::new(1, 9)]), + ], + ), + Version::new_model1( + 2, + vec![ + ECBlocks::new(10, vec![ECB::new(1, 36)]), + ECBlocks::new(16, vec![ECB::new(1, 30)]), + ECBlocks::new(22, vec![ECB::new(1, 24)]), + ECBlocks::new(30, vec![ECB::new(1, 16)]), + ], + ), + Version::new_model1( + 3, + vec![ + ECBlocks::new(15, vec![ECB::new(1, 57)]), + ECBlocks::new(28, vec![ECB::new(1, 44)]), + ECBlocks::new(36, vec![ECB::new(1, 36)]), + ECBlocks::new(48, vec![ECB::new(1, 24)]), + ], + ), + Version::new_model1( + 4, + vec![ + ECBlocks::new(20, vec![ECB::new(1, 80)]), + ECBlocks::new(40, vec![ECB::new(1, 60)]), + ECBlocks::new(50, vec![ECB::new(1, 50)]), + ECBlocks::new(66, vec![ECB::new(1, 34)]), + ], + ), + Version::new_model1( + 5, + vec![ + ECBlocks::new(26, vec![ECB::new(1, 108)]), + ECBlocks::new(52, vec![ECB::new(1, 82)]), + ECBlocks::new(66, vec![ECB::new(1, 68)]), + ECBlocks::new(88, vec![ECB::new(2, 46)]), + ], + ), + Version::new_model1( + 6, + vec![ + ECBlocks::new(34, vec![ECB::new(1, 136)]), + ECBlocks::new(63, vec![ECB::new(2, 106)]), + ECBlocks::new(84, vec![ECB::new(2, 86)]), + ECBlocks::new(112, vec![ECB::new(2, 58)]), + ], + ), + Version::new_model1( + 7, + vec![ + ECBlocks::new(42, vec![ECB::new(1, 170)]), + ECBlocks::new(80, vec![ECB::new(2, 132)]), + ECBlocks::new(104, vec![ECB::new(2, 108)]), + ECBlocks::new(138, vec![ECB::new(3, 72)]), + ], + ), + Version::new_model1( + 8, + vec![ + ECBlocks::new(48, vec![ECB::new(2, 208)]), + ECBlocks::new(96, vec![ECB::new(2, 160)]), + ECBlocks::new(128, vec![ECB::new(2, 128)]), + ECBlocks::new(168, vec![ECB::new(3, 87)]), + ], + ), + Version::new_model1( + 9, + vec![ + ECBlocks::new(60, vec![ECB::new(2, 246)]), + ECBlocks::new(120, vec![ECB::new(2, 186)]), + ECBlocks::new(150, vec![ECB::new(3, 156)]), + ECBlocks::new(204, vec![ECB::new(3, 102)]), + ], + ), + Version::new_model1( + 10, + vec![ + ECBlocks::new(68, vec![ECB::new(2, 290)]), + ECBlocks::new(136, vec![ECB::new(2, 222)]), + ECBlocks::new(174, vec![ECB::new(3, 183)]), + ECBlocks::new(232, vec![ECB::new(4, 124)]), + ], + ), + Version::new_model1( + 11, + vec![ + ECBlocks::new(80, vec![ECB::new(2, 336)]), + ECBlocks::new(160, vec![ECB::new(4, 256)]), + ECBlocks::new(208, vec![ECB::new(4, 208)]), + ECBlocks::new(270, vec![ECB::new(5, 145)]), + ], + ), + Version::new_model1( + 12, + vec![ + ECBlocks::new(92, vec![ECB::new(2, 384)]), + ECBlocks::new(184, vec![ECB::new(4, 292)]), + ECBlocks::new(232, vec![ECB::new(4, 244)]), + ECBlocks::new(310, vec![ECB::new(5, 165)]), + ], + ), + Version::new_model1( + 13, + vec![ + ECBlocks::new(108, vec![ECB::new(3, 432)]), + ECBlocks::new(208, vec![ECB::new(4, 332)]), + ECBlocks::new(264, vec![ECB::new(4, 276)]), + ECBlocks::new(348, vec![ECB::new(6, 192)]), + ], + ), + Version::new_model1( + 14, + vec![ + ECBlocks::new(120, vec![ECB::new(3, 489)]), + ECBlocks::new(240, vec![ECB::new(4, 368)]), + ECBlocks::new(300, vec![ECB::new(5, 310)]), + ECBlocks::new(396, vec![ECB::new(6, 210)]), + ], + ), + ]) + } } impl fmt::Display for Version { diff --git a/test_resources/blackbox/cpp/qrcode-2/qr-model-1.metadata.txt b/test_resources/blackbox/cpp/qrcode-2/qr-model-1.metadata.txt new file mode 100644 index 0000000..7f36347 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-2/qr-model-1.metadata.txt @@ -0,0 +1,2 @@ +symbologyIdentifier=]Q0 +ecLevel=M diff --git a/test_resources/blackbox/cpp/qrcode-2/qr-model-1.png b/test_resources/blackbox/cpp/qrcode-2/qr-model-1.png new file mode 100644 index 0000000000000000000000000000000000000000..4b199902d0bae9a8fd98dbe698d570069d80ac40 GIT binary patch literal 230 zcmeAS@N?(olHy`uVBq!ia0vp^P9V(43?y&PnzR~7u?6^qxB}__|Nk$&IsYz@HQUq0 zF+}71+DnF9%my5+0S8SdtY(Q}jH(NKJ#hus_iDb=GVF(QR)qtdDm~>V#Of0&