feat: ported qr-model1 support from zxing-cpp

This commit is contained in:
Henry Schimke
2024-01-11 12:53:04 -06:00
parent 087124f24a
commit 4ed49e10ff
17 changed files with 485 additions and 41 deletions

View File

@@ -67,6 +67,8 @@ pub enum BarcodeFormat {
MICRO_QR_CODE, MICRO_QR_CODE,
RECTANGULAR_MICRO_QR_CODE,
/** RSS 14 */ /** RSS 14 */
RSS_14, RSS_14,
@@ -108,6 +110,7 @@ impl Display for BarcodeFormat {
BarcodeFormat::PDF_417 => "pdf 417", BarcodeFormat::PDF_417 => "pdf 417",
BarcodeFormat::QR_CODE => "qrcode", BarcodeFormat::QR_CODE => "qrcode",
BarcodeFormat::MICRO_QR_CODE => "mqr", BarcodeFormat::MICRO_QR_CODE => "mqr",
BarcodeFormat::RECTANGULAR_MICRO_QR_CODE => "rmqr",
BarcodeFormat::RSS_14 => "rss 14", BarcodeFormat::RSS_14 => "rss 14",
BarcodeFormat::RSS_EXPANDED => "rss expanded", BarcodeFormat::RSS_EXPANDED => "rss expanded",
BarcodeFormat::TELEPEN => "telepen", BarcodeFormat::TELEPEN => "telepen",
@@ -150,6 +153,9 @@ impl From<&str> for BarcodeFormat {
"mqr" | "microqr" | "micro_qr" | "micro_qrcode" | "micro_qr_code" | "mqr_code" => { "mqr" | "microqr" | "micro_qr" | "micro_qrcode" | "micro_qr_code" | "mqr_code" => {
BarcodeFormat::MICRO_QR_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" "rss 14" | "rss_14" | "rss14" | "gs1 databar" | "gs1 databar coupon"
| "gs1_databar_coupon" => BarcodeFormat::RSS_14, | "gs1_databar_coupon" => BarcodeFormat::RSS_14,
"rss expanded" | "expanded rss" | "rss_expanded" => BarcodeFormat::RSS_EXPANDED, "rss expanded" | "expanded rss" | "rss_expanded" => BarcodeFormat::RSS_EXPANDED,

View File

@@ -2,6 +2,8 @@ use crate::qrcode::decoder::{
ErrorCorrectionLevel, FormatInformation, FORMAT_INFO_DECODE_LOOKUP, FORMAT_INFO_MASK_QR, 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] = [ pub const FORMAT_INFO_DECODE_LOOKUP_MICRO: [[u32; 2]; 32] = [
[0x4445, 0x00], [0x4445, 0x00],
[0x4172, 0x01], [0x4172, 0x01],
@@ -50,7 +52,7 @@ impl FormatInformation {
let formatInfoBits2 = let formatInfoBits2 =
((formatInfoBits2 >> 1) & 0b111111100000000) | (formatInfoBits2 & 0b11111111); ((formatInfoBits2 >> 1) & 0b111111100000000) | (formatInfoBits2 & 0b11111111);
let mut fi = Self::FindBestFormatInfo( let mut fi = Self::FindBestFormatInfo(
FORMAT_INFO_MASK_QR, &[0, FORMAT_INFO_MASK_QR],
FORMAT_INFO_DECODE_LOOKUP, FORMAT_INFO_DECODE_LOOKUP,
&[ &[
formatInfoBits1, 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. // Use bits 3/4 for error correction, and 0-2 for mask.
fi.error_correction_level = fi.error_correction_level =
ErrorCorrectionLevel::ECLevelFromBits((fi.index >> 3) & 0x03, false); ErrorCorrectionLevel::ECLevelFromBits((fi.index >> 3) & 0x03, false);
@@ -72,7 +90,7 @@ impl FormatInformation {
pub fn DecodeMQR(formatInfoBits: u32) -> Self { pub fn DecodeMQR(formatInfoBits: u32) -> Self {
// We don't use the additional masking (with 0x4445) to work around potentially non complying MicroQRCode encoders // We don't use the additional masking (with 0x4445) to work around potentially non complying MicroQRCode encoders
let mut fi = Self::FindBestFormatInfo( let mut fi = Self::FindBestFormatInfo(
0, &[0],
FORMAT_INFO_DECODE_LOOKUP_MICRO, FORMAT_INFO_DECODE_LOOKUP_MICRO,
&[formatInfoBits, Self::MirrorBits(formatInfoBits)], &[formatInfoBits, Self::MirrorBits(formatInfoBits)],
); );
@@ -94,11 +112,11 @@ impl FormatInformation {
(bits.reverse_bits()) >> 17 (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(); let mut fi = FormatInformation::default();
// Some QR codes apparently do not apply the XOR mask. Try without and with additional masking. // 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 (auto mask : {0, mask})
for (bitsIndex, bit_set) in bits.iter().enumerate() { for (bitsIndex, bit_set) in bits.iter().enumerate() {
// for (int bitsIndex = 0; bitsIndex < Size(bits); ++bitsIndex) // for (int bitsIndex = 0; bitsIndex < Size(bits); ++bitsIndex)

View File

@@ -1,5 +1,7 @@
use crate::common::Result; 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; use crate::Exceptions;
// const Version* Version::AllMicroVersions() // const Version* Version::AllMicroVersions()
@@ -16,7 +18,7 @@ use crate::Exceptions;
// } // }
impl Version { impl Version {
pub fn FromDimension(dimension: u32) -> Result<VersionRef> { pub fn FromDimension(dimension: u32, is_model1: bool) -> Result<VersionRef> {
let isMicro = dimension < 21; let isMicro = dimension < 21;
if dimension % Self::DimensionStep(isMicro) != 1 { if dimension % Self::DimensionStep(isMicro) != 1 {
//throw std::invalid_argument("Unexpected dimension"); //throw std::invalid_argument("Unexpected dimension");
@@ -25,17 +27,31 @@ impl Version {
Self::FromNumber( Self::FromNumber(
(dimension - Self::DimensionOffset(isMicro)) / Self::DimensionStep(isMicro), (dimension - Self::DimensionOffset(isMicro)) / Self::DimensionStep(isMicro),
isMicro, isMicro,
is_model1,
) )
} }
pub fn FromNumber(versionNumber: u32, is_micro: bool) -> Result<VersionRef> { pub fn FromNumber(versionNumber: u32, is_micro: bool, is_model1: bool) -> Result<VersionRef> {
if versionNumber < 1 || versionNumber > (if is_micro { 4 } else { 40 }) { 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]."); //throw std::invalid_argument("Version should be in range [1-40].");
return Err(Exceptions::ILLEGAL_ARGUMENT); return Err(Exceptions::ILLEGAL_ARGUMENT);
} }
Ok(if is_micro { Ok(if is_micro {
&MICRO_VERSIONS[versionNumber as usize - 1] &MICRO_VERSIONS[versionNumber as usize - 1]
} else if is_model1 {
&MODEL1_VERSIONS[versionNumber as usize - 1]
} else { } else {
&VERSIONS[versionNumber as usize - 1] &VERSIONS[versionNumber as usize - 1]
}) })
@@ -92,4 +108,8 @@ impl Version {
pub const fn isMicroQRCode(&self) -> bool { pub const fn isMicroQRCode(&self) -> bool {
self.is_micro self.is_micro
} }
pub const fn isQRCodeModel1(&self) -> bool {
self.is_model1
}
} }

View File

@@ -157,6 +157,13 @@ where
self self
} }
pub fn withIsModel1(mut self, is_model_1: bool) -> DecoderResult<T> {
if is_model_1 {
self.content.symbology.modifier = 48
}
self
}
// pub fn build(self) -> DecoderResult<T> { // pub fn build(self) -> DecoderResult<T> {
// } // }

View File

@@ -183,6 +183,9 @@ impl MultiFormatReader {
} }
} }
BarcodeFormat::MICRO_QR_CODE => QrReader.decode_with_hints(image, &self.hints), 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 => { BarcodeFormat::DATA_MATRIX => {
DataMatrixReader.decode_with_hints(image, &self.hints) DataMatrixReader.decode_with_hints(image, &self.hints)
} }

View File

@@ -33,7 +33,7 @@ pub fn hasValidDimension(bitMatrix: &BitMatrix, isMicro: bool) -> bool {
pub fn ReadVersion(bitMatrix: &BitMatrix) -> Result<VersionRef> { pub fn ReadVersion(bitMatrix: &BitMatrix) -> Result<VersionRef> {
let dimension = bitMatrix.height(); let dimension = bitMatrix.height();
let mut version = Version::FromDimension(dimension)?; let mut version = Version::FromDimension(dimension, false)?;
if version.getVersionNumber() < 7 { if version.getVersionNumber() < 7 {
return Ok(version); return Ok(version);
@@ -235,6 +235,111 @@ pub fn ReadMQRCodewords(
Ok(result.iter().copied().map(|x| x as u8).collect()) Ok(result.iter().copied().map(|x| x as u8).collect())
} }
pub fn ReadQRCodewordsModel1(
bitMatrix: &BitMatrix,
version: VersionRef,
formatInfo: &FormatInformation,
) -> Result<Vec<u8>> {
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( pub fn ReadCodewords(
bitMatrix: &BitMatrix, bitMatrix: &BitMatrix,
version: VersionRef, version: VersionRef,
@@ -246,6 +351,8 @@ pub fn ReadCodewords(
if version.isMicroQRCode() { if version.isMicroQRCode() {
ReadMQRCodewords(bitMatrix, version, formatInfo) ReadMQRCodewords(bitMatrix, version, formatInfo)
} else if formatInfo.isModel1 {
ReadQRCodewordsModel1(bitMatrix, version, formatInfo)
} else { } else {
ReadQRCodewords(bitMatrix, version, formatInfo) ReadQRCodewords(bitMatrix, version, formatInfo)
} }

View File

@@ -299,6 +299,10 @@ pub fn DecodeBitStream(
let mut structuredAppend = StructuredAppendInfo::default(); let mut structuredAppend = StructuredAppendInfo::default();
let modeBitLength = Mode::get_codec_mode_bits_length(version); 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 = (|| { let res = (|| {
while !IsEndOfStream(&mut bits, version)? { while !IsEndOfStream(&mut bits, version)? {
let mode: Mode = if modeBitLength == 0 { let mode: Mode = if modeBitLength == 0 {
@@ -387,11 +391,21 @@ pub fn DecodeBitStream(
.withError(res.err()) .withError(res.err())
.withEcLevel(ecLevel.to_string()) .withEcLevel(ecLevel.to_string())
.withVersionNumber(version.getVersionNumber()) .withVersionNumber(version.getVersionNumber())
.withStructuredAppend(structuredAppend)) .withStructuredAppend(structuredAppend)
.withIsModel1(version.isQRCodeModel1()))
} }
pub fn Decode(bits: &BitMatrix) -> Result<DecoderResult<bool>> { pub fn Decode(bits: &BitMatrix) -> Result<DecoderResult<bool>> {
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")); return Err(Exceptions::format_with("Invalid version"));
}; };
let version = pversion; let version = pversion;

View File

@@ -42,7 +42,7 @@ fn SimpleByteMode() {
let bytes: Vec<u8> = ba.into(); let bytes: Vec<u8> = ba.into();
let result = DecodeBitStream( let result = DecodeBitStream(
&bytes, &bytes,
Version::FromNumber(1, false).expect("find_version"), Version::FromNumber(1, false, false).expect("find_version"),
ErrorCorrectionLevel::M, ErrorCorrectionLevel::M,
) )
.expect("Decode") .expect("Decode")
@@ -64,7 +64,7 @@ fn SimpleSJIS() {
let bytes: Vec<u8> = ba.into(); let bytes: Vec<u8> = ba.into();
let result = DecodeBitStream( let result = DecodeBitStream(
&bytes, &bytes,
Version::FromNumber(1, false).unwrap(), Version::FromNumber(1, false, false).unwrap(),
ErrorCorrectionLevel::M, ErrorCorrectionLevel::M,
) )
.unwrap() .unwrap()
@@ -86,7 +86,7 @@ fn ECI() {
let bytes: Vec<u8> = ba.into(); let bytes: Vec<u8> = ba.into();
let result = DecodeBitStream( let result = DecodeBitStream(
&bytes, &bytes,
Version::FromNumber(1, false).unwrap(), Version::FromNumber(1, false, false).unwrap(),
ErrorCorrectionLevel::M, ErrorCorrectionLevel::M,
) )
.unwrap() .unwrap()
@@ -105,7 +105,7 @@ fn Hanzi() {
let bytes: Vec<u8> = ba.into(); let bytes: Vec<u8> = ba.into();
let result = DecodeBitStream( let result = DecodeBitStream(
&bytes, &bytes,
Version::FromNumber(1, false).unwrap(), Version::FromNumber(1, false, false).unwrap(),
ErrorCorrectionLevel::M, ErrorCorrectionLevel::M,
) )
.unwrap() .unwrap()
@@ -127,7 +127,7 @@ fn HanziLevel1() {
let result = DecodeBitStream( let result = DecodeBitStream(
&bytes, &bytes,
Version::FromNumber(1, false).unwrap(), Version::FromNumber(1, false, false).unwrap(),
ErrorCorrectionLevel::M, ErrorCorrectionLevel::M,
) )
.unwrap() .unwrap()
@@ -138,7 +138,7 @@ fn HanziLevel1() {
#[test] #[test]
fn SymbologyIdentifier() { fn SymbologyIdentifier() {
let version = Version::FromNumber(1, false).unwrap(); let version = Version::FromNumber(1, false, false).unwrap();
let ecLevel = ErrorCorrectionLevel::M; let ecLevel = ErrorCorrectionLevel::M;
// Plain "ANUM(1) A" // Plain "ANUM(1) A"

View File

@@ -63,7 +63,7 @@ fn DecodeWithBitDifference() {
MASKED_TEST_FORMAT_INFO2 ^ 0x07, MASKED_TEST_FORMAT_INFO2 ^ 0x07,
), ),
); );
assert!(!FormatInformation::DecodeQR( assert!(FormatInformation::DecodeQR(
MASKED_TEST_FORMAT_INFO ^ 0x0F, MASKED_TEST_FORMAT_INFO ^ 0x0F,
MASKED_TEST_FORMAT_INFO2 ^ 0x0F MASKED_TEST_FORMAT_INFO2 ^ 0x0F
) )

View File

@@ -27,27 +27,39 @@ fn CharacterCount() {
// Spot check a few values // Spot check a few values
assert_eq!( assert_eq!(
10, 10,
Mode::CharacterCountBits(&Mode::NUMERIC, Version::FromNumber(5, false).unwrap()) Mode::CharacterCountBits(
&Mode::NUMERIC,
Version::FromNumber(5, false, false).unwrap()
)
); );
assert_eq!( assert_eq!(
12, 12,
Mode::CharacterCountBits(&Mode::NUMERIC, Version::FromNumber(26, false).unwrap()) Mode::CharacterCountBits(
&Mode::NUMERIC,
Version::FromNumber(26, false, false).unwrap()
)
); );
assert_eq!( assert_eq!(
14, 14,
Mode::CharacterCountBits(&Mode::NUMERIC, Version::FromNumber(40, false).unwrap()) Mode::CharacterCountBits(
&Mode::NUMERIC,
Version::FromNumber(40, false, false).unwrap()
)
); );
assert_eq!( assert_eq!(
9, 9,
Mode::CharacterCountBits(&Mode::ALPHANUMERIC, Version::FromNumber(6, false).unwrap()) Mode::CharacterCountBits(
&Mode::ALPHANUMERIC,
Version::FromNumber(6, false, false).unwrap()
)
); );
assert_eq!( assert_eq!(
8, 8,
Mode::CharacterCountBits(&Mode::BYTE, Version::FromNumber(7, false).unwrap()) Mode::CharacterCountBits(&Mode::BYTE, Version::FromNumber(7, false, false).unwrap())
); );
assert_eq!( assert_eq!(
8, 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 // Spot check a few values
assert_eq!( assert_eq!(
3, 3,
Mode::CharacterCountBits(&Mode::NUMERIC, Version::FromNumber(1, true).unwrap()) Mode::CharacterCountBits(&Mode::NUMERIC, Version::FromNumber(1, true, false).unwrap())
); );
assert_eq!( assert_eq!(
4, 4,
Mode::CharacterCountBits(&Mode::NUMERIC, Version::FromNumber(2, true).unwrap()) Mode::CharacterCountBits(&Mode::NUMERIC, Version::FromNumber(2, true, false).unwrap())
); );
assert_eq!( assert_eq!(
6, 6,
Mode::CharacterCountBits(&Mode::NUMERIC, Version::FromNumber(4, true).unwrap()) Mode::CharacterCountBits(&Mode::NUMERIC, Version::FromNumber(4, true, false).unwrap())
); );
assert_eq!( assert_eq!(
3, 3,
Mode::CharacterCountBits(&Mode::ALPHANUMERIC, Version::FromNumber(2, true).unwrap()) Mode::CharacterCountBits(
&Mode::ALPHANUMERIC,
Version::FromNumber(2, true, false).unwrap()
)
); );
assert_eq!( assert_eq!(
4, 4,
Mode::CharacterCountBits(&Mode::BYTE, Version::FromNumber(3, true).unwrap()) Mode::CharacterCountBits(&Mode::BYTE, Version::FromNumber(3, true, false).unwrap())
); );
assert_eq!( assert_eq!(
4, 4,
Mode::CharacterCountBits(&Mode::KANJI, Version::FromNumber(4, true).unwrap()) Mode::CharacterCountBits(&Mode::KANJI, Version::FromNumber(4, true, false).unwrap())
); );
} }

View File

@@ -26,13 +26,13 @@ fn DoTestVersion(expectedVersion: u32, mask: i32) {
#[test] #[test]
fn VersionForNumber() { 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"); assert!(version.is_err(), "There is version with number 0");
for i in 1..=40 { for i in 1..=40 {
// for (int i = 1; i <= 40; i++) { // for (int i = 1; i <= 40; i++) {
CheckVersion( CheckVersion(
Version::FromNumber(i, false).expect("version number found"), Version::FromNumber(i, false, false).expect("version number found"),
i, i,
4 * i + 17, 4 * i + 17,
); );
@@ -43,7 +43,7 @@ fn VersionForNumber() {
fn GetProvisionalVersionForDimension() { fn GetProvisionalVersionForDimension() {
for i in 1..=40 { for i in 1..=40 {
// for (int i = 1; i <= 40; i++) { // 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}")); .unwrap_or_else(|_| panic!("version should exist for {i}"));
// assert_ne!(prov, nullptr); // assert_ne!(prov, nullptr);
assert_eq!(i, prov.getVersionNumber()); assert_eq!(i, prov.getVersionNumber());
@@ -63,13 +63,14 @@ fn DecodeVersionInformation() {
#[test] #[test]
fn MicroVersionForNumber() { 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"); assert!(version.is_err(), "There is version with number 0");
for i in 1..=4 { for i in 1..=4 {
// for (int i = 1; i <= 4; i++) { // for (int i = 1; i <= 4; i++) {
CheckVersion( 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, i,
2 * i + 9, 2 * i + 9,
); );
@@ -80,7 +81,7 @@ fn MicroVersionForNumber() {
fn GetProvisionalMicroVersionForDimension() { fn GetProvisionalMicroVersionForDimension() {
for i in 1..=4 { for i in 1..=4 {
// for (int i = 1; i <= 4; i++) { // 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")); .unwrap_or_else(|_| panic!("version for micro {i} should exist"));
// assert_ne!(prov, nullptr); // assert_ne!(prov, nullptr);
assert_eq!(i, prov.getVersionNumber()); assert_eq!(i, prov.getVersionNumber());
@@ -100,7 +101,7 @@ fn FunctionPattern() {
}; };
for i in 1..=4 { for i in 1..=4 {
// for (int i = 1; i <= 4; i++) { // 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 let functionPattern = version
.buildFunctionPattern() .buildFunctionPattern()
.expect("function pattern must be found"); .expect("function pattern must be found");

View File

@@ -73,6 +73,7 @@ pub struct FormatInformation {
pub data_mask: u8, pub data_mask: u8,
pub microVersion: u32, pub microVersion: u32,
pub isMirrored: bool, pub isMirrored: bool,
pub isModel1: bool,
pub index: u8, // = 255; pub index: u8, // = 255;
pub bitsIndex: u8, // = 255; pub bitsIndex: u8, // = 255;
@@ -86,6 +87,7 @@ impl Default for FormatInformation {
data_mask: Default::default(), data_mask: Default::default(),
microVersion: 0, microVersion: 0,
isMirrored: false, isMirrored: false,
isModel1: false,
index: 255, index: 255,
bitsIndex: 255, bitsIndex: 255,
} }
@@ -104,6 +106,7 @@ impl FormatInformation {
error_correction_level: errorCorrectionLevel, error_correction_level: errorCorrectionLevel,
data_mask: dataMask, data_mask: dataMask,
isMirrored: false, isMirrored: false,
isModel1: false,
index: 255, index: 255,
bitsIndex: 255, bitsIndex: 255,
}) })

View File

@@ -29,6 +29,7 @@ pub type VersionRef = &'static Version;
pub static VERSIONS: Lazy<Vec<Version>> = Lazy::new(Version::buildVersions); pub static VERSIONS: Lazy<Vec<Version>> = Lazy::new(Version::buildVersions);
pub static MICRO_VERSIONS: Lazy<Vec<Version>> = Lazy::new(Version::build_micro_versions); pub static MICRO_VERSIONS: Lazy<Vec<Version>> = Lazy::new(Version::build_micro_versions);
pub static MODEL1_VERSIONS: Lazy<Vec<Version>> = Lazy::new(Version::build_model1_versions);
/** /**
* See ISO 18004:2006 Annex D. * See ISO 18004:2006 Annex D.
@@ -55,6 +56,7 @@ pub struct Version {
ecBlocks: Vec<ECBlocks>, ecBlocks: Vec<ECBlocks>,
totalCodewords: u32, totalCodewords: u32,
pub(crate) is_micro: bool, pub(crate) is_micro: bool,
pub(crate) is_model1: bool,
} }
impl Version { impl Version {
fn new(versionNumber: u32, alignmentPatternCenters: Vec<u32>, ecBlocks: [ECBlocks; 4]) -> Self { fn new(versionNumber: u32, alignmentPatternCenters: Vec<u32>, ecBlocks: [ECBlocks; 4]) -> Self {
@@ -73,6 +75,7 @@ impl Version {
ecBlocks: ecBlocks.to_vec(), ecBlocks: ecBlocks.to_vec(),
totalCodewords: total, totalCodewords: total,
is_micro: false, is_micro: false,
is_model1: false,
} }
} }
@@ -92,9 +95,35 @@ impl Version {
ecBlocks, ecBlocks,
totalCodewords: total, totalCodewords: total,
is_micro: true, is_micro: true,
is_model1: false,
} }
} }
fn new_model1(versionNumber: u32, ecBlocks: Vec<ECBlocks>) -> 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 { pub const fn getVersionNumber(&self) -> u32 {
self.versionNumber self.versionNumber
} }
@@ -970,6 +999,224 @@ impl Version {
new ECB::new(61, 16))) 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<Version> {
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 { impl fmt::Display for Version {

View File

@@ -0,0 +1,2 @@
symbologyIdentifier=]Q0
ecLevel=M

Binary file not shown.

After

Width:  |  Height:  |  Size: 230 B

View File

@@ -0,0 +1 @@
QR Code Model 1

View File

@@ -198,10 +198,10 @@ fn cpp_qrcode_black_box2_test_case() {
BarcodeFormat::QR_CODE, BarcodeFormat::QR_CODE,
); );
tester.add_test(45, 47, 0.0); tester.add_test(46, 48, 0.0);
tester.add_test(45, 47, 90.0); tester.add_test(46, 48, 90.0);
tester.add_test(45, 47, 180.0); tester.add_test(46, 48, 180.0);
tester.add_test(45, 46, 270.0); tester.add_test(46, 48, 270.0);
tester.ignore_pure = true; tester.ignore_pure = true;