steady updates for qr

This commit is contained in:
Henry Schimke
2022-09-30 18:10:04 -05:00
parent 9f0fcf7ce9
commit 2a80c95d75
3 changed files with 314 additions and 236 deletions

View File

@@ -39,7 +39,7 @@ impl ErrorCorrectionLevel {
* @param bits int containing the two bits encoding a QR Code's error correction level * @param bits int containing the two bits encoding a QR Code's error correction level
* @return ErrorCorrectionLevel representing the encoded error correction level * @return ErrorCorrectionLevel representing the encoded error correction level
*/ */
pub fn forBits(bits: u32) -> Result<Self, Exceptions> { pub fn forBits(bits: u8) -> Result<Self, Exceptions> {
match bits { match bits {
0 => Ok(Self::M), 0 => Ok(Self::M),
1 => Ok(Self::L), 1 => Ok(Self::L),
@@ -62,6 +62,20 @@ impl ErrorCorrectionLevel {
} }
} }
impl TryFrom<u8> for ErrorCorrectionLevel {
type Error = Exceptions;
fn try_from(value: u8) -> Result<Self, Self::Error> {
ErrorCorrectionLevel::forBits(value)
}
}
impl From<ErrorCorrectionLevel> for u8 {
fn from(value: ErrorCorrectionLevel) -> Self {
value.get_value()
}
}
//private static final ErrorCorrectionLevel[] FOR_BITS = {M, L, H, Q}; //private static final ErrorCorrectionLevel[] FOR_BITS = {M, L, H, Q};
//private final int bits; //private final int bits;

View File

@@ -14,64 +14,61 @@
* limitations under the License. * limitations under the License.
*/ */
use crate::Exceptions;
/** /**
* <p>See ISO 18004:2006, 6.4.1, Tables 2 and 3. This enum encapsulates the various modes in which * <p>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.</p> * data can be encoded to bits in the QR code standard.</p>
* *
* @author Sean Owen * @author Sean Owen
*/ */
public enum Mode { pub enum Mode {
TERMINATOR, //(new int[]{0, 0, 0}, 0x00), // Not really a mode...
TERMINATOR(new int[]{0, 0, 0}, 0x00), // Not really a mode... NUMERIC, //(new int[]{10, 12, 14}, 0x01),
NUMERIC(new int[]{10, 12, 14}, 0x01), ALPHANUMERIC, //(new int[]{9, 11, 13}, 0x02),
ALPHANUMERIC(new int[]{9, 11, 13}, 0x02), STRUCTURED_APPEND, //(new int[]{0, 0, 0}, 0x03), // Not supported
STRUCTURED_APPEND(new int[]{0, 0, 0}, 0x03), // Not supported BYTE, //(new int[]{8, 16, 16}, 0x04),
BYTE(new int[]{8, 16, 16}, 0x04), ECI, //(new int[]{0, 0, 0}, 0x07), // character counts don't apply
ECI(new int[]{0, 0, 0}, 0x07), // character counts don't apply KANJI, //(new int[]{8, 10, 12}, 0x08),
KANJI(new int[]{8, 10, 12}, 0x08), FNC1_FIRST_POSITION, //(new int[]{0, 0, 0}, 0x05),
FNC1_FIRST_POSITION(new int[]{0, 0, 0}, 0x05), FNC1_SECOND_POSITION, //(new int[]{0, 0, 0}, 0x09),
FNC1_SECOND_POSITION(new int[]{0, 0, 0}, 0x09),
/** See GBT 18284-2000; "Hanzi" is a transliteration of this mode name. */ /** See GBT 18284-2000; "Hanzi" is a transliteration of this mode name. */
HANZI(new int[]{8, 10, 12}, 0x0D); 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;
} }
// 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 * @param bits four bits encoding a QR Code data mode
* @return Mode encoded by these bits * @return Mode encoded by these bits
* @throws IllegalArgumentException if bits do not correspond to a known mode * @throws IllegalArgumentException if bits do not correspond to a known mode
*/ */
public static Mode forBits(int bits) { pub fn forBits(bits: u8) -> Result<Self, Exceptions> {
switch (bits) { match bits {
case 0x0: 0x0 => Ok(Self::TERMINATOR),
return TERMINATOR; 0x1 => Ok(Self::NUMERIC),
case 0x1: 0x2 => Ok(Self::ALPHANUMERIC),
return NUMERIC; 0x3 => Ok(Self::STRUCTURED_APPEND),
case 0x2: 0x4 => Ok(Self::BYTE),
return ALPHANUMERIC; 0x5 => Ok(Self::FNC1_FIRST_POSITION),
case 0x3: 0x7 => Ok(Self::ECI),
return STRUCTURED_APPEND; 0x8 => Ok(Self::KANJI),
case 0x4: 0x9 => Ok(Self::FNC1_SECOND_POSITION),
return BYTE; 0xD =>
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 // 0xD is defined in GBT 18284-2000, may not be supported in foreign country
return HANZI; {
default: Ok(Self::HANZI)
throw new IllegalArgumentException(); }
_ => Err(Exceptions::IllegalArgumentException(format!(
"{} is not valid",
bits
))),
} }
} }
@@ -80,21 +77,59 @@ public enum Mode {
* @return number of bits used, in this QR Code symbol {@link Version}, to encode the * @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 * count of characters that will follow encoded in this Mode
*/ */
public int getCharacterCountBits(Version version) { pub fn getCharacterCountBits(&self, version: &Version) -> u8 {
int number = version.getVersionNumber(); let number = version.getVersionNumber();
int offset; let offset = if number <= 9 {
if (number <= 9) { 0
offset = 0; } else if number <= 26 {
} else if (number <= 26) { 1
offset = 1;
} else { } else {
offset = 2; 2
} };
return characterCountBitsForVersions[offset]; self.get_character_counts()[offset]
} }
public int getBits() { fn get_character_counts(&self) -> &[u8] {
return bits; 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<Mode> for u8 {
fn from(value: Mode) -> Self {
value.getBits()
}
}
impl TryFrom<u8> for Mode {
type Error = Exceptions;
fn try_from(value: u8) -> Result<Self, Self::Error> {
Self::forBits(value)
}
} }

View File

@@ -14,18 +14,17 @@
* limitations under the License. * limitations under the License.
*/ */
/** use std::fmt;
* See ISO 18004:2006 Annex D
* use crate::{Exceptions, common::BitMatrix};
* @author Sean Owen
*/ use super::ErrorCorrectionLevel;
pub struct Version {
/** /**
* See ISO 18004:2006 Annex D. * See ISO 18004:2006 Annex D.
* Element i represents the raw version bits that specify version i + 7 * Element i represents the raw version bits that specify version i + 7
*/ */
private static final int[] VERSION_DECODE_INFO = { const VERSION_DECODE_INFO : [u32;34] = [
0x07C94, 0x085BC, 0x09A99, 0x0A4D3, 0x0BBF6, 0x07C94, 0x085BC, 0x09A99, 0x0A4D3, 0x0BBF6,
0x0C762, 0x0D847, 0x0E60D, 0x0F928, 0x10B78, 0x0C762, 0x0D847, 0x0E60D, 0x0F928, 0x10B78,
0x1145D, 0x12A17, 0x13532, 0x149A6, 0x15683, 0x1145D, 0x12A17, 0x13532, 0x149A6, 0x15683,
@@ -33,50 +32,66 @@ pub struct Version {
0x1B08E, 0x1CC1A, 0x1D33F, 0x1ED75, 0x1F250, 0x1B08E, 0x1CC1A, 0x1D33F, 0x1ED75, 0x1F250,
0x209D5, 0x216F0, 0x228BA, 0x2379F, 0x24B0B, 0x209D5, 0x216F0, 0x228BA, 0x2379F, 0x24B0B,
0x2542E, 0x26A64, 0x27541, 0x28C69 0x2542E, 0x26A64, 0x27541, 0x28C69
}; ];
private static final Version[] VERSIONS = buildVersions(); const VERSIONS :Vec<Version> = Version::buildVersions();
/**
* See ISO 18004:2006 Annex D
*
* @author Sean Owen
*/
pub struct Version {
private final int versionNumber; // private static final Version[] VERSIONS = buildVersions();
private final int[] alignmentPatternCenters;
private final ECBlocks[] ecBlocks; versionNumber: u32,
private final int totalCodewords; alignmentPatternCenters: Vec<u32>,
ecBlocks:Vec<ECBlocks>,
totalCodewords:u32,
} }
impl Version { impl Version {
private Version(int versionNumber, const fn new( versionNumber:u32,
int[] alignmentPatternCenters, alignmentPatternCenters:Vec<u32>,
ECBlocks... ecBlocks) { ecBlocks:Vec<ECBlocks>) -> Self {
this.versionNumber = versionNumber;
this.alignmentPatternCenters = alignmentPatternCenters; let total = 0;
this.ecBlocks = ecBlocks; let ecCodewords = ecBlocks[0].getECCodewordsPerBlock();
int total = 0; let ecbArray = ecBlocks[0].getECBlocks();
int ecCodewords = ecBlocks[0].getECCodewordsPerBlock(); let mut i = 0;
ECB[] ecbArray = ecBlocks[0].getECBlocks(); while i < ecbArray.len() {
for (ECB ecBlock : ecbArray) { // for ecBlock in ecbArray {
total += ecBlock.getCount() * (ecBlock.getDataCodewords() + ecCodewords); // for (ECB ecBlock : ecbArray) {
} total += ecbArray[i].getCount() * (ecbArray[i].getDataCodewords() + ecCodewords);
this.totalCodewords = total; i+=1;
} }
public int getVersionNumber() { Self {
return versionNumber; versionNumber,
alignmentPatternCenters,
ecBlocks,
totalCodewords: total,
}
} }
public int[] getAlignmentPatternCenters() { pub fn getVersionNumber(&self) -> u32 {
return alignmentPatternCenters; self. versionNumber
} }
public int getTotalCodewords() { pub fn getAlignmentPatternCenters(&self) -> &[u32]{
return totalCodewords; &self. alignmentPatternCenters
} }
public int getDimensionForVersion() { pub fn getTotalCodewords(&self) -> u32{
return 17 + 4 * versionNumber; self.totalCodewords
} }
public ECBlocks getECBlocksForLevel(ErrorCorrectionLevel ecLevel) { pub fn getDimensionForVersion(&self)->u32 {
return ecBlocks[ecLevel.ordinal()]; 17 + 4 * self.versionNumber
}
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 * @return Version for a QR Code of that dimension
* @throws FormatException if dimension is not 1 mod 4 * @throws FormatException if dimension is not 1 mod 4
*/ */
public static Version getProvisionalVersionForDimension(int dimension) throws FormatException { pub fn getProvisionalVersionForDimension( dimension:u32) -> Result<Version,Exceptions> {
if (dimension % 4 != 1) { if dimension % 4 != 1 {
throw FormatException.getFormatInstance(); return Err(Exceptions::FormatException("dimension incorrect".to_owned()));
}
try {
return getVersionForNumber((dimension - 17) / 4);
} catch (IllegalArgumentException ignored) {
throw FormatException.getFormatInstance();
} }
Self::getVersionForNumber((dimension - 17) / 4)
// try {
// return getVersionForNumber((dimension - 17) / 4);
// } catch (IllegalArgumentException ignored) {
// throw FormatException.getFormatInstance();
// }
} }
public static Version getVersionForNumber(int versionNumber) { pub fn getVersionForNumber( versionNumber:u32) -> Result<Version,Exceptions>{
if (versionNumber < 1 || versionNumber > 40) { if versionNumber < 1 || versionNumber > 40 {
throw new IllegalArgumentException(); 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) { pub fn decodeVersionInformation( versionBits:u32) -> Result<Version,Exceptions> {
int bestDifference = Integer.MAX_VALUE; let bestDifference = u32::MAX;
int bestVersion = 0; let bestVersion = 0;
for (int i = 0; i < VERSION_DECODE_INFO.length; i++) { for i in 0..VERSION_DECODE_INFO.len() as u32 {
int targetVersion = VERSION_DECODE_INFO[i]; // for (int i = 0; i < VERSION_DECODE_INFO.length; i++) {
let targetVersion = VERSION_DECODE_INFO[i];
// Do the version info bits match exactly? done. // Do the version info bits match exactly? done.
if (targetVersion == versionBits) { 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 // Otherwise see if this is the closest to a real version info bit string
// we have seen so far // we have seen so far
int bitsDifference = FormatInformation.numBitsDiffering(versionBits, targetVersion); let bitsDifference = FormatInformation.numBitsDiffering(versionBits, targetVersion);
if (bitsDifference < bestDifference) { if bitsDifference < bestDifference {
bestVersion = i + 7; bestVersion = i + 7;
bestDifference = bitsDifference; bestDifference = bitsDifference;
} }
} }
// We can tolerate up to 3 bits of error since no two version info codewords will // We can tolerate up to 3 bits of error since no two version info codewords will
// differ in less than 8 bits. // differ in less than 8 bits.
if (bestDifference <= 3) { if bestDifference <= 3 {
return getVersionForNumber(bestVersion); return Self::getVersionForNumber(bestVersion);
} }
// If we didn't find a close enough match, fail // 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 * See ISO 18004:2006 Annex E
*/ */
BitMatrix buildFunctionPattern() { pub fn buildFunctionPattern(&self) -> BitMatrix{
int dimension = getDimensionForVersion(); let dimension = self.getDimensionForVersion();
BitMatrix bitMatrix = new BitMatrix(dimension); let bitMatrix = BitMatrix::with_single_dimension(dimension);
// Top left finder pattern + separator + format // Top left finder pattern + separator + format
bitMatrix.setRegion(0, 0, 9, 9); bitMatrix.setRegion(0, 0, 9, 9);
@@ -145,12 +162,14 @@ impl Version {
bitMatrix.setRegion(0, dimension - 8, 9, 8); bitMatrix.setRegion(0, dimension - 8, 9, 8);
// Alignment patterns // Alignment patterns
int max = alignmentPatternCenters.length; let max = self.alignmentPatternCenters.len();
for (int x = 0; x < max; x++) { for x in 0..max {
int i = alignmentPatternCenters[x] - 2; // for (int x = 0; x < max; x++) {
for (int y = 0; y < max; y++) { let i = self.alignmentPatternCenters[x] - 2;
if ((x != 0 || (y != 0 && y != max - 1)) && (x != max - 1 || y != 0)) { for y in 0..max {
bitMatrix.setRegion(alignmentPatternCenters[y] - 2, i, 5, 5); // 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 // else no o alignment patterns near the three finder patterns
} }
@@ -161,100 +180,36 @@ impl Version {
// Horizontal timing pattern // Horizontal timing pattern
bitMatrix.setRegion(9, 6, dimension - 17, 1); bitMatrix.setRegion(9, 6, dimension - 17, 1);
if (versionNumber > 6) { if self.versionNumber > 6 {
// Version info, top right // Version info, top right
bitMatrix.setRegion(dimension - 11, 0, 3, 6); bitMatrix.setRegion(dimension - 11, 0, 3, 6);
// Version info, bottom left // Version info, bottom left
bitMatrix.setRegion(0, dimension - 11, 6, 3); bitMatrix.setRegion(0, dimension - 11, 6, 3);
} }
return bitMatrix; bitMatrix
}
/**
* <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>
*/
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;
}
}
/**
* <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>
*/
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);
} }
/** /**
* See ISO 18004:2006 6.5.1 Table 9 * See ISO 18004:2006 6.5.1 Table 9
*/ */
private static Version[] buildVersions() { pub const fn buildVersions() -> Vec<Version> {
return new Version[]{ [
new Version(1, new int[]{}, Version::new(1, [],Vec::from([
new ECBlocks(7, new ECB(1, 19)), ECBlocks::new(7, ECB::new(1, 19)),
new ECBlocks(10, new ECB(1, 16)), ECBlocks::new(10, ECB::new(1, 16)),
new ECBlocks(13, new ECB(1, 13)), ECBlocks::new(13, ECB::new(1, 13)),
new ECBlocks(17, new ECB(1, 9))), ECBlocks::new(17, ECB::new(1, 9))])),
new Version(2, new int[]{6, 18}, Version::new(2, [6, 18], Vec::from([
new ECBlocks(10, new ECB(1, 34)), ECBlocks::new(10, ECB::new(1, 34)),
new ECBlocks(16, new ECB(1, 28)), ECBlocks::new(16, ECB::new(1, 28)),
new ECBlocks(22, new ECB(1, 22)), ECBlocks::new(22, ECB::new(1, 22)),
new ECBlocks(28, new ECB(1, 16))), ECBlocks::new(28, ECB::new(1, 16))])),
new Version(3, new int[]{6, 22}, Version::new(3, [6, 22], Vec::from([
new ECBlocks(15, new ECB(1, 55)), ECBlocks::new(15, ECB::new(1, 55)),
new ECBlocks(26, new ECB(1, 44)), ECBlocks::new(26, ECB::new(1, 44)),
new ECBlocks(18, new ECB(2, 17)), ECBlocks::new(18, ECB::new(2, 17)),
new ECBlocks(22, new ECB(2, 13))), ECBlocks::new(22, ECB::new(2, 13))])),
new Version(4, new int[]{6, 26}, new Version(4, new int[]{6, 26},
new ECBlocks(20, new ECB(1, 80)), new ECBlocks(20, new ECB(1, 80)),
new ECBlocks(18, new ECB(2, 32)), new ECBlocks(18, new ECB(2, 32)),
@@ -568,7 +523,81 @@ impl Version {
new ECB(34, 25)), new ECB(34, 25)),
new ECBlocks(30, new ECB(20, 15), new ECBlocks(30, new ECB(20, 15),
new ECB(61, 16))) new ECB(61, 16)))
}; ]
}
} }
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>
*/
pub struct ECBlocks {
ecCodewordsPerBlock:u32,
ecBlocks:Vec<ECB>,
}
impl ECBlocks {
pub const fn new(ecCodewordsPerBlock:u32, ecBlocks:Vec<ECB>)-> 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
}
}
/**
* <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>
*/
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
}
} }