From 1b3535c0dfe0bbfc699ef90a29deca337ee72906 Mon Sep 17 00:00:00 2001 From: Henry Schimke Date: Mon, 19 Sep 2022 21:48:40 -0500 Subject: [PATCH] non passing aztec decoder port --- .../zxing/aztec/decoder/DecoderTest.java | 226 ------- src/aztec/AztecDetectorResult.java | 58 -- src/aztec/AztecDetectorResult.rs | 80 +++ src/aztec/DecoderTest.rs | 285 +++++++++ .../detector => src/aztec}/DetectorTest.java | 0 .../aztec/EncoderTest.rs | 223 ++++--- src/aztec/decoder.rs | 572 ++++++++++++++++++ src/aztec/decoder/Decoder.java | 443 -------------- .../{detector/Detector.java => detector.rs} | 65 +- src/aztec/mod.rs | 10 + src/aztec/shared_test_methods.rs | 32 + src/common/mod.rs | 28 +- src/lib.rs | 3 +- 13 files changed, 1131 insertions(+), 894 deletions(-) delete mode 100644 java_test/java/com/google/zxing/aztec/decoder/DecoderTest.java delete mode 100644 src/aztec/AztecDetectorResult.java create mode 100644 src/aztec/AztecDetectorResult.rs create mode 100644 src/aztec/DecoderTest.rs rename {java_test/java/com/google/zxing/aztec/detector => src/aztec}/DetectorTest.java (100%) rename java_test/java/com/google/zxing/aztec/encoder/EncoderTest.java => src/aztec/EncoderTest.rs (83%) create mode 100644 src/aztec/decoder.rs delete mode 100644 src/aztec/decoder/Decoder.java rename src/aztec/{detector/Detector.java => detector.rs} (94%) create mode 100644 src/aztec/mod.rs create mode 100644 src/aztec/shared_test_methods.rs diff --git a/java_test/java/com/google/zxing/aztec/decoder/DecoderTest.java b/java_test/java/com/google/zxing/aztec/decoder/DecoderTest.java deleted file mode 100644 index dc4681f..0000000 --- a/java_test/java/com/google/zxing/aztec/decoder/DecoderTest.java +++ /dev/null @@ -1,226 +0,0 @@ -/* - * Copyright 2014 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. - */ -package com.google.zxing.aztec.decoder; - -import com.google.zxing.aztec.encoder.EncoderTest; - -import com.google.zxing.FormatException; -import com.google.zxing.RXingResultPoint; -import com.google.zxing.aztec.AztecDetectorRXingResult; -import com.google.zxing.common.BitArray; -import com.google.zxing.common.BitMatrix; -import com.google.zxing.common.DecoderRXingResult; -import org.junit.Test; -import org.junit.Assert; - -/** - * Tests {@link Decoder}. - */ -public final class DecoderTest extends Assert { - - private static final RXingResultPoint[] NO_POINTS = new RXingResultPoint[0]; - - @Test - public void testHighLevelDecode() throws FormatException { - // no ECI codes - testHighLevelDecodeString("A. b.", - // 'A' P/S '. ' L/L b D/L '.' - "...X. ..... ...XX XXX.. ...XX XXXX. XX.X"); - - // initial ECI code 26 (switch to UTF-8) - testHighLevelDecodeString("Ça", - // P/S FLG(n) 2 '2' '6' B/S 2 0xc3 0x87 L/L 'a' - "..... ..... .X. .X.. X... XXXXX ...X. XX....XX X....XXX XXX.. ...X."); - - // initial character without ECI (must be interpreted as ISO_8859_1) - // followed by ECI code 26 (= UTF-8) and UTF-8 text - testHighLevelDecodeString("±Ça", - // B/S 1 0xb1 P/S FLG(n) 2 '2' '6' B/S 2 0xc3 0x87 L/L 'a' - "XXXXX ....X X.XX...X ..... ..... .X. .X.. X... XXXXX ...X. XX....XX X....XXX XXX.. ...X."); - - // GS1 data - testHighLevelDecodeString("101233742", - // P/S FLG(n) 0 D/L 1 0 1 2 3 P/S FLG(n) 0 3 7 4 2 - "..... ..... ... XXXX. ..XX ..X. ..XX .X.. .X.X .... ..... ... .X.X X..X .XX. .X.."); - } - - private static void testHighLevelDecodeString(String expectedString, String b) throws FormatException { - BitArray bits = EncoderTest.toBitArray(EncoderTest.stripSpace(b)); - assertEquals("highLevelDecode() failed for input bits: " + b, - expectedString, Decoder.highLevelDecode(EncoderTest.toBooleanArray(bits))); - } - - @Test - public void testAztecRXingResult() throws FormatException { - BitMatrix matrix = BitMatrix.parse( - "X X X X X X X X X X X X X X \n" + - "X X X X X X X X X X X X X X X \n" + - " X X X X X X X X X X X X \n" + - " X X X X X X X X X X \n" + - " X X X X X X X X \n" + - " X X X X X X X X X X X X X X X X X X \n" + - " X X X X X X X X X \n" + - " X X X X X X X X X X X X X X X X X \n" + - " X X X X X X X X X \n" + - " X X X X X X X X X X X X X X X X \n" + - " X X X X X X X X X X X X \n" + - " X X X X X X X X X X X \n" + - " X X X X X X X X X X X X \n" + - " X X X X X X X X X X X X X X X X X \n" + - "X X X X X X X X X X X \n" + - " X X X X X X X X X X X X X X \n" + - " X X X X X X X X \n" + - " X X X X X X X X X X X X X X X X X X X \n" + - "X X X X X X X X X \n" + - "X X X X X X X X X X X X X X X \n" + - "X X X X X X X X X X X X \n" + - "X X X X X X X X X X X X X X \n" + - " X X X X X X X X X X X X X \n", - "X ", " "); - AztecDetectorRXingResult r = new AztecDetectorRXingResult(matrix, NO_POINTS, false, 30, 2); - DecoderRXingResult result = new Decoder().decode(r); - assertEquals("88888TTTTTTTTTTTTTTTTTTTTTTTTTTTTTT", result.getText()); - assertArrayEquals( - new byte[] {-11, 85, 85, 117, 107, 90, -42, -75, -83, 107, - 90, -42, -75, -83, 107, 90, -42, -75, -83, 107, - 90, -42, -80}, - result.getRawBytes()); - assertEquals(180, result.getNumBits()); - } - - @Test - public void testAztecRXingResultECI() throws FormatException { - BitMatrix matrix = BitMatrix.parse( - " X X X X X X \n" + - " X X X X X X X X X X X X \n" + - " X X X X \n" + - " X X X X X X X X X X X X X X X X X \n" + - " X X \n" + - " X X X X X X X X X X X X \n" + - " X X X X X X X X \n" + - " X X X X X X X X X X X X \n" + - " X X X X X X X X \n" + - " X X X X X X X X X \n" + - "X X X X X X X X X \n" + - " X X X X X X X X X X X X \n" + - " X X X X X X \n" + - " X X X X X X X X X X X X X \n" + - " X X X \n" + - "X X X X X X X X X X X X X X X X X \n" + - "X X X X X X X X X \n" + - " X X X X X X X X X X X \n" + - "X X X X X X X X \n", - "X ", " "); - AztecDetectorRXingResult r = new AztecDetectorRXingResult(matrix, NO_POINTS, false, 15, 1); - DecoderRXingResult result = new Decoder().decode(r); - assertEquals("Français", result.getText()); - } - - @Test(expected = FormatException.class) - public void testDecodeTooManyErrors() throws FormatException { - BitMatrix matrix = BitMatrix.parse("" - + "X X . X . . . X X . . . X . . X X X . X . X X X X X . \n" - + "X X . . X X . . . . . X X . . . X X . . . X . X . . X \n" - + "X . . . X X . . X X X . X X . X X X X . X X . . X . . \n" - + ". . . . X . X X . . X X . X X . X . X X X X . X . . X \n" - + "X X X . . X X X X X . . . . . X X . . . X . X . X . X \n" - + "X X . . . . . . . . X . . . X . X X X . X . . X . . . \n" - + "X X . . X . . . . . X X . . . . . X . . . . X . . X X \n" - + ". . . X . X . X . . . . . X X X X X X . . . . . . X X \n" - + "X . . . X . X X X X X X . . X X X . X . X X X X X X . \n" - + "X . . X X X . X X X X X X X X X X X X X . . . X . X X \n" - + ". . . . X X . . . X . . . . . . . X X . . . X X . X . \n" - + ". . . X X X . . X X . X X X X X . X . . X . . . . . . \n" - + "X . . . . X . X . X . X . . . X . X . X X . X X . X X \n" - + "X . X . . X . X . X . X . X . X . X . . . . . X . X X \n" - + "X . X X X . . X . X . X . . . X . X . X X X . . . X X \n" - + "X X X X X X X X . X . X X X X X . X . X . X . X X X . \n" - + ". . . . . . . X . X . . . . . . . X X X X . . . X X X \n" - + "X X . . X . . X . X X X X X X X X X X X X X . . X . X \n" - + "X X X . X X X X . . X X X X . . X . . . . X . . X X X \n" - + ". . . . X . X X X . . . . X X X X . . X X X X . . . . \n" - + ". . X . . X . X . . . X . X X . X X . X . . . X . X . \n" - + "X X . . X . . X X X X X X X . . X . X X X X X X X . . \n" - + "X . X X . . X X . . . . . X . . . . . . X X . X X X . \n" - + "X . . X X . . X X . X . X . . . . X . X . . X . . X . \n" - + "X . X . X . . X . X X X X X X X X . X X X X . . X X . \n" - + "X X X X . . . X . . X X X . X X . . X . . . . X X X . \n" - + "X X . X . X . . . X . X . . . . X X . X . . X X . . . \n", - "X ", ". "); - AztecDetectorRXingResult r = new AztecDetectorRXingResult(matrix, NO_POINTS, true, 16, 4); - new Decoder().decode(r); - } - - @Test(expected = FormatException.class) - public void testDecodeTooManyErrors2() throws FormatException { - BitMatrix matrix = BitMatrix.parse("" - + ". X X . . X . X X . . . X . . X X X . . . X X . X X . \n" - + "X X . X X . . X . . . X X . . . X X . X X X . X . X X \n" - + ". . . . X . . . X X X . X X . X X X X . X X . . X . . \n" - + "X . X X . . X . . . X X . X X . X . X X . . . . . X . \n" - + "X X . X . . X . X X . . . . . X X . . . . . X . . . X \n" - + "X . . X . . . . . . X . . . X . X X X X X X X . . . X \n" - + "X . . X X . . X . . X X . . . . . X . . . . . X X X . \n" - + ". . X X X X . X . . . . . X X X X X X . . . . . . X X \n" - + "X . . . X . X X X X X X . . X X X . X . X X X X X X . \n" - + "X . . X X X . X X X X X X X X X X X X X . . . X . X X \n" - + ". . . . X X . . . X . . . . . . . X X . . . X X . X . \n" - + ". . . X X X . . X X . X X X X X . X . . X . . . . . . \n" - + "X . . . . X . X . X . X . . . X . X . X X . X X . X X \n" - + "X . X . . X . X . X . X . X . X . X . . . . . X . X X \n" - + "X . X X X . . X . X . X . . . X . X . X X X . . . X X \n" - + "X X X X X X X X . X . X X X X X . X . X . X . X X X . \n" - + ". . . . . . . X . X . . . . . . . X X X X . . . X X X \n" - + "X X . . X . . X . X X X X X X X X X X X X X . . X . X \n" - + "X X X . X X X X . . X X X X . . X . . . . X . . X X X \n" - + ". . X X X X X . X . . . . X X X X . . X X X . X . X . \n" - + ". . X X . X . X . . . X . X X . X X . . . . X X . . . \n" - + "X . . . X . X . X X X X X X . . X . X X X X X . X . . \n" - + ". X . . . X X X . . . . . X . . . . . X X X X X . X . \n" - + "X . . X . X X X X . X . X . . . . X . X X . X . . X . \n" - + "X . . . X X . X . X X X X X X X X . X X X X . . X X . \n" - + ". X X X X . . X . . X X X . X X . . X . . . . X X X . \n" - + "X X . . . X X . . X . X . . . . X X . X . . X . X . X \n", - "X ", ". "); - AztecDetectorRXingResult r = new AztecDetectorRXingResult(matrix, NO_POINTS, true, 16, 4); - new Decoder().decode(r); - } - - @Test - public void testRawBytes() { - boolean[] bool0 = {}; - boolean[] bool1 = { true }; - boolean[] bool7 = { true, false, true, false, true, false, true }; - boolean[] bool8 = { true, false, true, false, true, false, true, false }; - boolean[] bool9 = { true, false, true, false, true, false, true, false, - true }; - boolean[] bool16 = { false, true, true, false, false, false, true, true, - true, true, false, false, false, false, false, true }; - byte[] byte0 = {}; - byte[] byte1 = { -128 }; - byte[] byte7 = { -86 }; - byte[] byte8 = { -86 }; - byte[] byte9 = { -86, -128 }; - byte[] byte16 = { 99, -63 }; - - assertArrayEquals(byte0, Decoder.convertBoolArrayToByteArray(bool0)); - assertArrayEquals(byte1, Decoder.convertBoolArrayToByteArray(bool1)); - assertArrayEquals(byte7, Decoder.convertBoolArrayToByteArray(bool7)); - assertArrayEquals(byte8, Decoder.convertBoolArrayToByteArray(bool8)); - assertArrayEquals(byte9, Decoder.convertBoolArrayToByteArray(bool9)); - assertArrayEquals(byte16, Decoder.convertBoolArrayToByteArray(bool16)); - } -} diff --git a/src/aztec/AztecDetectorResult.java b/src/aztec/AztecDetectorResult.java deleted file mode 100644 index c1c427d..0000000 --- a/src/aztec/AztecDetectorResult.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2010 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. - */ - -package com.google.zxing.aztec; - -import com.google.zxing.RXingResultPoint; -import com.google.zxing.common.BitMatrix; -import com.google.zxing.common.DetectorRXingResult; - -/** - *

Extends {@link DetectorRXingResult} with more information specific to the Aztec format, - * like the number of layers and whether it's compact.

- * - * @author Sean Owen - */ -public final class AztecDetectorRXingResult extends DetectorRXingResult { - - private final boolean compact; - private final int nbDatablocks; - private final int nbLayers; - - public AztecDetectorRXingResult(BitMatrix bits, - RXingResultPoint[] points, - boolean compact, - int nbDatablocks, - int nbLayers) { - super(bits, points); - this.compact = compact; - this.nbDatablocks = nbDatablocks; - this.nbLayers = nbLayers; - } - - public int getNbLayers() { - return nbLayers; - } - - public int getNbDatablocks() { - return nbDatablocks; - } - - public boolean isCompact() { - return compact; - } - -} diff --git a/src/aztec/AztecDetectorResult.rs b/src/aztec/AztecDetectorResult.rs new file mode 100644 index 0000000..3aff5f7 --- /dev/null +++ b/src/aztec/AztecDetectorResult.rs @@ -0,0 +1,80 @@ +/* + * Copyright 2010 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. + */ + +// package com.google.zxing.aztec; + +// import com.google.zxing.RXingResultPoint; +// import com.google.zxing.common.BitMatrix; +// import com.google.zxing.common.DetectorRXingResult; + +use crate::{ + common::{BitMatrix, DetectorRXingResult}, + RXingResultPoint, +}; + +/** + *

Extends {@link DetectorRXingResult} with more information specific to the Aztec format, + * like the number of layers and whether it's compact.

+ * + * @author Sean Owen + */ +pub struct AztecDetectorRXingResult { + bits: BitMatrix, + points: Vec, + compact: bool, + nbDatablocks: u32, + nbLayers: u32, +} + +impl DetectorRXingResult for AztecDetectorRXingResult { + fn getBits(&self) -> &BitMatrix { + &self.bits + } + + fn getPoints(&self) -> &Vec { + &self.points + } +} + +impl AztecDetectorRXingResult { + pub fn new( + bits: BitMatrix, + points: Vec, + compact: bool, + nbDatablocks: u32, + nbLayers: u32, + ) -> Self { + Self { + bits, + points, + compact, + nbDatablocks, + nbLayers, + } + } + + pub fn getNbLayers(&self) -> u32 { + self.nbLayers + } + + pub fn getNbDatablocks(&self) -> u32 { + self.nbDatablocks + } + + pub fn isCompact(&self) -> bool { + self.compact + } +} diff --git a/src/aztec/DecoderTest.rs b/src/aztec/DecoderTest.rs new file mode 100644 index 0000000..1542099 --- /dev/null +++ b/src/aztec/DecoderTest.rs @@ -0,0 +1,285 @@ +/* + * Copyright 2014 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. + */ +// package com.google.zxing.aztec.decoder; + +// import com.google.zxing.aztec.encoder.EncoderTest; + +// import com.google.zxing.FormatException; +// import com.google.zxing.RXingResultPoint; +// import com.google.zxing.aztec.AztecDetectorRXingResult; +// import com.google.zxing.common.BitArray; +// import com.google.zxing.common.BitMatrix; +// import com.google.zxing.common.DecoderRXingResult; +// import org.junit.Test; +// import org.junit.Assert; + +use crate::{ + aztec::shared_test_methods::{stripSpace, toBitArray, toBooleanArray}, + common::BitMatrix, + RXingResultPoint, +}; + +use super::{decoder, AztecDetectorResult::AztecDetectorRXingResult}; + +/** + * Tests {@link Decoder}. + */ + +const NO_POINTS: &[RXingResultPoint] = &[RXingResultPoint { x: 0.0, y: 0.0 }; 0]; + +#[test] +fn testHighLevelDecode() { + // no ECI codes + testHighLevelDecodeString( + "A. b.", + // 'A' P/S '. ' L/L b D/L '.' + "...X. ..... ...XX XXX.. ...XX XXXX. XX.X", + ); + + // initial ECI code 26 (switch to UTF-8) + testHighLevelDecodeString( + "Ça", + // P/S FLG(n) 2 '2' '6' B/S 2 0xc3 0x87 L/L 'a' + "..... ..... .X. .X.. X... XXXXX ...X. XX....XX X....XXX XXX.. ...X.", + ); + + // initial character without ECI (must be interpreted as ISO_8859_1) + // followed by ECI code 26 (= UTF-8) and UTF-8 text + testHighLevelDecodeString( + "±Ça", + // B/S 1 0xb1 P/S FLG(n) 2 '2' '6' B/S 2 0xc3 0x87 L/L 'a' + "XXXXX ....X X.XX...X ..... ..... .X. .X.. X... XXXXX ...X. XX....XX X....XXX XXX.. ...X.", + ); + + // GS1 data + testHighLevelDecodeString( + "101233742", + // P/S FLG(n) 0 D/L 1 0 1 2 3 P/S FLG(n) 0 3 7 4 2 + "..... ..... ... XXXX. ..XX ..X. ..XX .X.. .X.X .... ..... ... .X.X X..X .XX. .X..", + ); +} + +fn testHighLevelDecodeString(expectedString: &str, b: &str) { + let bits = toBitArray(&stripSpace(b)); + assert_eq!( + expectedString, + decoder::highLevelDecode(&toBooleanArray(&bits)).expect("highLevelDecode Failed"), + "highLevelDecode() failed for input bits: {}", + b + ); +} + +#[test] +fn testAztecRXingResult() { + let matrix = BitMatrix::parse_strings( + r"X X X X X X X X X X X X X X +X X X X X X X X X X X X X X X + X X X X X X X X X X X X + X X X X X X X X X X + X X X X X X X X + X X X X X X X X X X X X X X X X X X + X X X X X X X X X + X X X X X X X X X X X X X X X X X + X X X X X X X X X + X X X X X X X X X X X X X X X X + X X X X X X X X X X X X + X X X X X X X X X X X + X X X X X X X X X X X X + X X X X X X X X X X X X X X X X X +X X X X X X X X X X X + X X X X X X X X X X X X X X + X X X X X X X X + X X X X X X X X X X X X X X X X X X X +X X X X X X X X X +X X X X X X X X X X X X X X X +X X X X X X X X X X X X +X X X X X X X X X X X X X X + X X X X X X X X X X X X X +", + "X ", + " ", + ) + .expect("Bitmatrix should init"); + let r = AztecDetectorRXingResult::new(matrix, NO_POINTS.to_vec(), false, 30, 2); + let result = decoder::decode(&r).expect("decoder should init"); + assert_eq!("88888TTTTTTTTTTTTTTTTTTTTTTTTTTTTTT", result.getText()); + assert_eq!( + &vec![ + -11i8 as u8, + 85, + 85, + 117, + 107, + 90, + -42i8 as u8, + -75i8 as u8, + -83i8 as u8, + 107, + 90, + -42i8 as u8, + -75i8 as u8, + -83i8 as u8, + 107, + 90, + -42i8 as u8, + -75i8 as u8, + -83i8 as u8, + 107, + 90, + -42i8 as u8, + -80i8 as u8 + ], + result.getRawBytes() + ); + assert_eq!(180, result.getNumBits()); +} + +#[test] +fn testAztecRXingResultECI() { + let matrix = BitMatrix::parse_strings( + r" X X X X X X + X X X X X X X X X X X X + X X X X + X X X X X X X X X X X X X X X X X + X X + X X X X X X X X X X X X + X X X X X X X X + X X X X X X X X X X X X + X X X X X X X X + X X X X X X X X X +X X X X X X X X X + X X X X X X X X X X X X + X X X X X X + X X X X X X X X X X X X X + X X X +X X X X X X X X X X X X X X X X X +X X X X X X X X X + X X X X X X X X X X X +X X X X X X X X ", + "X ", + " ", + ) + .expect("string parse success"); + let r = AztecDetectorRXingResult::new(matrix, NO_POINTS.to_vec(), false, 15, 1); + let result = decoder::decode(&r).expect("decode success"); + assert_eq!("Français", result.getText()); +} + +#[test] +#[should_panic] +fn testDecodeTooManyErrors() { + let matrix = BitMatrix::parse_strings( + r" + X X . X . . . X X . . . X . . X X X . X . X X X X X . + X X . . X X . . . . . X X . . . X X . . . X . X . . X + X . . . X X . . X X X . X X . X X X X . X X . . X . . + . . . . X . X X . . X X . X X . X . X X X X . X . . X + X X X . . X X X X X . . . . . X X . . . X . X . X . X + X X . . . . . . . . X . . . X . X X X . X . . X . . . + X X . . X . . . . . X X . . . . . X . . . . X . . X X \ + . . . X . X . X . . . . . X X X X X X . . . . . . X X + X . . . X . X X X X X X . . X X X . X . X X X X X X . + X . . X X X . X X X X X X X X X X X X X . . . X . X X + . . . . X X . . . X . . . . . . . X X . . . X X . X . + . . . X X X . . X X . X X X X X . X . . X . . . . . . + X . . . . X . X . X . X . . . X . X . X X . X X . X X + X . X . . X . X . X . X . X . X . X . . . . . X . X X + X . X X X . . X . X . X . . . X . X . X X X . . . X X + X X X X X X X X . X . X X X X X . X . X . X . X X X . + . . . . . . . X . X . . . . . . . X X X X . . . X X X + X X . . X . . X . X X X X X X X X X X X X X . . X . X + X X X . X X X X . . X X X X . . X . . . . X . . X X X + . . . . X . X X X . . . . X X X X . . X X X X . . . . + . . X . . X . X . . . X . X X . X X . X . . . X . X . + X X . . X . . X X X X X X X . . X . X X X X X X X . . + X . X X . . X X . . . . . X . . . . . . X X . X X X . + X . . X X . . X X . X . X . . . . X . X . . X . . X . + X . X . X . . X . X X X X X X X X . X X X X . . X X . + X X X X . . . X . . X X X . X X . . X . . . . X X X . + X X . X . X . . . X . X . . . . X X . X . . X X . . . ", + "X ", + ". ", + ) + .expect("parse string success"); + let r = AztecDetectorRXingResult::new(matrix, NO_POINTS.to_vec(), true, 16, 4); + decoder::decode(&r); +} + +#[test] +#[should_panic] +fn testDecodeTooManyErrors2() { + let matrix = BitMatrix::parse_strings( + r" + . X X . . X . X X . . . X . . X X X . . . X X . X X . + X X . X X . . X . . . X X . . . X X . X X X . X . X X + . . . . X . . . X X X . X X . X X X X . X X . . X . . + X . X X . . X . . . X X . X X . X . X X . . . . . X . + X X . X . . X . X X . . . . . X X . . . . . X . . . X + X . . X . . . . . . X . . . X . X X X X X X X . . . X + X . . X X . . X . . X X . . . . . X . . . . . X X X . + . . X X X X . X . . . . . X X X X X X . . . . . . X X + X . . . X . X X X X X X . . X X X . X . X X X X X X . + X . . X X X . X X X X X X X X X X X X X . . . X . X X + . . . . X X . . . X . . . . . . . X X . . . X X . X . + . . . X X X . . X X . X X X X X . X . . X . . . . . . + X . . . . X . X . X . X . . . X . X . X X . X X . X X + X . X . . X . X . X . X . X . X . X . . . . . X . X X + X . X X X . . X . X . X . . . X . X . X X X . . . X X + X X X X X X X X . X . X X X X X . X . X . X . X X X . + . . . . . . . X . X . . . . . . . X X X X . . . X X X + X X . . X . . X . X X X X X X X X X X X X X . . X . X + X X X . X X X X . . X X X X . . X . . . . X . . X X X + . . X X X X X . X . . . . X X X X . . X X X . X . X . + . . X X . X . X . . . X . X X . X X . . . . X X . . . + X . . . X . X . X X X X X X . . X . X X X X X . X . . + . X . . . X X X . . . . . X . . . . . X X X X X . X . + X . . X . X X X X . X . X . . . . X . X X . X . . X . + X . . . X X . X . X X X X X X X X . X X X X . . X X . + . X X X X . . X . . X X X . X X . . X . . . . X X X . + X X . . . X X . . X . X . . . . X X . X . . X . X . X ", + "X ", + ". ", + ) + .expect("String Parse OK"); + let r = AztecDetectorRXingResult::new(matrix, NO_POINTS.to_vec(), true, 16, 4); + decoder::decode(&r); +} + +#[test] +fn testRawBytes() { + let bool0 = vec![false; 0]; + let bool1 = vec![true]; + let bool7 = vec![true, false, true, false, true, false, true]; + let bool8 = vec![true, false, true, false, true, false, true, false]; + let bool9 = vec![true, false, true, false, true, false, true, false, true]; + let bool16 = vec![ + false, true, true, false, false, false, true, true, true, true, false, false, false, false, + false, true, + ]; + let byte0 = vec![0u8; 0]; + let byte1 = vec![-128i8 as u8]; + let byte7 = vec![-86i8 as u8]; + let byte8 = vec![-86i8 as u8]; + let byte9 = vec![-86i8 as u8, -128i8 as u8]; + let byte16 = vec![99, -63i8 as u8]; + + assert_eq!(byte0, decoder::convertBoolArrayToByteArray(&bool0)); + assert_eq!(byte1, decoder::convertBoolArrayToByteArray(&bool1)); + assert_eq!(byte7, decoder::convertBoolArrayToByteArray(&bool7)); + assert_eq!(byte8, decoder::convertBoolArrayToByteArray(&bool8)); + assert_eq!(byte9, decoder::convertBoolArrayToByteArray(&bool9)); + assert_eq!(byte16, decoder::convertBoolArrayToByteArray(&bool16)); +} diff --git a/java_test/java/com/google/zxing/aztec/detector/DetectorTest.java b/src/aztec/DetectorTest.java similarity index 100% rename from java_test/java/com/google/zxing/aztec/detector/DetectorTest.java rename to src/aztec/DetectorTest.java diff --git a/java_test/java/com/google/zxing/aztec/encoder/EncoderTest.java b/src/aztec/EncoderTest.rs similarity index 83% rename from java_test/java/com/google/zxing/aztec/encoder/EncoderTest.java rename to src/aztec/EncoderTest.rs index 5952f0a..b269af4 100644 --- a/java_test/java/com/google/zxing/aztec/encoder/EncoderTest.java +++ b/src/aztec/EncoderTest.rs @@ -14,27 +14,29 @@ * limitations under the License. */ -package com.google.zxing.aztec.encoder; +// package com.google.zxing.aztec.encoder; -import com.google.zxing.BarcodeFormat; -import com.google.zxing.EncodeHintType; -import com.google.zxing.FormatException; -import com.google.zxing.RXingResultPoint; -import com.google.zxing.aztec.AztecDetectorRXingResult; -import com.google.zxing.aztec.AztecWriter; -import com.google.zxing.aztec.decoder.Decoder; -import com.google.zxing.common.BitArray; -import com.google.zxing.common.BitMatrix; -import com.google.zxing.common.DecoderRXingResult; -import org.junit.Assert; -import org.junit.Test; +// import com.google.zxing.BarcodeFormat; +// import com.google.zxing.EncodeHintType; +// import com.google.zxing.FormatException; +// import com.google.zxing.RXingResultPoint; +// import com.google.zxing.aztec.AztecDetectorRXingResult; +// import com.google.zxing.aztec.AztecWriter; +// import com.google.zxing.aztec.decoder.Decoder; +// import com.google.zxing.common.BitArray; +// import com.google.zxing.common.BitMatrix; +// import com.google.zxing.common.DecoderRXingResult; +// import org.junit.Assert; +// import org.junit.Test; -import java.nio.charset.Charset; -import java.nio.charset.StandardCharsets; -import java.util.EnumMap; -import java.util.Map; -import java.util.Random; -import java.util.regex.Pattern; +// import java.nio.charset.Charset; +// import java.nio.charset.StandardCharsets; +// import java.util.EnumMap; +// import java.util.Map; +// import java.util.Random; +// import java.util.regex.Pattern; + +use crate::common::BitArray; /** * Aztec 2D generator unit tests. @@ -42,22 +44,21 @@ import java.util.regex.Pattern; * @author Rustam Abdullaev * @author Frank Yellin */ -public final class EncoderTest extends Assert { - private static final Charset ISO_8859_1 = StandardCharsets.ISO_8859_1; - private static final Charset UTF_8 = StandardCharsets.UTF_8; - private static final Charset SHIFT_JIS = Charset.forName("Shift_JIS"); - private static final Charset ISO_8859_15 = Charset.forName("ISO-8859-15"); - private static final Charset WINDOWS_1252 = Charset.forName("Windows-1252"); + const ISO_8859_1:&'static encoding::Encoding = StandardCharsets.ISO_8859_1; + const UTF_8 :&'static encoding::Encoding= StandardCharsets.UTF_8; + const SHIFT_JIS:&'static encoding::Encoding = Charset.forName("Shift_JIS"); + const ISO_8859_15:&'static encoding::Encoding = Charset.forName("ISO-8859-15"); + const WINDOWS_1252 :&'static encoding::Encoding= Charset.forName("Windows-1252"); - private static final Pattern DOTX = Pattern.compile("[^.X]"); - private static final Pattern SPACES = Pattern.compile("\\s+"); - private static final RXingResultPoint[] NO_POINTS = new RXingResultPoint[0]; + const DOTX : &str = "[^.X]"; + const SPACES :&str= "\\s+"; + const NO_POINTS:Vec = Vec::new(); // real life tests - @Test - public void testEncode1() { + #[test] + fn testEncode1() { testEncode("This is an example Aztec symbol for Wikipedia.", true, 3, "X X X X X X X X \n" + "X X X X X X X X X X \n" + @@ -84,8 +85,8 @@ public final class EncoderTest extends Assert { " X X X X X X X X X X \n"); } - @Test - public void testEncode2() { + #[test] + fn testEncode2() { testEncode("Aztec Code is a public domain 2D matrix barcode symbology" + " of nominally square symbols built on a square grid with a " + "distinctive square bullseye pattern at their center.", false, 6, @@ -132,57 +133,57 @@ public final class EncoderTest extends Assert { "X X X X X X X X X X X X X \n"); } - @Test - public void testAztecWriter() throws Exception { - testWriter("Espa\u00F1ol", null, 25, true, 1); // Without ECI (implicit ISO-8859-1) - testWriter("Espa\u00F1ol", ISO_8859_1, 25, true, 1); // Explicit ISO-8859-1 - testWriter("\u20AC 1 sample data.", WINDOWS_1252, 25, true, 2); // ISO-8859-1 can't encode Euro; Windows-1252 can - testWriter("\u20AC 1 sample data.", ISO_8859_15, 25, true, 2); - testWriter("\u20AC 1 sample data.", UTF_8, 25, true, 2); - testWriter("\u20AC 1 sample data.", UTF_8, 100, true, 3); - testWriter("\u20AC 1 sample data.", UTF_8, 300, true, 4); - testWriter("\u20AC 1 sample data.", UTF_8, 500, false, 5); - testWriter("The capital of Japan is named \u6771\u4EAC.", SHIFT_JIS, 25, true, 3); + + fn testAztecWriter() { + testWriter("Espa\u{00F1}ol", null, 25, true, 1); // Without ECI (implicit ISO-8859-1) + testWriter("Espa\u{00F1}ol", ISO_8859_1, 25, true, 1); // Explicit ISO-8859-1 + testWriter("\u{20AC} 1 sample data.", WINDOWS_1252, 25, true, 2); // ISO-8859-1 can't encode Euro; Windows-1252 can + testWriter("\u{20AC} 1 sample data.", ISO_8859_15, 25, true, 2); + testWriter("\u{20AC} 1 sample data.", UTF_8, 25, true, 2); + testWriter("\u{20AC} 1 sample data.", UTF_8, 100, true, 3); + testWriter("\u{20AC} 1 sample data.", UTF_8, 300, true, 4); + testWriter("\u{20AC} 1 sample data.", UTF_8, 500, false, 5); + testWriter("The capital of Japan is named \u{6771}\u{4EAC}.", SHIFT_JIS, 25, true, 3); // Test AztecWriter defaults - String data = "In ut magna vel mauris malesuada"; - AztecWriter writer = new AztecWriter(); - BitMatrix matrix = writer.encode(data, BarcodeFormat.AZTEC, 0, 0); - AztecCode aztec = Encoder.encode(data, + let data = "In ut magna vel mauris malesuada"; + let writer = AztecWriter::new(); + let matrix = writer.encode(data, BarcodeFormat.AZTEC, 0, 0); + let aztec = Encoder.encode(data, Encoder.DEFAULT_EC_PERCENT, Encoder.DEFAULT_AZTEC_LAYERS); - BitMatrix expectedMatrix = aztec.getMatrix(); + let expectedMatrix = aztec.getMatrix(); assertEquals(matrix, expectedMatrix); } // synthetic tests (encode-decode round-trip) - @Test - public void testEncodeDecode1() throws Exception { + #[test] + fn testEncodeDecode1() { testEncodeDecode("Abc123!", true, 1); } - @Test - public void testEncodeDecode2() throws Exception { + #[test] + fn testEncodeDecode2() { testEncodeDecode("Lorem ipsum. http://test/", true, 2); } - @Test - public void testEncodeDecode3() throws Exception { + #[test] + fn testEncodeDecode3() { testEncodeDecode("AAAANAAAANAAAANAAAANAAAANAAAANAAAANAAAANAAAANAAAAN", true, 3); } - @Test - public void testEncodeDecode4() throws Exception { + #[test] + fn testEncodeDecode4() { testEncodeDecode("http://test/~!@#*^%&)__ ;:'\"[]{}\\|-+-=`1029384", true, 4); } - @Test - public void testEncodeDecode5() throws Exception { + #[test] + fn testEncodeDecode5() { testEncodeDecode("http://test/~!@#*^%&)__ ;:'\"[]{}\\|-+-=`1029384756<>/?abc" + "Four score and seven our forefathers brought forth", false, 5); } - @Test - public void testEncodeDecode10() throws Exception { + #[test] + fn testEncodeDecode10() { testEncodeDecode("In ut magna vel mauris malesuada dictum. Nulla ullamcorper metus quis diam" + " cursus facilisis. Sed mollis quam id justo rutrum sagittis. Donec laoreet rutrum" + " est, nec convallis mauris condimentum sit amet. Phasellus gravida, justo et congue" + @@ -191,8 +192,8 @@ public final class EncoderTest extends Assert { " elementum sapien dolor et diam.", false, 10); } - @Test - public void testEncodeDecode23() throws Exception { + #[test] + fn testEncodeDecode23() { testEncodeDecode("In ut magna vel mauris malesuada dictum. Nulla ullamcorper metus quis diam" + " cursus facilisis. Sed mollis quam id justo rutrum sagittis. Donec laoreet rutrum" + " est, nec convallis mauris condimentum sit amet. Phasellus gravida, justo et congue" + @@ -217,8 +218,8 @@ public final class EncoderTest extends Assert { " erat pulvinar nisi, id elementum sapien dolor et diam.", false, 23); } - @Test - public void testEncodeDecode31() throws Exception { + #[test] + fn testEncodeDecode31() { testEncodeDecode("In ut magna vel mauris malesuada dictum. Nulla ullamcorper metus quis diam" + " cursus facilisis. Sed mollis quam id justo rutrum sagittis. Donec laoreet rutrum" + " est, nec convallis mauris condimentum sit amet. Phasellus gravida, justo et congue" + @@ -258,16 +259,16 @@ public final class EncoderTest extends Assert { " hendrerit felis turpis nec lorem.", false, 31); } - @Test - public void testGenerateModeMessage() { + #[test] + fn testGenerateModeMessage() { testModeMessage(true, 2, 29, ".X .XXX.. ...X XX.. ..X .XX. .XX.X"); testModeMessage(true, 4, 64, "XX XXXXXX .X.. ...X ..XX .X.. XX.."); testModeMessage(false, 21, 660, "X.X.. .X.X..X..XX .XXX ..X.. .XXX. .X... ..XXX"); testModeMessage(false, 32, 4096, "XXXXX XXXXXXXXXXX X.X. ..... XXX.X ..X.. X.XXX"); } - @Test - public void testStuffBits() { + #[test] + fn testStuffBits() { testStuffBits(5, ".X.X. X.X.X .X.X.", ".X.X. X.X.X .X.X."); testStuffBits(5, ".X.X. ..... .X.X", @@ -285,8 +286,8 @@ public final class EncoderTest extends Assert { ".....X ...XXX XX..XX ..X... ..X.X. X..... X.X... ....X. X..... X....X X..X.. .....X X.X..X XXX.XX .XXXXX"); } - @Test - public void testHighLevelEncode() throws FormatException { + #[test] + fn testHighLevelEncode() { testHighLevelEncodeString("A. b.", // 'A' P/S '. ' L/L b D/L '.' "...X. ..... ...XX XXX.. ...XX XXXX. XX.X"); @@ -315,8 +316,8 @@ public final class EncoderTest extends Assert { 823); } - @Test - public void testHighLevelEncodeBinary() throws FormatException { + #[test] + fn testHighLevelEncodeBinary() { // binary short form single byte testHighLevelEncodeString("N\0N", // 'N' B/S =1 '\0' N @@ -327,12 +328,12 @@ public final class EncoderTest extends Assert { ".XXXX XXXXX ...X. ........ .XX.XXX."); // Encode "n" in BINARY // binary short form consecutive bytes - testHighLevelEncodeString("N\0\u0080 A", + testHighLevelEncodeString("N\0\u{0080} A", // 'N' B/S =2 '\0' \u0080 ' ' 'A' ".XXXX XXXXX ...X. ........ X....... ....X ...X."); // binary skipping over single character - testHighLevelEncodeString("\0a\u00FF\u0080 A", + testHighLevelEncodeString("\0a\u{00FF}\u{0080} A", // B/S =4 '\0' 'a' '\3ff' '\200' ' ' 'A' "XXXXX ..X.. ........ .XX....X XXXXXXXX X....... ....X ...X."); @@ -343,7 +344,7 @@ public final class EncoderTest extends Assert { ); // Create a string in which every character requires binary - StringBuilder sb = new StringBuilder(); + let sb = String::new(); for (int i = 0; i <= 3000; i++) { sb.append((char) (128 + (i % 30))); } @@ -402,8 +403,8 @@ public final class EncoderTest extends Assert { testHighLevelEncodeString(sb.toString(), 21 + 64 * 8); } - @Test - public void testHighLevelEncodePairs() throws FormatException { + #[test] + fn testHighLevelEncodePairs() { // Typical usage testHighLevelEncodeString("ABC. DEF\r\n", // A B C P/S . D E F P/S \r\n @@ -424,17 +425,19 @@ public final class EncoderTest extends Assert { "...X. XXXXX ..X.. X....... ..X.XXX. ..X..... X......."); } - @Test(expected = IllegalArgumentException.class) - public void testUserSpecifiedLayers() { + #[test] + #[should_panic] + fn testUserSpecifiedLayers() { doTestUserSpecifiedLayers(33); } - @Test(expected = IllegalArgumentException.class) - public void testUserSpecifiedLayers2() { + #[test] + #[should_panic] + fn testUserSpecifiedLayers2() { doTestUserSpecifiedLayers(-1); } - private void doTestUserSpecifiedLayers(int userSpecifiedLayers) { + fn doTestUserSpecifiedLayers( userSpecifiedLayers:usize) { String alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; AztecCode aztec = Encoder.encode(alphabet, 25, -2); assertEquals(2, aztec.getLayers()); @@ -447,8 +450,9 @@ public final class EncoderTest extends Assert { Encoder.encode(alphabet, 25, userSpecifiedLayers); } - @Test(expected = IllegalArgumentException.class) - public void testBorderCompact4CaseFailed() { + #[test] + #[should_panic] + fn testBorderCompact4CaseFailed() { // Compact(4) con hold 608 bits of information, but at most 504 can be data. Rest must // be error correction String alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; @@ -457,8 +461,8 @@ public final class EncoderTest extends Assert { Encoder.encode(alphabet4, 0, -4); } - @Test - public void testBorderCompact4Case() { + #[test] + fn testBorderCompact4Case() { // Compact(4) con hold 608 bits of information, but at most 504 can be data. Rest must // be error correction String alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; @@ -479,7 +483,7 @@ public final class EncoderTest extends Assert { // Helper routines - private static void testEncode(String data, boolean compact, int layers, String expected) { + fn testEncode( data:&str, compact:bool, layers:usize, expected:&str) { AztecCode aztec = Encoder.encode(data, 33, Encoder.DEFAULT_AZTEC_LAYERS); assertEquals("Unexpected symbol format (compact)", compact, aztec.isCompact()); assertEquals("Unexpected nr. of layers", layers, aztec.getLayers()); @@ -487,7 +491,7 @@ public final class EncoderTest extends Assert { assertEquals("encode() failed", expected, matrix.toString()); } - private static void testEncodeDecode(String data, boolean compact, int layers) throws Exception { + fn testEncodeDecode( data:&str, compact:bool, layers:usize) { AztecCode aztec = Encoder.encode(data, 25, Encoder.DEFAULT_AZTEC_LAYERS); assertEquals("Unexpected symbol format (compact)", compact, aztec.isCompact()); assertEquals("Unexpected nr. of layers", layers, aztec.getLayers()); @@ -507,11 +511,11 @@ public final class EncoderTest extends Assert { assertEquals(data, res.getText()); } - private static void testWriter(String data, - Charset charset, - int eccPercent, - boolean compact, - int layers) throws FormatException { + fn testWriter( data:&str, + charset:&'static dyn encoding::Encoding, + eccPercent:u32, + compact:bool, + layers:usize) throws FormatException { // Perform an encode-decode round-trip because it can be lossy. Map hints = new EnumMap<>(EncodeHintType.class); if (null != charset) { @@ -548,56 +552,35 @@ public final class EncoderTest extends Assert { assertEquals(data, res.getText()); } - private static Random getPseudoRandom() { + fn getPseudoRandom() -> rand::Rng{ return new Random(0xDEADBEEF); } - private static void testModeMessage(boolean compact, int layers, int words, String expected) { + fn testModeMessage( compact:bool, layers:usize, words:usize, expected:&str) { BitArray in = Encoder.generateModeMessage(compact, layers, words); assertEquals("generateModeMessage() failed", stripSpace(expected), stripSpace(in.toString())); } - private static void testStuffBits(int wordSize, String bits, String expected) { + fn testStuffBits( wordSize:usize, bits:&str, expected:&str) { BitArray in = toBitArray(bits); BitArray stuffed = Encoder.stuffBits(in, wordSize); assertEquals("stuffBits() failed for input string: " + bits, stripSpace(expected), stripSpace(stuffed.toString())); } - public static BitArray toBitArray(CharSequence bits) { - BitArray in = new BitArray(); - char[] str = DOTX.matcher(bits).replaceAll("").toCharArray(); - for (char aStr : str) { - in.appendBit(aStr == 'X'); - } - return in; - } + - public static boolean[] toBooleanArray(BitArray bitArray) { - boolean[] result = new boolean[bitArray.getSize()]; - for (int i = 0; i < result.length; i++) { - result[i] = bitArray.get(i); - } - return result; - } - - private static void testHighLevelEncodeString(String s, String expectedBits) throws FormatException { + fn testHighLevelEncodeString( s:&str, expectedBits:&str) { BitArray bits = new HighLevelEncoder(s.getBytes(StandardCharsets.ISO_8859_1)).encode(); String receivedBits = stripSpace(bits.toString()); assertEquals("highLevelEncode() failed for input string: " + s, stripSpace(expectedBits), receivedBits); assertEquals(s, Decoder.highLevelDecode(toBooleanArray(bits))); } - private static void testHighLevelEncodeString(String s, int expectedReceivedBits) throws FormatException { + fn testHighLevelEncodeString( s:&str, expectedReceivedBits:u32) { BitArray bits = new HighLevelEncoder(s.getBytes(StandardCharsets.ISO_8859_1)).encode(); int receivedBitCount = stripSpace(bits.toString()).length(); assertEquals("highLevelEncode() failed for input string: " + s, expectedReceivedBits, receivedBitCount); assertEquals(s, Decoder.highLevelDecode(toBooleanArray(bits))); } - - public static String stripSpace(String s) { - return SPACES.matcher(s).replaceAll(""); - } - -} diff --git a/src/aztec/decoder.rs b/src/aztec/decoder.rs new file mode 100644 index 0000000..95f1367 --- /dev/null +++ b/src/aztec/decoder.rs @@ -0,0 +1,572 @@ +/* + * Copyright 2010 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. + */ + +// package com.google.zxing.aztec.decoder; + +// import com.google.zxing.FormatException; +// import com.google.zxing.aztec.AztecDetectorRXingResult; +// import com.google.zxing.common.BitMatrix; +// import com.google.zxing.common.CharacterSetECI; +// import com.google.zxing.common.DecoderRXingResult; +// import com.google.zxing.common.reedsolomon.GenericGF; +// import com.google.zxing.common.reedsolomon.ReedSolomonDecoder; +// import com.google.zxing.common.reedsolomon.ReedSolomonException; + +// import java.io.ByteArrayOutputStream; +// import java.io.UnsupportedEncodingException; +// import java.nio.charset.Charset; +// import java.nio.charset.StandardCharsets; +// import java.util.Arrays; + +use encoding::Encoding; + +use crate::{ + common::{ + self, + reedsolomon::{ + get_predefined_genericgf, GenericGF, PredefinedGenericGF, ReedSolomonDecoder, + }, + BitMatrix, CharacterSetECI, DecoderRXingResult, DetectorRXingResult, + }, + exceptions::Exceptions, +}; + +use super::AztecDetectorResult::AztecDetectorRXingResult; + +/** + *

The main class which implements Aztec Code decoding -- as opposed to locating and extracting + * the Aztec Code from an image.

+ * + * @author David Olivier + */ + +#[derive(PartialEq, Eq,Copy,Clone)] +enum Table { + UPPER, + LOWER, + MIXED, + DIGIT, + PUNCT, + BINARY, +} + +const UPPER_TABLE: [&str; 32] = [ + "CTRL_PS", " ", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", + "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "CTRL_LL", "CTRL_ML", "CTRL_DL", "CTRL_BS", +]; + +const LOWER_TABLE: [&str; 32] = [ + "CTRL_PS", " ", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", + "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "CTRL_US", "CTRL_ML", "CTRL_DL", "CTRL_BS", +]; + +const MIXED_TABLE: [&str; 32] = [ + "CTRL_PS", " ", "\u{1}", "\u{2}", "\u{3}", "\u{4}", "\u{5}", "\u{6}", "\u{7}", "\u{8}", "\t", + "\n", "\u{13}", "\u{0c}", "\r", "\u{33}", "\u{34}", "\u{35}", "\u{36}", "\u{37}", "@", "\\", + "^", "_", "`", "|", "~", "\u{177}", "CTRL_LL", "CTRL_UL", "CTRL_PL", "CTRL_BS", +]; + +const PUNCT_TABLE: [&str; 32] = [ + "FLG(n)", "\r", "\r\n", ". ", ", ", ": ", "!", "\"", "#", "$", "%", "&", "'", "(", ")", "*", + "+", ",", "-", ".", "/", ":", ";", "<", "=", ">", "?", "[", "]", "{", "}", "CTRL_UL", +]; + +const DIGIT_TABLE: [&str; 16] = [ + "CTRL_PS", " ", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ",", ".", "CTRL_UL", + "CTRL_US", +]; + +// private static final Charset DEFAULT_ENCODING = StandardCharsets.ISO_8859_1; + +// private AztecDetectorRXingResult ddata; + +pub fn decode( + detectorRXingResult: &AztecDetectorRXingResult, +) -> Result { + //let mut detectorRXingResult = detectorRXingResult.clone(); + let matrix = detectorRXingResult.getBits(); + let rawbits = extractBits(&detectorRXingResult, matrix); + let correctedBits = correctBits(&detectorRXingResult, &rawbits)?; + let rawBytes = convertBoolArrayToByteArray(&correctedBits.correctBits); + let result = getEncodedData(&correctedBits.correctBits); + let mut decoderRXingResult = DecoderRXingResult::new( + rawBytes, + result?, + Vec::new(), + format!("{}%", correctedBits.ecLevel), + ); + decoderRXingResult.setNumBits(correctedBits.correctBits.len()); + + Ok(decoderRXingResult) +} + +/// This method is used for testing the high-level encoder +pub fn highLevelDecode(correctedBits: &[bool]) -> Result { + getEncodedData(correctedBits) +} + +/** + * Gets the string encoded in the aztec code bits + * + * @return the decoded string + */ +fn getEncodedData(correctedBits: &[bool]) -> Result { + let endIndex = correctedBits.len(); + let mut latchTable = Table::UPPER; // table most recently latched to + let mut shiftTable = Table::UPPER; // table to use for the next read + + // Final decoded string result + // (correctedBits-5) / 4 is an upper bound on the size (all-digit result) + let mut result = String::with_capacity((correctedBits.len() - 5) / 4); + + // Intermediary buffer of decoded bytes, which is decoded into a string and flushed + // when character encoding changes (ECI) or input ends. + let mut decodedBytes: Vec = Vec::new(); + let mut encdr: &'static dyn encoding::Encoding = encoding::all::UTF_8; + + let mut index = 0; + while index < endIndex { + if shiftTable == Table::BINARY { + if endIndex - index < 5 { + break; + } + let mut length = readCode(correctedBits, index, 5); + index += 5; + if length == 0 { + if endIndex - index < 11 { + break; + } + length = readCode(correctedBits, index, 11) + 31; + index += 11; + } + for _charCount in 0..length { + // for (int charCount = 0; charCount < length; charCount++) { + if endIndex - index < 8 { + index = endIndex; // Force outer loop to exit + break; + } + let code = readCode(correctedBits, index, 8); + decodedBytes.push(code as u8); + index += 8; + } + // Go back to whatever mode we had been in + shiftTable = latchTable; + } else { + let size = if shiftTable == Table::DIGIT { 4 } else { 5 }; + if endIndex - index < size { + break; + } + let code = readCode(correctedBits, index, size); + index += size; + let str = getCharacter(shiftTable, code)?; + if "FLG(n)" == str { + if endIndex - index < 3 { + break; + } + let mut n = readCode(correctedBits, index, 3); + index += 3; + // flush bytes, FLG changes state + // try { + result.push_str(&String::from_utf8(decodedBytes.clone()).unwrap()); + // result.append(decodedBytes.toString(encoding.name())); + // } catch (UnsupportedEncodingException uee) { + // throw new IllegalStateException(uee); + // } + decodedBytes.clear(); + match n { + 0 => result.push(29 as char), // translate FNC1 as ASCII 29 + 7 => { + return Err(Exceptions::FormatException( + "FLG(7) is reserved and illegal".to_owned(), + )) + } // FLG(7) is reserved and illegal + _ => { + // ECI is decimal integer encoded as 1-6 codes in DIGIT mode + let mut eci = 0; + if endIndex - index < 4 * (n as usize) { + break; + } + while n > 0 { + //while (n-- > 0) { + let nextDigit = readCode(correctedBits, index, 4); + index += 4; + if nextDigit < 2 || nextDigit > 11 { + return Err(Exceptions::FormatException( + "Not a decimal digit".to_owned(), + )); // Not a decimal digit + } + eci = eci * 10 + (nextDigit - 2); + n -= 1; + } + let charsetECI = CharacterSetECI::getCharacterSetECIByValue(eci); + if charsetECI.is_err() { + return Err(Exceptions::FormatException( + "Charset must exist".to_owned(), + )); + } + encdr = CharacterSetECI::getCharset(&charsetECI?); + // encdr = charsetECI?::getCharset(); + } + } + // switch (n) { + // case 0: + // result.append(29 as char); // translate FNC1 as ASCII 29 + // break; + // case 7: + // throw FormatException.getFormatInstance(); // FLG(7) is reserved and illegal + // default: + // // ECI is decimal integer encoded as 1-6 codes in DIGIT mode + // int eci = 0; + // if (endIndex - index < 4 * n) { + // break; + // } + // while (n-- > 0) { + // int nextDigit = readCode(correctedBits, index, 4); + // index += 4; + // if (nextDigit < 2 || nextDigit > 11) { + // throw FormatException.getFormatInstance(); // Not a decimal digit + // } + // eci = eci * 10 + (nextDigit - 2); + // } + // CharacterSetECI charsetECI = CharacterSetECI.getCharacterSetECIByValue(eci); + // if (charsetECI == null) { + // throw FormatException.getFormatInstance(); + // } + // encoding = charsetECI.getCharset(); + // } + // Go back to whatever mode we had been in + shiftTable = latchTable; + } else if str.starts_with("CTRL_") { + // Table changes + // ISO/IEC 24778:2008 prescribes ending a shift sequence in the mode from which it was invoked. + // That's including when that mode is a shift. + // Our test case dlusbs.png for issue #642 exercises that. + latchTable = shiftTable; // Latch the current mode, so as to return to Upper after U/S B/S + shiftTable = getTable(str.chars().nth(5).unwrap()); + if str.chars().nth(6).unwrap() == 'L' { + latchTable = shiftTable; + } + } else { + // Though stored as a table of strings for convenience, codes actually represent 1 or 2 *bytes*. + let b = str.as_bytes(); + //let b = str.getBytes(StandardCharsets.US_ASCII); + //decodedBytes.write(b, 0, b.length); + for bt in b { + decodedBytes.push(*bt); + } + // Go back to whatever mode we had been in + shiftTable = latchTable; + } + } + } + //try { + if let Ok(str) = encdr.decode(&decodedBytes, encoding::DecoderTrap::Strict) { + result.push_str(&str); + } else { + return Err(Exceptions::IllegalStateException("bad encoding".to_owned())); + } + // result.push_str(decodedBytes.toString(encoding.name())); + //} catch (UnsupportedEncodingException uee) { + // can't happen + //throw new IllegalStateException(uee); + //} + Ok(result) +} + +/** + * gets the table corresponding to the char passed + */ +fn getTable(t: char) -> Table { + match t { + 'L' => Table::LOWER, + 'P' => Table::PUNCT, + 'M' => Table::MIXED, + 'D' => Table::DIGIT, + 'B' => Table::BINARY, + _ => Table::UPPER, + } + // switch (t) { + // case 'L': + // return Table.LOWER; + // case 'P': + // return Table.PUNCT; + // case 'M': + // return Table.MIXED; + // case 'D': + // return Table.DIGIT; + // case 'B': + // return Table.BINARY; + // case 'U': + // default: + // return Table.UPPER; + // } +} + +/** + * Gets the character (or string) corresponding to the passed code in the given table + * + * @param table the table used + * @param code the code of the character + */ +fn getCharacter(table: Table, code: u32) -> Result<&'static str, Exceptions> { + match table { + Table::UPPER => Ok(UPPER_TABLE[code as usize]), + Table::LOWER => Ok(LOWER_TABLE[code as usize]), + Table::MIXED => Ok(MIXED_TABLE[code as usize]), + Table::DIGIT => Ok(DIGIT_TABLE[code as usize]), + Table::PUNCT => Ok(PUNCT_TABLE[code as usize]), + _ => Err(Exceptions::IllegalStateException("Bad table".to_owned())), + } + // switch (table) { + // case UPPER: + // return UPPER_TABLE[code]; + // case LOWER: + // return LOWER_TABLE[code]; + // case MIXED: + // return MIXED_TABLE[code]; + // case PUNCT: + // return PUNCT_TABLE[code]; + // case DIGIT: + // return DIGIT_TABLE[code]; + // default: + // // Should not reach here. + // throw new IllegalStateException("Bad table"); +} + +struct CorrectedBitsRXingResult { + correctBits: Vec, + ecLevel: u32, +} +impl CorrectedBitsRXingResult { + pub fn new(correctBits: Vec, ecLevel: u32) -> Self { + Self { + correctBits, + ecLevel, + } + } +} + +/** + *

Performs RS error correction on an array of bits.

+ * + * @return the corrected array + * @throws FormatException if the input contains too many errors + */ +fn correctBits( + ddata: &&AztecDetectorRXingResult, + rawbits: &[bool], +) -> Result { + let gf: GenericGF; + let codewordSize; + + if ddata.getNbLayers() <= 2 { + codewordSize = 6; + gf = get_predefined_genericgf(PredefinedGenericGF::AztecData6); //GenericGF.AZTEC_DATA_6; + } else if ddata.getNbLayers() <= 8 { + codewordSize = 8; + gf = get_predefined_genericgf(PredefinedGenericGF::AztecData8); //GenericGF.AZTEC_DATA_8; + } else if ddata.getNbLayers() <= 22 { + codewordSize = 10; + gf = get_predefined_genericgf(PredefinedGenericGF::AztecData10); //GenericGF.AZTEC_DATA_10; + } else { + codewordSize = 12; + gf = get_predefined_genericgf(PredefinedGenericGF::AztecData12); //GenericGF.AZTEC_DATA_12; + } + + let numDataCodewords = ddata.getNbDatablocks(); + let numCodewords = rawbits.len() / codewordSize; + if numCodewords < numDataCodewords as usize { + return Err(Exceptions::FormatException(format!( + "numCodewords {}< numDataCodewords{}", + numCodewords, numDataCodewords + ))); + } + let mut offset = rawbits.len() % codewordSize; + + let mut dataWords = vec![0i32; numCodewords]; + for i in 0..numCodewords { + // for (int i = 0; i < numCodewords; i++, offset += codewordSize) { + dataWords[i] = readCode(rawbits, offset, codewordSize) as i32; + offset += codewordSize; + } + + //try { + let rsDecoder = ReedSolomonDecoder::new(gf); + rsDecoder.decode( + &mut dataWords, + (numCodewords - numDataCodewords as usize) as i32, + ); + //} catch (ReedSolomonException ex) { + //throw FormatException.getFormatInstance(ex); + //} + + // Now perform the unstuffing operation. + // First, count how many bits are going to be thrown out as stuffing + let mask = (1 << codewordSize) - 1; + let mut stuffedBits = 0; + for i in 0..numDataCodewords as usize { + // for (int i = 0; i < numDataCodewords; i++) { + let dataWord = dataWords[i]; + if dataWord == 0 || dataWord == mask { + return Err(Exceptions::FormatException( + "dataWord == 0 || dataWord == mask".to_owned(), + )); + //throw FormatException.getFormatInstance(); + } else if dataWord == 1 || dataWord == mask - 1 { + stuffedBits += 1; + } + } + // Now, actually unpack the bits and remove the stuffing + let mut correctedBits = + vec![false; (numDataCodewords * codewordSize as u32 - stuffedBits) as usize]; + let mut index = 0; + for i in 0..numDataCodewords as usize { + // for (int i = 0; i < numDataCodewords; i++) { + let dataWord = dataWords[i]; + if dataWord == 1 || dataWord == mask - 1 { + // next codewordSize-1 bits are all zeros or all ones + correctedBits.splice( + index..index + codewordSize - 1, + vec![dataWord > 1; codewordSize], + ); + // Arrays.fill(correctedBits, index, index + codewordSize - 1, dataWord > 1); + index += codewordSize - 1; + } else { + for bit in (0..codewordSize).rev() { + // for (int bit = codewordSize - 1; bit >= 0; --bit) { + correctedBits[index] = (dataWord & (1 << bit)) != 0; + index += 1; + } + } + } + + Ok(CorrectedBitsRXingResult::new( + correctedBits, + (100 * (numCodewords - numDataCodewords as usize) / numCodewords) as u32, + )) +} + +/** + * Gets the array of bits from an Aztec Code matrix + * + * @return the array of bits + */ +fn extractBits(ddata: &AztecDetectorRXingResult, matrix: &BitMatrix) -> Vec { + let compact = ddata.isCompact(); + let layers = ddata.getNbLayers(); + let baseMatrixSize = ((if compact { 11 } else { 14 }) + layers * 4) as usize; // not including alignment lines + let mut alignmentMap = vec![0u32; baseMatrixSize]; + let mut rawbits = vec![false; totalBitsInLayer(layers as usize, compact)]; + + if compact { + for i in 0..alignmentMap.len() { + // for (int i = 0; i < alignmentMap.length; i++) { + alignmentMap[i] = i as u32; + } + } else { + let matrixSize = baseMatrixSize + 1 + 2 * ((baseMatrixSize / 2 - 1) / 15); + let origCenter = baseMatrixSize / 2; + let center = matrixSize / 2; + for i in 0..origCenter { + // for (int i = 0; i < origCenter; i++) { + let newOffset = i + i / 15; + alignmentMap[origCenter - i - 1] = (center - newOffset - 1) as u32; + alignmentMap[origCenter + i] = (center + newOffset + 1) as u32; + } + } + let mut rowOffset = 0; + for i in 0..layers { + // for (int i = 0, rowOffset = 0; i < layers; i++) { + let rowSize = (layers - i) * 4 + (if compact { 9 } else { 12 }); + // The top-left most point of this layer is (not including alignment lines) + let low = i * 2; + // The bottom-right most point of this layer is (not including alignment lines) + let high = baseMatrixSize as u32 - 1 - low; + // We pull bits from the two 2 x rowSize columns and two rowSize x 2 rows + for j in 0..rowSize { + // for (int j = 0; j < rowSize; j++) { + let columnOffset = j * 2; + for k in 0..2 { + // for (int k = 0; k < 2; k++) { + // left column + rawbits[(rowOffset + columnOffset + k) as usize] = matrix.get( + alignmentMap[(low + k) as usize], + alignmentMap[(low + j) as usize], + ); + // bottom row + rawbits[(rowOffset + 2 * rowSize + columnOffset + k) as usize] = matrix.get( + alignmentMap[(low + j) as usize], + alignmentMap[(high - k) as usize], + ); + // right column + rawbits[(rowOffset + 4 * rowSize + columnOffset + k) as usize] = matrix.get( + alignmentMap[(high - k) as usize], + alignmentMap[(high - j) as usize], + ); + // top row + rawbits[(rowOffset + 6 * rowSize + columnOffset + k) as usize] = matrix.get( + alignmentMap[(high - j) as usize], + alignmentMap[(low + k) as usize], + ); + } + } + rowOffset += rowSize * 8; + } + return rawbits; +} + +/** + * Reads a code of given length and at given index in an array of bits + */ +fn readCode(rawbits: &[bool], startIndex: usize, length: usize) -> u32 { + let mut res = 0; + for i in startIndex..length { + // for (int i = startIndex; i < startIndex + length; i++) { + res <<= 1; + if rawbits[i] { + res |= 0x01; + } + } + return res; +} + +/** + * Reads a code of length 8 in an array of bits, padding with zeros + */ +fn readByte(rawbits: &[bool], startIndex: usize) -> u8 { + let n = rawbits.len() - startIndex; + if n >= 8 { + return readCode(rawbits, startIndex, 8) as u8; + } + return (readCode(rawbits, startIndex, n) << (8 - n)) as u8; +} + +/** + * Packs a bit array into bytes, most significant bit first + */ +pub fn convertBoolArrayToByteArray(boolArr: &[bool]) -> Vec { + let mut byteArr = vec![0u8; (boolArr.len() + 7) / 8]; + for i in 0..byteArr.len() { + // for (int i = 0; i < byteArr.length; i++) { + byteArr[i] = readByte(boolArr, 8 * i); + } + return byteArr; +} + +fn totalBitsInLayer(layers: usize, compact: bool) -> usize { + (if compact { 88 } else { 112 } + 16 * layers) * layers + // return ((compact ? 88 : 112) + 16 * layers) * layers; +} diff --git a/src/aztec/decoder/Decoder.java b/src/aztec/decoder/Decoder.java deleted file mode 100644 index 92a5033..0000000 --- a/src/aztec/decoder/Decoder.java +++ /dev/null @@ -1,443 +0,0 @@ -/* - * Copyright 2010 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. - */ - -package com.google.zxing.aztec.decoder; - -import com.google.zxing.FormatException; -import com.google.zxing.aztec.AztecDetectorRXingResult; -import com.google.zxing.common.BitMatrix; -import com.google.zxing.common.CharacterSetECI; -import com.google.zxing.common.DecoderRXingResult; -import com.google.zxing.common.reedsolomon.GenericGF; -import com.google.zxing.common.reedsolomon.ReedSolomonDecoder; -import com.google.zxing.common.reedsolomon.ReedSolomonException; - -import java.io.ByteArrayOutputStream; -import java.io.UnsupportedEncodingException; -import java.nio.charset.Charset; -import java.nio.charset.StandardCharsets; -import java.util.Arrays; - -/** - *

The main class which implements Aztec Code decoding -- as opposed to locating and extracting - * the Aztec Code from an image.

- * - * @author David Olivier - */ -public final class Decoder { - - private enum Table { - UPPER, - LOWER, - MIXED, - DIGIT, - PUNCT, - BINARY - } - - private static final String[] UPPER_TABLE = { - "CTRL_PS", " ", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", - "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "CTRL_LL", "CTRL_ML", "CTRL_DL", "CTRL_BS" - }; - - private static final String[] LOWER_TABLE = { - "CTRL_PS", " ", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", - "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "CTRL_US", "CTRL_ML", "CTRL_DL", "CTRL_BS" - }; - - private static final String[] MIXED_TABLE = { - "CTRL_PS", " ", "\1", "\2", "\3", "\4", "\5", "\6", "\7", "\b", "\t", "\n", - "\13", "\f", "\r", "\33", "\34", "\35", "\36", "\37", "@", "\\", "^", "_", - "`", "|", "~", "\177", "CTRL_LL", "CTRL_UL", "CTRL_PL", "CTRL_BS" - }; - - private static final String[] PUNCT_TABLE = { - "FLG(n)", "\r", "\r\n", ". ", ", ", ": ", "!", "\"", "#", "$", "%", "&", "'", "(", ")", - "*", "+", ",", "-", ".", "/", ":", ";", "<", "=", ">", "?", "[", "]", "{", "}", "CTRL_UL" - }; - - private static final String[] DIGIT_TABLE = { - "CTRL_PS", " ", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ",", ".", "CTRL_UL", "CTRL_US" - }; - - private static final Charset DEFAULT_ENCODING = StandardCharsets.ISO_8859_1; - - private AztecDetectorRXingResult ddata; - - public DecoderRXingResult decode(AztecDetectorRXingResult detectorRXingResult) throws FormatException { - ddata = detectorRXingResult; - BitMatrix matrix = detectorRXingResult.getBits(); - boolean[] rawbits = extractBits(matrix); - CorrectedBitsRXingResult correctedBits = correctBits(rawbits); - byte[] rawBytes = convertBoolArrayToByteArray(correctedBits.correctBits); - String result = getEncodedData(correctedBits.correctBits); - DecoderRXingResult decoderRXingResult = - new DecoderRXingResult(rawBytes, result, null, String.format("%d%%", correctedBits.ecLevel)); - decoderRXingResult.setNumBits(correctedBits.correctBits.length); - return decoderRXingResult; - } - - // This method is used for testing the high-level encoder - public static String highLevelDecode(boolean[] correctedBits) throws FormatException { - return getEncodedData(correctedBits); - } - - /** - * Gets the string encoded in the aztec code bits - * - * @return the decoded string - */ - private static String getEncodedData(boolean[] correctedBits) throws FormatException { - int endIndex = correctedBits.length; - Table latchTable = Table.UPPER; // table most recently latched to - Table shiftTable = Table.UPPER; // table to use for the next read - - // Final decoded string result - // (correctedBits-5) / 4 is an upper bound on the size (all-digit result) - StringBuilder result = new StringBuilder((correctedBits.length - 5) / 4); - - // Intermediary buffer of decoded bytes, which is decoded into a string and flushed - // when character encoding changes (ECI) or input ends. - ByteArrayOutputStream decodedBytes = new ByteArrayOutputStream(); - Charset encoding = DEFAULT_ENCODING; - - int index = 0; - while (index < endIndex) { - if (shiftTable == Table.BINARY) { - if (endIndex - index < 5) { - break; - } - int length = readCode(correctedBits, index, 5); - index += 5; - if (length == 0) { - if (endIndex - index < 11) { - break; - } - length = readCode(correctedBits, index, 11) + 31; - index += 11; - } - for (int charCount = 0; charCount < length; charCount++) { - if (endIndex - index < 8) { - index = endIndex; // Force outer loop to exit - break; - } - int code = readCode(correctedBits, index, 8); - decodedBytes.write((byte) code); - index += 8; - } - // Go back to whatever mode we had been in - shiftTable = latchTable; - } else { - int size = shiftTable == Table.DIGIT ? 4 : 5; - if (endIndex - index < size) { - break; - } - int code = readCode(correctedBits, index, size); - index += size; - String str = getCharacter(shiftTable, code); - if ("FLG(n)".equals(str)) { - if (endIndex - index < 3) { - break; - } - int n = readCode(correctedBits, index, 3); - index += 3; - // flush bytes, FLG changes state - try { - result.append(decodedBytes.toString(encoding.name())); - } catch (UnsupportedEncodingException uee) { - throw new IllegalStateException(uee); - } - decodedBytes.reset(); - switch (n) { - case 0: - result.append((char) 29); // translate FNC1 as ASCII 29 - break; - case 7: - throw FormatException.getFormatInstance(); // FLG(7) is reserved and illegal - default: - // ECI is decimal integer encoded as 1-6 codes in DIGIT mode - int eci = 0; - if (endIndex - index < 4 * n) { - break; - } - while (n-- > 0) { - int nextDigit = readCode(correctedBits, index, 4); - index += 4; - if (nextDigit < 2 || nextDigit > 11) { - throw FormatException.getFormatInstance(); // Not a decimal digit - } - eci = eci * 10 + (nextDigit - 2); - } - CharacterSetECI charsetECI = CharacterSetECI.getCharacterSetECIByValue(eci); - if (charsetECI == null) { - throw FormatException.getFormatInstance(); - } - encoding = charsetECI.getCharset(); - } - // Go back to whatever mode we had been in - shiftTable = latchTable; - } else if (str.startsWith("CTRL_")) { - // Table changes - // ISO/IEC 24778:2008 prescribes ending a shift sequence in the mode from which it was invoked. - // That's including when that mode is a shift. - // Our test case dlusbs.png for issue #642 exercises that. - latchTable = shiftTable; // Latch the current mode, so as to return to Upper after U/S B/S - shiftTable = getTable(str.charAt(5)); - if (str.charAt(6) == 'L') { - latchTable = shiftTable; - } - } else { - // Though stored as a table of strings for convenience, codes actually represent 1 or 2 *bytes*. - byte[] b = str.getBytes(StandardCharsets.US_ASCII); - decodedBytes.write(b, 0, b.length); - // Go back to whatever mode we had been in - shiftTable = latchTable; - } - } - } - try { - result.append(decodedBytes.toString(encoding.name())); - } catch (UnsupportedEncodingException uee) { - // can't happen - throw new IllegalStateException(uee); - } - return result.toString(); - } - - /** - * gets the table corresponding to the char passed - */ - private static Table getTable(char t) { - switch (t) { - case 'L': - return Table.LOWER; - case 'P': - return Table.PUNCT; - case 'M': - return Table.MIXED; - case 'D': - return Table.DIGIT; - case 'B': - return Table.BINARY; - case 'U': - default: - return Table.UPPER; - } - } - - /** - * Gets the character (or string) corresponding to the passed code in the given table - * - * @param table the table used - * @param code the code of the character - */ - private static String getCharacter(Table table, int code) { - switch (table) { - case UPPER: - return UPPER_TABLE[code]; - case LOWER: - return LOWER_TABLE[code]; - case MIXED: - return MIXED_TABLE[code]; - case PUNCT: - return PUNCT_TABLE[code]; - case DIGIT: - return DIGIT_TABLE[code]; - default: - // Should not reach here. - throw new IllegalStateException("Bad table"); - } - } - - static final class CorrectedBitsRXingResult { - private final boolean[] correctBits; - private final int ecLevel; - - CorrectedBitsRXingResult(boolean[] correctBits, int ecLevel) { - this.correctBits = correctBits; - this.ecLevel = ecLevel; - } - } - - /** - *

Performs RS error correction on an array of bits.

- * - * @return the corrected array - * @throws FormatException if the input contains too many errors - */ - private CorrectedBitsRXingResult correctBits(boolean[] rawbits) throws FormatException { - GenericGF gf; - int codewordSize; - - if (ddata.getNbLayers() <= 2) { - codewordSize = 6; - gf = GenericGF.AZTEC_DATA_6; - } else if (ddata.getNbLayers() <= 8) { - codewordSize = 8; - gf = GenericGF.AZTEC_DATA_8; - } else if (ddata.getNbLayers() <= 22) { - codewordSize = 10; - gf = GenericGF.AZTEC_DATA_10; - } else { - codewordSize = 12; - gf = GenericGF.AZTEC_DATA_12; - } - - int numDataCodewords = ddata.getNbDatablocks(); - int numCodewords = rawbits.length / codewordSize; - if (numCodewords < numDataCodewords) { - throw FormatException.getFormatInstance(); - } - int offset = rawbits.length % codewordSize; - - int[] dataWords = new int[numCodewords]; - for (int i = 0; i < numCodewords; i++, offset += codewordSize) { - dataWords[i] = readCode(rawbits, offset, codewordSize); - } - - try { - ReedSolomonDecoder rsDecoder = new ReedSolomonDecoder(gf); - rsDecoder.decode(dataWords, numCodewords - numDataCodewords); - } catch (ReedSolomonException ex) { - throw FormatException.getFormatInstance(ex); - } - - // Now perform the unstuffing operation. - // First, count how many bits are going to be thrown out as stuffing - int mask = (1 << codewordSize) - 1; - int stuffedBits = 0; - for (int i = 0; i < numDataCodewords; i++) { - int dataWord = dataWords[i]; - if (dataWord == 0 || dataWord == mask) { - throw FormatException.getFormatInstance(); - } else if (dataWord == 1 || dataWord == mask - 1) { - stuffedBits++; - } - } - // Now, actually unpack the bits and remove the stuffing - boolean[] correctedBits = new boolean[numDataCodewords * codewordSize - stuffedBits]; - int index = 0; - for (int i = 0; i < numDataCodewords; i++) { - int dataWord = dataWords[i]; - if (dataWord == 1 || dataWord == mask - 1) { - // next codewordSize-1 bits are all zeros or all ones - Arrays.fill(correctedBits, index, index + codewordSize - 1, dataWord > 1); - index += codewordSize - 1; - } else { - for (int bit = codewordSize - 1; bit >= 0; --bit) { - correctedBits[index++] = (dataWord & (1 << bit)) != 0; - } - } - } - - return new CorrectedBitsRXingResult(correctedBits, 100 * (numCodewords - numDataCodewords) / numCodewords); - } - - /** - * Gets the array of bits from an Aztec Code matrix - * - * @return the array of bits - */ - private boolean[] extractBits(BitMatrix matrix) { - boolean compact = ddata.isCompact(); - int layers = ddata.getNbLayers(); - int baseMatrixSize = (compact ? 11 : 14) + layers * 4; // not including alignment lines - int[] alignmentMap = new int[baseMatrixSize]; - boolean[] rawbits = new boolean[totalBitsInLayer(layers, compact)]; - - if (compact) { - for (int i = 0; i < alignmentMap.length; i++) { - alignmentMap[i] = i; - } - } else { - int matrixSize = baseMatrixSize + 1 + 2 * ((baseMatrixSize / 2 - 1) / 15); - int origCenter = baseMatrixSize / 2; - int center = matrixSize / 2; - for (int i = 0; i < origCenter; i++) { - int newOffset = i + i / 15; - alignmentMap[origCenter - i - 1] = center - newOffset - 1; - alignmentMap[origCenter + i] = center + newOffset + 1; - } - } - for (int i = 0, rowOffset = 0; i < layers; i++) { - int rowSize = (layers - i) * 4 + (compact ? 9 : 12); - // The top-left most point of this layer is (not including alignment lines) - int low = i * 2; - // The bottom-right most point of this layer is (not including alignment lines) - int high = baseMatrixSize - 1 - low; - // We pull bits from the two 2 x rowSize columns and two rowSize x 2 rows - for (int j = 0; j < rowSize; j++) { - int columnOffset = j * 2; - for (int k = 0; k < 2; k++) { - // left column - rawbits[rowOffset + columnOffset + k] = - matrix.get(alignmentMap[low + k], alignmentMap[low + j]); - // bottom row - rawbits[rowOffset + 2 * rowSize + columnOffset + k] = - matrix.get(alignmentMap[low + j], alignmentMap[high - k]); - // right column - rawbits[rowOffset + 4 * rowSize + columnOffset + k] = - matrix.get(alignmentMap[high - k], alignmentMap[high - j]); - // top row - rawbits[rowOffset + 6 * rowSize + columnOffset + k] = - matrix.get(alignmentMap[high - j], alignmentMap[low + k]); - } - } - rowOffset += rowSize * 8; - } - return rawbits; - } - - /** - * Reads a code of given length and at given index in an array of bits - */ - private static int readCode(boolean[] rawbits, int startIndex, int length) { - int res = 0; - for (int i = startIndex; i < startIndex + length; i++) { - res <<= 1; - if (rawbits[i]) { - res |= 0x01; - } - } - return res; - } - - /** - * Reads a code of length 8 in an array of bits, padding with zeros - */ - private static byte readByte(boolean[] rawbits, int startIndex) { - int n = rawbits.length - startIndex; - if (n >= 8) { - return (byte) readCode(rawbits, startIndex, 8); - } - return (byte) (readCode(rawbits, startIndex, n) << (8 - n)); - } - - /** - * Packs a bit array into bytes, most significant bit first - */ - static byte[] convertBoolArrayToByteArray(boolean[] boolArr) { - byte[] byteArr = new byte[(boolArr.length + 7) / 8]; - for (int i = 0; i < byteArr.length; i++) { - byteArr[i] = readByte(boolArr, 8 * i); - } - return byteArr; - } - - private static int totalBitsInLayer(int layers, boolean compact) { - return ((compact ? 88 : 112) + 16 * layers) * layers; - } -} diff --git a/src/aztec/detector/Detector.java b/src/aztec/detector.rs similarity index 94% rename from src/aztec/detector/Detector.java rename to src/aztec/detector.rs index b5e5dbd..3bf829b 100644 --- a/src/aztec/detector/Detector.java +++ b/src/aztec/detector.rs @@ -14,18 +14,27 @@ * limitations under the License. */ -package com.google.zxing.aztec.detector; +// package com.google.zxing.aztec.detector; -import com.google.zxing.NotFoundException; -import com.google.zxing.RXingResultPoint; -import com.google.zxing.aztec.AztecDetectorRXingResult; -import com.google.zxing.common.BitMatrix; -import com.google.zxing.common.GridSampler; -import com.google.zxing.common.detector.MathUtils; -import com.google.zxing.common.detector.WhiteRectangleDetector; -import com.google.zxing.common.reedsolomon.GenericGF; -import com.google.zxing.common.reedsolomon.ReedSolomonDecoder; -import com.google.zxing.common.reedsolomon.ReedSolomonException; +// import com.google.zxing.NotFoundException; +// import com.google.zxing.RXingResultPoint; +// import com.google.zxing.aztec.AztecDetectorRXingResult; +// import com.google.zxing.common.BitMatrix; +// import com.google.zxing.common.GridSampler; +// import com.google.zxing.common.detector.MathUtils; +// import com.google.zxing.common.detector.WhiteRectangleDetector; +// import com.google.zxing.common.reedsolomon.GenericGF; +// import com.google.zxing.common.reedsolomon.ReedSolomonDecoder; +// import com.google.zxing.common.reedsolomon.ReedSolomonException; + +use crate::common::BitMatrix; + +const EXPECTED_CORNER_BITS : [u16;4]= [ + 0xee0, // 07340 XXX .XX X.. ... + 0x1dc, // 00734 ... XXX .XX X.. + 0x83b, // 04073 X.. ... XXX .XX + 0x707, // 03407 .XX X.. ... XXX +]; /** * Encapsulates logic that can detect an Aztec Code in an image, even if the Aztec Code @@ -34,25 +43,27 @@ import com.google.zxing.common.reedsolomon.ReedSolomonException; * @author David Olivier * @author Frank Yellin */ -public final class Detector { +pub struct Detector { + image:BitMatrix, - private static final int[] EXPECTED_CORNER_BITS = { - 0xee0, // 07340 XXX .XX X.. ... - 0x1dc, // 00734 ... XXX .XX X.. - 0x83b, // 04073 X.. ... XXX .XX - 0x707, // 03407 .XX X.. ... XXX - }; + compact:bool, + nbLayers:u32, + nbDataBlocks:u32, + nbCenterLayers:u32, + shift:u32, +} - private final BitMatrix image; +impl Detector { - private boolean compact; - private int nbLayers; - private int nbDataBlocks; - private int nbCenterLayers; - private int shift; - - public Detector(BitMatrix image) { - this.image = image; + pub fn new( image:BitMatrix) -> Self{ + Self{ + image, + compact: false, + nbLayers: 0, + nbDataBlocks: 0, + nbCenterLayers: 0, + shift: 0, + } } public AztecDetectorRXingResult detect() throws NotFoundException { diff --git a/src/aztec/mod.rs b/src/aztec/mod.rs new file mode 100644 index 0000000..5017799 --- /dev/null +++ b/src/aztec/mod.rs @@ -0,0 +1,10 @@ +mod AztecDetectorResult; +pub mod decoder; +// pub mod detector; + +#[cfg(test)] +mod DecoderTest; +// #[cfg(test)] +// mod EncoderTest; + +mod shared_test_methods; \ No newline at end of file diff --git a/src/aztec/shared_test_methods.rs b/src/aztec/shared_test_methods.rs new file mode 100644 index 0000000..1ba5823 --- /dev/null +++ b/src/aztec/shared_test_methods.rs @@ -0,0 +1,32 @@ +use regex::Regex; + +use crate::common::BitArray; + +const SPACES :&str= "\\s+"; +const DOTX : &str = "[^.X]"; + +pub fn toBitArray( bits:&str) -> BitArray{ + let mut ba_in = BitArray::new(); + let replacer_regex = Regex::new(DOTX).unwrap(); + let str = replacer_regex.replace_all(bits, ""); + for aStr in str.chars() { + // for (char aStr : str) { + ba_in.appendBit(aStr == 'X'); + } + + ba_in + } + + pub fn toBooleanArray( bitArray:&BitArray) ->Vec{ + let mut result = vec![false;bitArray.getSize()]; + for i in 0..result.len() { + // for (int i = 0; i < result.length; i++) { + result[i] = bitArray.get(i); + } + result + } + + pub fn stripSpace( s:&str) -> String{ + let replacer_regex = Regex::new(SPACES).unwrap(); + replacer_regex.replace_all(s, "").to_string() + } \ No newline at end of file diff --git a/src/common/mod.rs b/src/common/mod.rs index 2beb92e..02eab35 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -719,27 +719,17 @@ impl fmt::Display for BitArray { * * @author Sean Owen */ -pub struct DetectorRXingResult { - bits: BitMatrix, - points: Vec, +pub trait DetectorRXingResult { + + fn getBits(&self) -> &BitMatrix; + + fn getPoints(&self) -> &Vec; } -impl DetectorRXingResult { - pub fn new(bits: BitMatrix, points: Vec) -> Self { - Self { - bits: bits, - points: points, - } - } - - pub fn getBits(&self) -> &BitMatrix { - return &self.bits; - } - - pub fn getPoints(&self) -> &Vec { - return &self.points; - } -} +// pub struct DetectorRXingResult { +// bits: BitMatrix, +// points: Vec, +// } /* * Copyright 2007 ZXing authors diff --git a/src/lib.rs b/src/lib.rs index 9218d5f..2661ab4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,7 @@ mod common; mod exceptions; mod client; +mod aztec; #[cfg(feature="image")] mod BufferedImageLuminanceSource; @@ -840,7 +841,7 @@ impl PartialEq for RXingResultPoint { } impl Eq for RXingResultPoint {} impl RXingResultPoint { - pub fn new(x: f32, y: f32) -> Self { + pub const fn new(x: f32, y: f32) -> Self { Self { x, y } }