Files
rxing/src/qrcode/decoder/version.rs
2024-01-16 12:42:46 -06:00

367 lines
12 KiB
Rust
Executable File

/*
* 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 std::fmt;
use crate::{
common::{BitMatrix, Result},
qrcode::cpp_port::Type,
Exceptions,
};
use super::{ErrorCorrectionLevel, FormatInformation};
use once_cell::sync::Lazy;
pub type VersionRef = &'static Version;
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 MODEL1_VERSIONS: Lazy<Vec<Version>> = Lazy::new(Version::build_model1_versions);
pub static RMQR_VERSIONS: Lazy<Vec<Version>> = Lazy::new(Version::build_rmqr_versions);
/**
* See ISO 18004:2006 Annex D.
* Element i represents the raw version bits that specify version i + 7
*/
pub 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: &'static[Version] = &Version::buildVersions();
/**
* See ISO 18004:2006 Annex D
*
* @author Sean Owen
*/
#[derive(Debug)]
pub struct Version {
// private static final Version[] VERSIONS = buildVersions();
versionNumber: u32,
alignmentPatternCenters: Vec<u32>,
ecBlocks: Vec<ECBlocks>,
totalCodewords: u32,
pub(crate) qr_type: Type,
}
impl Version {
pub(super) fn new(
versionNumber: u32,
alignmentPatternCenters: Vec<u32>,
ecBlocks: [ECBlocks; 4],
) -> Self {
let mut total = 0;
let ecCodewords = ecBlocks[1].getECCodewordsPerBlock();
let ecbArray = ecBlocks[1].getECBlocks();
// let mut i = 0;
for ecb in ecbArray {
// while i < ecbArray.len() {
total += ecb.getCount() * (ecb.getDataCodewords() + ecCodewords);
// i += 1;
}
Self {
versionNumber,
alignmentPatternCenters,
ecBlocks: ecBlocks.to_vec(),
totalCodewords: total,
qr_type: if ecBlocks[0].getECCodewordsPerBlock() != 0 {
Type::Model2
} else {
Type::RectMicro
},
}
}
pub(super) fn new_micro(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,
qr_type: Type::Micro,
}
}
pub(super) 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,
qr_type: Type::Model1,
}
}
pub const fn getVersionNumber(&self) -> u32 {
self.versionNumber
}
pub fn getAlignmentPatternCenters(&self) -> &[u32] {
&self.alignmentPatternCenters
}
pub fn getTotalCodewords(&self) -> u32 {
self.totalCodewords
}
pub fn getDimensionForVersion(&self) -> u32 {
Self::DimensionOfVersion(self.versionNumber, self.qr_type == Type::Micro)
// 17 + 4 * self.versionNumber
}
pub fn getECBlocksForLevel(&self, ecLevel: ErrorCorrectionLevel) -> &ECBlocks {
if ecLevel.get_ordinal() as usize >= self.ecBlocks.len() {
return &self.ecBlocks[ecLevel.get_ordinal() as usize % self.ecBlocks.len()];
}
&self.ecBlocks[ecLevel.get_ordinal() as usize]
}
/**
* <p>Deduces version information purely from QR Code dimensions.</p>
*
* @param dimension dimension in modules
* @return Version for a QR Code of that dimension
* @throws FormatException if dimension is not 1 mod 4
*/
pub fn getProvisionalVersionForDimension(dimension: u32) -> Result<VersionRef> {
if dimension % 4 != 1 {
return Err(Exceptions::format_with("dimension incorrect"));
}
Self::getVersionForNumber((dimension - 17) / 4)
}
pub fn getVersionForNumber(versionNumber: u32) -> Result<VersionRef> {
if !(1..=40).contains(&versionNumber) {
return Err(Exceptions::illegal_argument_with("version out of spec"));
}
Ok(&VERSIONS[versionNumber as usize - 1])
}
pub fn decodeVersionInformation(versionBits: u32) -> Result<VersionRef> {
let mut bestDifference = u32::MAX;
let mut bestVersion = 0;
for i in 0..VERSION_DECODE_INFO.len() as u32 {
let targetVersion = VERSION_DECODE_INFO[i as usize];
// Do the version info bits match exactly? done.
if targetVersion == versionBits {
return Self::getVersionForNumber(i + 7);
}
// Otherwise see if this is the closest to a real version info bit string
// we have seen so far
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 Self::getVersionForNumber(bestVersion);
}
// If we didn't find a close enough match, fail
Err(Exceptions::NOT_FOUND)
}
/**
* See ISO 18004:2006 Annex E
*/
pub fn buildFunctionPattern(&self) -> Result<BitMatrix> {
if self.isRMQR() {
let size = Version::SymbolSize(self.versionNumber, Type::RectMicro);
let mut bitMatrix = BitMatrix::new(size.x as u32, size.y as u32)?;
// Set edge timing patterns
bitMatrix.setRegion(0, 0, size.x as u32, 1)?; // Top
bitMatrix.setRegion(0, (size.y - 1) as u32, size.x as u32, 1)?; // Bottom
bitMatrix.setRegion(0, 1, 1, (size.y - 2) as u32)?; // Left
bitMatrix.setRegion((size.x - 1) as u32, 1, 1, (size.y - 2) as u32)?; // Right
// Set vertical timing and alignment patterns
let max = self.alignmentPatternCenters.len(); // Same as vertical timing column
for x in 0..max {
// for (size_t x = 0; x < max; ++x) {
let cx = self.alignmentPatternCenters[x];
bitMatrix.setRegion(cx - 1, 1, 3, 2)?; // Top alignment pattern
bitMatrix.setRegion(cx - 1, (size.y - 3) as u32, 3, 2)?; // Bottom alignment pattern
bitMatrix.setRegion(cx, 3, 1, (size.y - 6) as u32)?; // Vertical timing pattern
}
// Top left finder pattern + separator
bitMatrix.setRegion(1, 1, 8 - 1, 8 - 1 - u32::from(size.y == 7))?; // R7 finder bottom flush with edge
// Top left format
bitMatrix.setRegion(8, 1, 3, 5)?;
bitMatrix.setRegion(11, 1, 1, 3)?;
// Bottom right finder subpattern
bitMatrix.setRegion((size.x - 5) as u32, (size.y - 5) as u32, 5 - 1, 5 - 1)?;
// Bottom right format
bitMatrix.setRegion((size.x - 8) as u32, (size.y - 6) as u32, 3, 5)?;
bitMatrix.setRegion((size.x - 5) as u32, (size.y - 6) as u32, 3, 1)?;
// Top right corner finder
bitMatrix.set((size.x - 2) as u32, 1);
if size.y > 9 {
// Bottom left corner finder
bitMatrix.set(1, (size.y - 2) as u32);
}
return Ok(bitMatrix);
}
let dimension = self.getDimensionForVersion();
let mut bitMatrix = BitMatrix::with_single_dimension(dimension)?;
// Top left finder pattern + separator + format
bitMatrix.setRegion(0, 0, 9, 9)?;
if self.qr_type != Type::Micro {
// Top right finder pattern + separator + format
bitMatrix.setRegion(dimension - 8, 0, 8, 9)?;
// Bottom left finder pattern + separator + format
bitMatrix.setRegion(0, dimension - 8, 9, 8)?;
// Alignment patterns
let max = self.alignmentPatternCenters.len();
for x in 0..max {
let i = self.alignmentPatternCenters[x] - 2;
for y in 0..max {
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
}
}
// Vertical timing pattern
bitMatrix.setRegion(6, 9, 1, dimension - 17)?;
// Horizontal timing pattern
bitMatrix.setRegion(9, 6, dimension - 17, 1)?;
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)?;
}
} else {
// Vertical timing pattern
bitMatrix.setRegion(9, 0, dimension - 9, 1)?;
// Horizontal timing pattern
bitMatrix.setRegion(0, 9, 1, dimension - 9)?;
}
Ok(bitMatrix)
}
}
impl fmt::Display for Version {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.versionNumber)
}
}
/**
* <p>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.</p>
*/
#[derive(Debug, Clone)]
pub struct ECBlocks {
ecCodewordsPerBlock: u32,
ecBlocks: Vec<ECB>,
}
impl ECBlocks {
pub const fn new(ecCodewordsPerBlock: u32, ecBlocks: Vec<ECB>) -> Self {
Self {
ecCodewordsPerBlock,
ecBlocks,
}
}
pub const fn getECCodewordsPerBlock(&self) -> u32 {
self.ecCodewordsPerBlock
}
pub fn getNumBlocks(&self) -> u32 {
let mut total = 0;
for ecBlock in &self.ecBlocks {
total += ecBlock.getCount();
}
total
}
pub fn getTotalECCodewords(&self) -> u32 {
self.ecCodewordsPerBlock * self.getNumBlocks()
}
pub fn getECBlocks(&self) -> &[ECB] {
&self.ecBlocks
}
}
/**
* <p>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.</p>
*/
#[derive(Debug, Clone, Copy)]
pub struct ECB {
count: u32,
dataCodewords: u32,
}
impl ECB {
pub const fn new(count: u32, dataCodewords: u32) -> Self {
Self {
count,
dataCodewords,
}
}
pub const fn getCount(&self) -> u32 {
self.count
}
pub const fn getDataCodewords(&self) -> u32 {
self.dataCodewords
}
}