diff --git a/Cargo.toml b/Cargo.toml index 8105bc5..b95206d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,9 @@ chrono-tz = "0.4" image = {version = "0.24.3", optional = true} imageproc = {version = "0.23.0", optional = true} +[dev-dependencies] +java-properties = "1.4.1" + [features] default = ["image"] image = ["dep:image", "dep:imageproc"] \ No newline at end of file diff --git a/src/aztec/DetectorTest.rs b/src/aztec/DetectorTest.rs index 053573f..fbfae1b 100644 --- a/src/aztec/DetectorTest.rs +++ b/src/aztec/DetectorTest.rs @@ -67,6 +67,16 @@ fn test_error_in_parameter_locator_not_compact() { test_error_in_parameter_locator(&format!("{}{}{}", alphabet, alphabet, alphabet)); } +#[test] +fn test_aztec_rxing_result_sample() { + let matrix = BitMatrix::parse_strings(TEST_BARCODE, "X ", " ").expect("string parse success"); + let r = Detector::new(matrix).detect(false); + assert!(r.is_ok()); + let r = r.expect("result already tested as ok"); + let res = decoder::decode(&r).expect("decode success"); + assert_eq!("Histórico", res.getText()); +} + // Test that we can tolerate errors in the parameter locator bits fn test_error_in_parameter_locator(data: &str) { let aztec = encoder::encoder::encode(data, 25, encoder::encoder::DEFAULT_AZTEC_LAYERS) @@ -260,3 +270,80 @@ fn get_orientation_points(code: &AztecCode) -> Vec { } return result; } + +const TEST_BARCODE: &str = 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 X X X X X + X X X X X X X X X X X X X X X X X X X X X X X X X + X X X X X X X X X X X X X X X X X X X X X X X X X + X X X X X X X X X X X X X X X X X X X X X X X X X +X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X +X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X +X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X +X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X +X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X +X X X X X X X X X X X X X X X X X X X X +X X X X X X X X X X X X X X X X X X X X +X X X X X X X X X X X X X X X X X X X X +X X X X X X X X X X X X X X X X X X X X +X X X X X X X X X X X X X X X X X X X X + X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X + X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X + X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X + X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X + X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X + X X X X X X X X X X X X X X X X X X X X X X X X X + X X X X X X X X X X X X X X X X X X X X X X X X X + X X X X X X X X X X X X X X X X X X X X X X X X X + X X X X X X X X X X X X X X X X X X X X X X X X X + X X X X X X X X X X X X X X X X X X X X X X X X X + X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X + X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X + X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X + X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X + X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X + X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X + X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X + X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X + X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X + X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X + X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X + X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X + X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X + X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X + X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X + X X X X X X X X X X X X X X X X X X X X X X X X X + X X X X X X X X X X X X X X X X X X X X X X X X X + X X X X X X X X X X X X X X X X X X X X X X X X X + X X X X X X X X X X X X X X X X X X X X X X X X X + X X X X X X X X X X X X X X X X X X X X X X X X X +X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X +X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X +X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X +X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X +X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X +X X X X X X X X X X X X X X X X X X X X +X X X X X X X X X X X X X X X X X X X X +X X X X X X X X X X X X X X X X X X X X +X X X X X X X X X X X X X X X X X X X X +X X X X X X X X X X X X X X X X X X X X +X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X +X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X +X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X +X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X +X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X +X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X +X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X +X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X +X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X +X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X +"; diff --git a/src/aztec/EncoderTest.rs b/src/aztec/EncoderTest.rs index 154c923..9d5ffe7 100644 --- a/src/aztec/EncoderTest.rs +++ b/src/aztec/EncoderTest.rs @@ -177,8 +177,8 @@ fn testAztecWriter() { ); // Test AztecWriter defaults let data = "In ut magna vel mauris malesuada"; - // let writer = AztecWriter{}; - let matrix = AztecWriter::encode(data, &BarcodeFormat::AZTEC, 0, 0).expect("matrix must exist"); + let writer = AztecWriter{}; + let matrix = writer.encode(data, &BarcodeFormat::AZTEC, 0, 0).expect("matrix must exist"); let aztec = encoder::encode( data, encoder::DEFAULT_EC_PERCENT, @@ -732,7 +732,7 @@ fn testWriter( EncodeHintType::ERROR_CORRECTION, EncodeHintValue::ErrorCorrection(ecc_percent.to_string()), ); - let mut matrix = AztecWriter::encode_with_hints(data, &BarcodeFormat::AZTEC, 0, 0, &hints) + let mut matrix = AztecWriter{}.encode_with_hints(data, &BarcodeFormat::AZTEC, 0, 0, &hints) .expect("encoder created"); let cset = match charset { diff --git a/src/aztec/aztec_reader.rs b/src/aztec/aztec_reader.rs index 8a1f818..20f9b33 100644 --- a/src/aztec/aztec_reader.rs +++ b/src/aztec/aztec_reader.rs @@ -20,7 +20,7 @@ use crate::{ common::{DecoderRXingResult, DetectorRXingResult}, exceptions::Exceptions, BarcodeFormat, BinaryBitmap, DecodeHintType, DecodeHintValue, RXingResult, - RXingResultMetadataType, Reader, RXingResultMetadataValue, + RXingResultMetadataType, RXingResultMetadataValue, Reader, }; use super::{decoder, detector::Detector}; @@ -40,7 +40,7 @@ impl Reader for AztecReader { * @throws NotFoundException if a Data Matrix code cannot be found * @throws FormatException if a Data Matrix code cannot be decoded */ - fn decode(&self,image: &BinaryBitmap) -> Result { + fn decode(&self, image: &BinaryBitmap) -> Result { self.decode_with_hints(image, &HashMap::new()) } @@ -52,11 +52,19 @@ impl Reader for AztecReader { // let notFoundException = None; // let formatException = None; let mut detector = Detector::new(image.getBlackMatrix()?.clone()); - let mut points; - let mut decoderRXingResult: DecoderRXingResult; + let points; + let decoderRXingResult: DecoderRXingResult; // try { - let detectorRXingResult = detector.detect(false)?; + let detectorRXingResult = if let Ok(det) = detector.detect(false) { + det + } else if let Ok(det) = detector.detect(true) { + det + } else { + return Err(Exceptions::NotFoundException( + "barcode not found".to_owned(), + )); + }; points = detectorRXingResult.getPoints(); decoderRXingResult = decoder::decode(&detectorRXingResult)?; @@ -67,9 +75,9 @@ impl Reader for AztecReader { // } // if (decoderRXingResult == null) { // try { - let detectorRXingResult = detector.detect(true)?; - points = detectorRXingResult.getPoints(); - decoderRXingResult = decoder::decode(&detectorRXingResult)?; + // let detectorRXingResult = detector.detect(true)?; + // points = detectorRXingResult.getPoints(); + // decoderRXingResult = decoder::decode(&detectorRXingResult)?; // } catch (NotFoundException | FormatException e) { // if (notFoundException != null) { // throw notFoundException; @@ -101,19 +109,26 @@ impl Reader for AztecReader { .as_millis(), ); - - let byteSegments = decoderRXingResult.getByteSegments(); if !byteSegments.is_empty() { - result.putMetadata(RXingResultMetadataType::BYTE_SEGMENTS, RXingResultMetadataValue::ByteSegments(byteSegments.clone())); + result.putMetadata( + RXingResultMetadataType::BYTE_SEGMENTS, + RXingResultMetadataValue::ByteSegments(byteSegments.clone()), + ); } let ecLevel = decoderRXingResult.getECLevel(); if !ecLevel.is_empty() { - result.putMetadata(RXingResultMetadataType::ERROR_CORRECTION_LEVEL, RXingResultMetadataValue::ErrorCorrectionLevel(ecLevel.to_owned())); + result.putMetadata( + RXingResultMetadataType::ERROR_CORRECTION_LEVEL, + RXingResultMetadataValue::ErrorCorrectionLevel(ecLevel.to_owned()), + ); } result.putMetadata( RXingResultMetadataType::SYMBOLOGY_IDENTIFIER, - RXingResultMetadataValue::SymbologyIdentifier(format!("]z{}", decoderRXingResult.getSymbologyModifier())), + RXingResultMetadataValue::SymbologyIdentifier(format!( + "]z{}", + decoderRXingResult.getSymbologyModifier() + )), ); Ok(result) diff --git a/src/BufferedImageLuminanceSource.rs b/src/buffered_image_luminance_source.rs similarity index 95% rename from src/BufferedImageLuminanceSource.rs rename to src/buffered_image_luminance_source.rs index 01fb657..7548582 100644 --- a/src/BufferedImageLuminanceSource.rs +++ b/src/buffered_image_luminance_source.rs @@ -21,12 +21,12 @@ // import java.awt.image.BufferedImage; // import java.awt.image.WritableRaster; -use image::{DynamicImage, Luma}; +use image::{DynamicImage, Luma, GenericImage, EncodableLayout}; use imageproc::geometric_transformations::rotate_about_center; use crate::LuminanceSource; -static MINUS_45_IN_RADIANS: f32 = -0.7853981633974483; // Math.toRadians(-45.0) +const MINUS_45_IN_RADIANS: f32 = -0.7853981633974483; // Math.toRadians(-45.0) /** * This LuminanceSource implementation is meant for J2SE clients and our blackbox unit tests. @@ -97,9 +97,10 @@ impl BufferedImageLuminanceSource { // } // } + Self { - image: image, + image: DynamicImage::from(image.to_luma8()), width: width, height: height, left: left, @@ -189,8 +190,8 @@ impl LuminanceSource for BufferedImageLuminanceSource { let img = rotate_about_center( &self.image.to_luma8(), MINUS_45_IN_RADIANS, - imageproc::geometric_transformations::Interpolation::Bicubic, - Luma([0; 1]), + imageproc::geometric_transformations::Interpolation::Nearest, + Luma([255; 1]), ); let new_img = DynamicImage::from(img); diff --git a/src/common/mod.rs b/src/common/mod.rs index 0688d16..6c506ab 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -3829,37 +3829,38 @@ impl Binarizer for HybridBinarizer { && height >= HybridBinarizer::MINIMUM_DIMENSION { let luminances = source.getMatrix(); - let mut subWidth = width >> HybridBinarizer::BLOCK_SIZE_POWER; + let mut sub_width = width >> HybridBinarizer::BLOCK_SIZE_POWER; if (width & HybridBinarizer::BLOCK_SIZE_MASK) != 0 { - subWidth += 1; + sub_width += 1; } - let mut subHeight = height >> HybridBinarizer::BLOCK_SIZE_POWER; + let mut sub_height = height >> HybridBinarizer::BLOCK_SIZE_POWER; if (height & HybridBinarizer::BLOCK_SIZE_MASK) != 0 { - subHeight += 1; + sub_height += 1; } - let blackPoints = Self::calculateBlackPoints( + let black_points = Self::calculateBlackPoints( &luminances, - subWidth as u32, - subHeight as u32, + sub_width as u32, + sub_height as u32, width as u32, height as u32, ); - let mut newMatrix = BitMatrix::new(width as u32, height as u32)?; + let mut new_matrix = BitMatrix::new(width as u32, height as u32)?; Self::calculateThresholdForBlock( &luminances, - subWidth as u32, - subHeight as u32, + sub_width as u32, + sub_height as u32, width as u32, height as u32, - &blackPoints, - &mut newMatrix, + &black_points, + &mut new_matrix, ); - matrix = newMatrix; + matrix = new_matrix; } else { // If the image is too small, fall back to the global histogram approach. matrix = self.ghb.getBlackMatrix()?; } + // dbg!(matrix.to_string()); Ok(matrix) } @@ -3997,16 +3998,16 @@ impl HybridBinarizer { if xoffset > maxXOffset as u32 { xoffset = maxXOffset as u32; } - let mut sum = 0; - let mut min = 0xFF; - let mut max = 0; + let mut sum = 0u32; + let mut min = 0; + let mut max = 0xFF; let mut offset = yoffset * width + xoffset; for yy in 0..HybridBinarizer::BLOCK_SIZE { // for (int yy = 0, offset = yoffset * width + xoffset; yy < HybridBinarizer::BLOCK_SIZE; yy++, offset += width) { for xx in 0..HybridBinarizer::BLOCK_SIZE { // for (int xx = 0; xx < HybridBinarizer::BLOCK_SIZE; xx++) { let pixel = luminances[offset as usize + xx]; - sum += pixel; + sum += pixel as u32; // still looking for good contrast if pixel < min { min = pixel; @@ -4023,7 +4024,7 @@ impl HybridBinarizer { // for (yy++, offset += width; yy < HybridBinarizer::BLOCK_SIZE; yy++, offset += width) { for xx in 0..HybridBinarizer::BLOCK_SIZE { // for (int xx = 0; xx < BLOCK_SIZE; xx++) { - sum += luminances[offset as usize + xx]; + sum += luminances[offset as usize + xx] as u32; } offset += width; } @@ -4041,7 +4042,7 @@ impl HybridBinarizer { // // The default assumption is that the block is light/background. Since no estimate for // the level of dark pixels exists locally, use half the min for the block. - average = min / 2; + average = min as u32 / 2; if y > 0 && x > 0 { // Correct the "white background" assumption for blocks that have neighbors by comparing @@ -4051,16 +4052,16 @@ impl HybridBinarizer { // the boundaries is used for the interior. // The (min < bp) is arbitrary but works better than other heuristics that were tried. - let averageNeighborBlackPoint = (blackPoints[y as usize - 1][x as usize] + let average_neighbor_black_point = (blackPoints[y as usize - 1][x as usize] + (2 * blackPoints[y as usize][x as usize - 1]) + blackPoints[y as usize - 1][x as usize - 1]) / 4; - if (min < averageNeighborBlackPoint) { - average = averageNeighborBlackPoint; + if (min < average_neighbor_black_point) { + average = average_neighbor_black_point as u32; } } } - blackPoints[y as usize][x as usize] = average; + blackPoints[y as usize][x as usize] = average as u8; } } return blackPoints diff --git a/src/lib.rs b/src/lib.rs index f8f93ce..39e891a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,13 +1,15 @@ -mod aztec; -mod client; -mod common; +pub mod aztec; +pub mod client; +pub mod common; mod exceptions; -#[cfg(feature = "image")] -mod BufferedImageLuminanceSource; +pub use exceptions::Exceptions; #[cfg(feature = "image")] -pub use BufferedImageLuminanceSource::*; +mod buffered_image_luminance_source; + +#[cfg(feature = "image")] +pub use buffered_image_luminance_source::*; use crate::common::{BitArray, BitMatrix}; use exceptions::*; @@ -454,7 +456,7 @@ pub enum EncodeHintValue { * @author dswitkin@google.com (Daniel Switkin) * @see Reader#decode(BinaryBitmap,java.util.Map) */ -#[derive(Eq, PartialEq, Hash, Debug)] +#[derive(Eq, PartialEq, Hash, Debug, Clone, Copy)] pub enum DecodeHintType { /** * Unspecified, application-specific hint. Maps to an unspecified {@link Object}. @@ -558,7 +560,7 @@ pub enum DecodeHintType { * @see DecodeHintType#NEED_RESULT_POINT_CALLBACK */ pub type RXingResultPointCallback = fn(&RXingResultPoint); - +#[derive(Clone)] pub enum DecodeHintValue { /** * Unspecified, application-specific hint. Maps to an unspecified {@link Object}. @@ -671,6 +673,7 @@ pub trait Writer { * @throws WriterException if contents cannot be encoded legally in a format */ fn encode( + &self, contents: &str, format: &BarcodeFormat, width: i32, @@ -687,6 +690,7 @@ pub trait Writer { * @throws WriterException if contents cannot be encoded legally in a format */ fn encode_with_hints( + &self, contents: &str, format: &BarcodeFormat, width: i32, @@ -735,7 +739,7 @@ pub trait Reader { * @throws ChecksumException if a potential barcode is found but does not pass its checksum * @throws FormatException if a potential barcode is found but format is invalid */ - fn decode(image: &BinaryBitmap) -> Result; + fn decode(&self, image: &BinaryBitmap) -> Result; /** * Locates and decodes a barcode in some format within an image. This method also accepts @@ -752,6 +756,7 @@ pub trait Reader { * @throws FormatException if a potential barcode is found but format is invalid */ fn decode_with_hints( + &self, image: &BinaryBitmap, hints: &HashMap, ) -> Result; @@ -760,7 +765,7 @@ pub trait Reader { * Resets any internal state the implementation has after a decode, to prepare it * for reuse. */ - fn reset(); + fn reset(&self); } /* @@ -787,7 +792,7 @@ pub trait Reader { * * @author Sean Owen */ -#[derive(Eq, PartialEq, Hash)] +#[derive(Eq, PartialEq, Hash, Debug)] pub enum RXingResultMetadataType { /** * Unspecified, application-specific metadata. Maps to an unspecified {@link Object}. @@ -867,11 +872,32 @@ pub enum RXingResultMetadataType { SYMBOLOGY_IDENTIFIER, } +impl From for RXingResultMetadataType { + fn from(in_str: String) -> Self { + match in_str.as_str() { + "OTHER" => RXingResultMetadataType::OTHER, + "ORIENTATION" => RXingResultMetadataType::ORIENTATION, + "BYTE_SEGMENTS" => RXingResultMetadataType::BYTE_SEGMENTS, + "ERROR_CORRECTION_LEVEL"=> RXingResultMetadataType::ERROR_CORRECTION_LEVEL, + "ISSUE_NUMBER"=> RXingResultMetadataType::ISSUE_NUMBER, + "SUGGESTED_PRICE"=> RXingResultMetadataType::SUGGESTED_PRICE, + "POSSIBLE_COUNTRY"=> RXingResultMetadataType::POSSIBLE_COUNTRY, + "UPC_EAN_EXTENSION"=>RXingResultMetadataType::UPC_EAN_EXTENSION, + "PDF417_EXTRA_METADATA"=> RXingResultMetadataType::PDF417_EXTRA_METADATA, + "STRUCTURED_APPEND_SEQUENCE"=> RXingResultMetadataType::STRUCTURED_APPEND_SEQUENCE, + "STRUCTURED_APPEND_PARITY"=>RXingResultMetadataType::STRUCTURED_APPEND_PARITY, + "SYMBOLOGY_IDENTIFIER"=>RXingResultMetadataType::SYMBOLOGY_IDENTIFIER, + _ => RXingResultMetadataType::OTHER, + } + } +} + +#[derive(Debug,PartialEq, Eq)] pub enum RXingResultMetadataValue { /** * Unspecified, application-specific metadata. Maps to an unspecified {@link Object}. */ - OTHER, + OTHER(String), /** * Denotes the likely approximate orientation of the barcode in the image. This value diff --git a/test_resources/matrix_visualizer.html b/test_resources/matrix_visualizer.html index bb6d65b..693e147 100644 --- a/test_resources/matrix_visualizer.html +++ b/test_resources/matrix_visualizer.html @@ -11,6 +11,9 @@ .bg_color { background-color: lightblue; } + .super_small { + font-size: xx-small; + }