diff --git a/tests/PDF417BlackBox4TestCase.java b/tests/PDF417BlackBox4TestCase.java deleted file mode 100644 index 6314aba..0000000 --- a/tests/PDF417BlackBox4TestCase.java +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Copyright 2013 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.pdf417; - -import com.google.zxing.BarcodeFormat; -import com.google.zxing.BinaryBitmap; -import com.google.zxing.BufferedImageLuminanceSource; -import com.google.zxing.DecodeHintType; -import com.google.zxing.LuminanceSource; -import com.google.zxing.ReaderException; -import com.google.zxing.RXingResult; -import com.google.zxing.RXingResultMetadataType; -import com.google.zxing.common.AbstractBlackBoxTestCase; -import com.google.zxing.common.HybridBinarizer; -import com.google.zxing.common.TestRXingResult; - -import com.google.zxing.multi.MultipleBarcodeReader; -import org.junit.Test; - -import javax.imageio.ImageIO; - -import java.awt.image.BufferedImage; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Comparator; -import java.util.EnumMap; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.logging.Logger; - -/** - * This class tests Macro PDF417 barcode specific functionality. It ensures that information, which is split into - * several barcodes can be properly combined again to yield the original data content. - * - * @author Guenther Grau - */ -public final class PDF417BlackBox4TestCase extends AbstractBlackBoxTestCase { - private static final Logger log = Logger.getLogger(AbstractBlackBoxTestCase.class.getSimpleName()); - - private final MultipleBarcodeReader barcodeReader = new PDF417Reader(); - - private final List testRXingResults = new ArrayList<>(); - - public PDF417BlackBox4TestCase() { - super("src/test/resources/blackbox/pdf417-4", null, BarcodeFormat.PDF_417); - testRXingResults.add(new TestRXingResult(3, 3, 0, 0, 0.0f)); - } - - @Test - @Override - public void testBlackBox() throws IOException { - assertFalse(testRXingResults.isEmpty()); - - Map> imageFiles = getImageFileLists(); - int testCount = testRXingResults.size(); - - int[] passedCounts = new int[testCount]; - int[] tryHarderCounts = new int[testCount]; - - Path testBase = getTestBase(); - - for (Entry> testImageGroup : imageFiles.entrySet()) { - log.fine(String.format("Starting Image Group %s", testImageGroup.getKey())); - - String fileBaseName = testImageGroup.getKey(); - String expectedText; - Path expectedTextFile = testBase.resolve(fileBaseName + ".txt"); - if (Files.exists(expectedTextFile)) { - expectedText = readFileAsString(expectedTextFile, StandardCharsets.UTF_8); - } else { - expectedTextFile = testBase.resolve(fileBaseName + ".bin"); - assertTrue(Files.exists(expectedTextFile)); - expectedText = readFileAsString(expectedTextFile, StandardCharsets.ISO_8859_1); - } - - for (int x = 0; x < testCount; x++) { - List results = new ArrayList<>(); - for (Path imageFile : testImageGroup.getValue()) { - BufferedImage image = ImageIO.read(imageFile.toFile()); - float rotation = testRXingResults.get(x).getRotation(); - BufferedImage rotatedImage = rotateImage(image, rotation); - LuminanceSource source = new BufferedImageLuminanceSource(rotatedImage); - BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source)); - - try { - results.addAll(Arrays.asList(decode(bitmap, false))); - } catch (ReaderException ignored) { - // ignore - } - } - results.sort(Comparator.comparingInt((RXingResult r) -> getMeta(r).getSegmentIndex())); - StringBuilder resultText = new StringBuilder(); - String fileId = null; - for (RXingResult result : results) { - PDF417RXingResultMetadata resultMetadata = getMeta(result); - assertNotNull("resultMetadata", resultMetadata); - if (fileId == null) { - fileId = resultMetadata.getFileId(); - } - assertEquals("FileId", fileId, resultMetadata.getFileId()); - resultText.append(result.getText()); - } - assertEquals("ExpectedText", expectedText, resultText.toString()); - passedCounts[x]++; - tryHarderCounts[x]++; - } - } - - // Print the results of all tests first - int totalFound = 0; - int totalMustPass = 0; - - int numberOfTests = imageFiles.keySet().size(); - for (int x = 0; x < testRXingResults.size(); x++) { - TestRXingResult testRXingResult = testRXingResults.get(x); - log.info(String.format("Rotation %d degrees:", (int) testRXingResult.getRotation())); - log.info(String.format(" %d of %d images passed (%d required)", passedCounts[x], numberOfTests, - testRXingResult.getMustPassCount())); - log.info(String.format(" %d of %d images passed with try harder (%d required)", tryHarderCounts[x], - numberOfTests, testRXingResult.getTryHarderCount())); - totalFound += passedCounts[x] + tryHarderCounts[x]; - totalMustPass += testRXingResult.getMustPassCount() + testRXingResult.getTryHarderCount(); - } - - int totalTests = numberOfTests * testCount * 2; - log.info(String.format("Decoded %d images out of %d (%d%%, %d required)", totalFound, totalTests, totalFound * - 100 / - totalTests, totalMustPass)); - if (totalFound > totalMustPass) { - log.warning(String.format("+++ Test too lax by %d images", totalFound - totalMustPass)); - } else if (totalFound < totalMustPass) { - log.warning(String.format("--- Test failed by %d images", totalMustPass - totalFound)); - } - - // Then run through again and assert if any failed - for (int x = 0; x < testCount; x++) { - TestRXingResult testRXingResult = testRXingResults.get(x); - String label = "Rotation " + testRXingResult.getRotation() + " degrees: Too many images failed"; - assertTrue(label, passedCounts[x] >= testRXingResult.getMustPassCount()); - assertTrue("Try harder, " + label, tryHarderCounts[x] >= testRXingResult.getTryHarderCount()); - } - } - - private static PDF417RXingResultMetadata getMeta(RXingResult result) { - return result.getRXingResultMetadata() == null ? null : (PDF417RXingResultMetadata) result.getRXingResultMetadata().get( - RXingResultMetadataType.PDF417_EXTRA_METADATA); - } - - private RXingResult[] decode(BinaryBitmap source, boolean tryHarder) throws ReaderException { - Map hints = new EnumMap<>(DecodeHintType.class); - if (tryHarder) { - hints.put(DecodeHintType.TRY_HARDER, Boolean.TRUE); - } - - return barcodeReader.decodeMultiple(source, hints); - } - - private Map> getImageFileLists() throws IOException { - Map> result = new HashMap<>(); - for (Path file : getImageFiles()) { - String testImageFileName = file.getFileName().toString(); - String fileBaseName = testImageFileName.substring(0, testImageFileName.indexOf('-')); - List files = result.computeIfAbsent(fileBaseName, k -> new ArrayList<>()); - files.add(file); - } - return result; - } - -} diff --git a/tests/PDF417BlackBox4TestCase.rs b/tests/PDF417BlackBox4TestCase.rs new file mode 100644 index 0000000..3805379 --- /dev/null +++ b/tests/PDF417BlackBox4TestCase.rs @@ -0,0 +1,38 @@ +/* + * Copyright 2013 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. + */ +mod common; + +use rxing::pdf417::PDF417Reader; + +/** + * This class tests Macro PDF417 barcode specific functionality. It ensures that information, which is split into + * several barcodes can be properly combined again to yield the original data content. + * + * @author Guenther Grau + */ +#[test] +fn pdf417_black_box4_test_case() { + let mut tester = common::PDF417MultiImageSpanAbstractBlackBoxTestCase::new( + "test_resources/blackbox/pdf417-4", + PDF417Reader::default(), + rxing::BarcodeFormat::PDF_417, + ); + + // super("src/test/resources/blackbox/pdf417-4", null, BarcodeFormat.PDF_417); + tester.add_test_complex(3, 3, 0, 0, 0.0); + + tester.test_black_box() +} diff --git a/tests/common/mod.rs b/tests/common/mod.rs index 06f48dd..a9d2073 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -1,5 +1,7 @@ mod abstract_black_box_test_case; +mod pdf_417_multiimage_span; mod test_result; pub use abstract_black_box_test_case::*; +pub use pdf_417_multiimage_span::*; pub use test_result::*; diff --git a/tests/common/pdf_417_multiimage_span.rs b/tests/common/pdf_417_multiimage_span.rs new file mode 100644 index 0000000..3cbbbc1 --- /dev/null +++ b/tests/common/pdf_417_multiimage_span.rs @@ -0,0 +1,797 @@ +/* + * Copyright 2008 ZXing authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use std::{ + collections::HashMap, + fs::{read_dir, read_to_string, File}, + io::Read, + path::{Path, PathBuf}, + rc::Rc, +}; + +use encoding::Encoding; +use rxing::{ + common::HybridBinarizer, multi::MultipleBarcodeReader, pdf417::PDF417RXingResultMetadata, + BarcodeFormat, BinaryBitmap, BufferedImageLuminanceSource, DecodeHintType, DecodeHintValue, + Exceptions, RXingResult, RXingResultMetadataType, RXingResultMetadataValue, Reader, +}; + +use super::TestRXingResult; + +/** + * @author Sean Owen + * @author dswitkin@google.com (Daniel Switkin) + */ +pub struct PDF417MultiImageSpanAbstractBlackBoxTestCase { + test_base: Box, + barcode_reader: T, + expected_format: BarcodeFormat, + test_rxing_results: Vec, + hints: HashMap, +} + +impl PDF417MultiImageSpanAbstractBlackBoxTestCase { + pub fn build_test_base(test_base_path_suffix: &str) -> Box { + // A little workaround to prevent aggravation in my IDE + let test_base = Path::new(test_base_path_suffix); + // if !testBase.exists() { + // // try starting with 'core' since the test base is often given as the project root + // testBase = Paths.get("core").resolve(testBasePathSuffix); + // } + test_base.to_owned().into() + } + + pub fn new( + test_base_path_suffix: &str, + barcode_reader: T, + expected_format: BarcodeFormat, + ) -> Self { + Self { + test_base: Self::build_test_base(test_base_path_suffix), + barcode_reader, + expected_format, + test_rxing_results: Vec::new(), + hints: HashMap::new(), + } + } + + pub fn get_test_base(&self) -> &Box { + &self.test_base + } + + pub fn add_test(&mut self, must_pass_count: u32, try_harder_count: u32, rotation: f32) { + self.add_test_complex(must_pass_count, try_harder_count, 0, 0, rotation); + } + + pub fn add_hint(&mut self, hint: DecodeHintType, value: DecodeHintValue) { + self.hints.insert(hint, value); + } + + /** + * Adds a new test for the current directory of images. + * + * @param mustPassCount The number of images which must decode for the test to pass. + * @param tryHarderCount The number of images which must pass using the try harder flag. + * @param maxMisreads Maximum number of images which can fail due to successfully reading the wrong contents + * @param maxTryHarderMisreads Maximum number of images which can fail due to successfully + * reading the wrong contents using the try harder flag + * @param rotation The rotation in degrees clockwise to use for this test. + */ + pub fn add_test_complex( + &mut self, + must_pass_count: u32, + try_harder_count: u32, + max_misreads: u32, + max_try_harder_misreads: u32, + rotation: f32, + ) { + self.test_rxing_results.push(TestRXingResult::new( + must_pass_count, + try_harder_count, + max_misreads, + max_try_harder_misreads, + rotation, + )); + } + + pub fn get_image_files(&self) -> Vec { + assert!( + self.test_base.exists(), + "Please download and install test images, and run from the 'core' directory" + ); + // let paths = Vec::new(); + let path_search = read_dir(&self.test_base); + const POSSIBLE_EXTENSIONS: &str = "jpg,jpeg,gif,png,JPG,JPEG,GIF,PNG"; + + let mut paths = path_search + .unwrap() + .into_iter() + .filter(|r| r.is_ok()) // Get rid of Err variants for Result + .map(|r| r.unwrap().path()) // This is safe, since we only have the Ok variants + .filter(|r| r.is_file()) // Filter out non-folders + .filter(|r| POSSIBLE_EXTENSIONS.contains(r.extension().unwrap().to_str().unwrap())) + // .map(|r| r.into_boxed_path()) + .collect::>(); + + paths.sort(); + + paths + } + + pub fn get_reader(&self) -> &T { + &self.barcode_reader + } + + pub fn test_black_box(&mut self) { + assert!(!self.test_rxing_results.is_empty()); + + let imageFiles = self.getImageFileLists().expect("images"); + let testCount = self.test_rxing_results.len(); + + let mut passedCounts = vec![0; testCount]; + let mut tryHarderCounts = vec![0; testCount]; + + let testBase = self.get_test_base().clone(); + + for (name, files) in &imageFiles { + // for (Entry> testImageGroup : imageFiles.entrySet()) { + log::fine(format!("Starting Image Group {}", name)); + + let fileBaseName = name; //testImageGroup.getKey(); + // let expectedText : String; + let mut expectedTextFile = testBase.clone().to_path_buf(); + expectedTextFile.push(fileBaseName); + expectedTextFile.set_extension("txt"); + // let expectedTextFile = testBase.resolve(fileBaseName + ".txt"); + let expectedText = if expectedTextFile.exists() { + Self::read_file_as_string(expectedTextFile) + } else { + let mut new_path = self.test_base.clone().to_path_buf(); + new_path.push(fileBaseName); + new_path.set_extension("bin"); + //expectedTextFile = testBase.resolve(fileBaseName + ".bin"); + assert!(new_path.exists()); + Self::read_file_as_string(new_path) + } + .unwrap(); + + for x in 0..testCount { + // for (int x = 0; x < testCount; x++) { + let mut results = Vec::new(); + for imageFile in files { + // for (Path imageFile : testImageGroup.getValue()) { + let image = image::open(imageFile).unwrap(); + let rotation: f32 = self.test_rxing_results.get(x).expect("ok").get_rotation(); + let rotatedImage = Self::rotate_image(&image, rotation); + let source = BufferedImageLuminanceSource::new(rotatedImage); + let bitmap = BinaryBitmap::new(Rc::new(HybridBinarizer::new(Box::new(source)))); + + if let Ok(res) = Self::decode_pdf417(&bitmap, false, &mut self.barcode_reader) { + for r in res { + results.push(r); + } + } + // try { + // results.addAll(Arrays.asList(decode(bitmap, false))); + // } catch (ReaderException ignored) { + // // ignore + // } + } + // results.sort(Comparator.comparingInt((RXingResult r) -> getMeta(r).getSegmentIndex())); + // results.sort(); + let mut resultText = String::new(); //new StringBuilder(); + let mut fileId: Option = None; + for result in results { + // for (RXingResult result : results) { + let resultMetadata = Self::getMeta(&result); + assert!(resultMetadata.is_some(), "resultMetadata"); + if fileId.is_none() { + fileId = Some(resultMetadata.as_ref().unwrap().getFileId().to_owned()); + } + assert_eq!( + fileId, + Some(resultMetadata.as_ref().unwrap().getFileId().to_owned()), + "FileId" + ); + resultText.push_str(result.getText()); + } + assert_eq!(expectedText, resultText, "ExpectedText"); + passedCounts[x] += 1; + tryHarderCounts[x] += 1; + } + } + + // Print the results of all tests first + let mut totalFound = 0; + let mut totalMustPass = 0; + + let numberOfTests = imageFiles.len(); //imageFiles.keySet().size(); + for x in 0..self.test_rxing_results.len() { + // for (int x = 0; x < testRXingResults.size(); x++) { + let testRXingResult = self.test_rxing_results.get(x).expect("ok"); + log::info(format!( + "Rotation {} degrees:", + testRXingResult.get_rotation() + )); + log::info(format!( + " {} of {} images passed ({} required)", + passedCounts[x], + numberOfTests, + testRXingResult.get_must_pass_count() + )); + log::info(format!( + " {} of {} images passed with try harder ({} required)", + tryHarderCounts[x], + numberOfTests, + testRXingResult.get_try_harder_count() + )); + totalFound += passedCounts[x] + tryHarderCounts[x]; + totalMustPass += + testRXingResult.get_must_pass_count() + testRXingResult.get_try_harder_count(); + } + + let totalTests = numberOfTests * testCount * 2; + log::info(format!( + "Decoded {} images out of {} ({}%, {} required)", + totalFound, + totalTests, + totalFound * 100 / totalTests, + totalMustPass + )); + if totalFound > totalMustPass as usize { + log::warning(format!( + "+++ Test too lax by {} images", + totalFound - totalMustPass as usize + )); + } else if totalFound < totalMustPass as usize { + log::warning(format!( + "--- Test failed by {} images", + totalMustPass as usize - totalFound + )); + } + + // Then run through again and assert if any failed + for x in 0..testCount { + // for (int x = 0; x < testCount; x++) { + let testRXingResult = self.test_rxing_results.get(x).expect("ok"); + // let label = "Rotation " + testRXingResult.getRotation() + " degrees: Too many images failed"; + assert!( + passedCounts[x] >= testRXingResult.get_must_pass_count() as usize, + "Rotation {} degrees: Too many images failed", + testRXingResult.get_rotation() + ); + assert!( + tryHarderCounts[x] >= testRXingResult.get_try_harder_count() as usize, + "Try harder, Rotation {} degrees: Too many images failed", + testRXingResult.get_rotation() + ); + } + } + + pub fn test_black_box_old(&mut self) { + assert!(!self.test_rxing_results.is_empty()); + + let image_files = self.get_image_files(); + let test_count = self.test_rxing_results.len(); + + let mut passed_counts = vec![0usize; test_count]; + let mut misread_counts = vec![0usize; test_count]; + let mut try_harder_counts = vec![0usize; test_count]; + let mut try_harder_misread_counts = vec![0usize; test_count]; + + for test_image in &image_files { + // for (Path testImage : imageFiles) { + log::info(format!("Starting {}", test_image.to_string_lossy())); + + let image = image::open(test_image).unwrap(); //ImageIO.read(testImage.toFile()); + + //let testImageFileName = testImage.getFileName().toString(); + let file_base_name = test_image.file_stem().unwrap(); + //let expectedTextFile = self.testBase.resolve(fileBaseName + ".txt"); + let mut expected_text_file = test_image.clone(); + expected_text_file.set_extension("txt"); + let expected_text = if expected_text_file.exists() { + Self::read_file_as_string(expected_text_file) + } else { + let mut new_path = self.test_base.clone().to_path_buf(); + new_path.push(file_base_name); + new_path.set_extension("bin"); + //expectedTextFile = testBase.resolve(fileBaseName + ".bin"); + assert!(new_path.exists()); + Self::read_file_as_string(new_path) + } + .unwrap(); + + let mut expected_metadata_file: PathBuf = self.test_base.clone().to_path_buf(); + expected_metadata_file.push(format!("{}.metadata", file_base_name.to_str().unwrap())); + expected_metadata_file.set_extension("txt"); + let expected_metadata_unfinished = if expected_metadata_file.exists() { + java_properties::read( + std::fs::File::open(expected_metadata_file) + .expect("file exists, we already know that"), + ) + .expect("valid java properties file") + // try (BufferedReader reader = Files.newBufferedReader(expectedMetadataFile, StandardCharsets.UTF_8)) { + // expectedMetadata.load(reader); + // } + } else { + HashMap::new() + }; + let expected_metadata = HashMap::new(); + for (k, v) in expected_metadata_unfinished { + let new_k = RXingResultMetadataType::from(k); + let _new_v = match new_k { + RXingResultMetadataType::OTHER => RXingResultMetadataValue::OTHER(v), + RXingResultMetadataType::ORIENTATION => { + RXingResultMetadataValue::Orientation(v.parse().unwrap_or_default()) + } + RXingResultMetadataType::BYTE_SEGMENTS => { + RXingResultMetadataValue::ByteSegments(vec![v.into_bytes()]) + } + RXingResultMetadataType::ERROR_CORRECTION_LEVEL => { + RXingResultMetadataValue::ErrorCorrectionLevel(v) + } + RXingResultMetadataType::ISSUE_NUMBER => { + RXingResultMetadataValue::IssueNumber(v.parse().unwrap_or_default()) + } + RXingResultMetadataType::SUGGESTED_PRICE => { + RXingResultMetadataValue::SuggestedPrice(v) + } + RXingResultMetadataType::POSSIBLE_COUNTRY => { + RXingResultMetadataValue::PossibleCountry(v) + } + RXingResultMetadataType::UPC_EAN_EXTENSION => { + RXingResultMetadataValue::UpcEanExtension(v) + } + RXingResultMetadataType::PDF417_EXTRA_METADATA => { + RXingResultMetadataValue::Pdf417ExtraMetadata(Rc::new( + PDF417RXingResultMetadata::default(), + )) + } + RXingResultMetadataType::STRUCTURED_APPEND_SEQUENCE => { + RXingResultMetadataValue::StructuredAppendSequence( + v.parse().unwrap_or_default(), + ) + } + RXingResultMetadataType::STRUCTURED_APPEND_PARITY => { + RXingResultMetadataValue::StructuredAppendParity( + v.parse().unwrap_or_default(), + ) + } + RXingResultMetadataType::SYMBOLOGY_IDENTIFIER => { + RXingResultMetadataValue::SymbologyIdentifier(v) + } + }; + } + + for x in 0..test_count { + // for (int x = 0; x < testCount; x++) { + let rotation = self.test_rxing_results.get(x).unwrap().get_rotation(); + let rotated_image = Self::rotate_image(&image, rotation); + let source = BufferedImageLuminanceSource::new(rotated_image); + let bitmap = BinaryBitmap::new(Rc::new(HybridBinarizer::new(Box::new(source)))); + + // if file_base_name == "15" { + // let mut f = File::create("test_file_output.txt").unwrap(); + // write!(f,"{}", bitmap.getBlackMatrix().unwrap()); + // drop(f); + // Self::rotate_image(&image, rotation).save("test_image.png").unwrap(); + // } + + if let Ok(decoded) = + self.decode(&bitmap, rotation, &expected_text, &expected_metadata, false) + { + if decoded { + passed_counts[x] += 1; + } else { + misread_counts[x] += 1; + } + } else { + log::fine(format!("could not read at rotation {}", rotation)); + } + // try { + // if (decode(bitmap, rotation, expectedText, expectedMetadata, false)) { + // passedCounts[x]+=1; + // } else { + // misreadCounts[x]+=1; + // } + // } catch (ReaderException ignored) { + // log::fine(format!("could not read at rotation {}", rotation)); + // } + if let Ok(decoded) = + self.decode(&bitmap, rotation, &expected_text, &expected_metadata, true) + { + if decoded { + try_harder_counts[x] += 1; + } else { + try_harder_misread_counts[x] += 1; + } + } else { + log::fine(format!("could not read at rotation {} w/TH", rotation)); + } + // try { + // if (decode(bitmap, rotation, expectedText, expectedMetadata, true)) { + // tryHarderCounts[x]+=1; + // } else { + // tryHarderMisreadCounts[x]+=1; + // } + // } catch (ReaderException ignored) { + // log::fine(format!("could not read at rotation {} w/TH", rotation)); + // } + } + } + + // Print the results of all tests first + let mut total_found = 0; + let mut total_must_pass = 0; + let mut total_misread = 0; + let mut total_max_misread = 0; + + for x in 0..self.test_rxing_results.len() { + // for (int x = 0; x < testRXingResults.size(); x++) { + let test_rxing_result = self.test_rxing_results.get(x).unwrap(); + log::info(format!( + "Rotation {} degrees:", + test_rxing_result.get_rotation() + )); + log::info(format!( + " {} of {} images passed ({} required)", + passed_counts[x], + image_files.len(), + test_rxing_result.get_must_pass_count() + )); + let mut failed = image_files.len() - passed_counts[x]; + log::info(format!( + " {} failed due to misreads, {} not detected", + misread_counts[x], + failed - misread_counts[x] + )); + log::info(format!( + " {} of {} images passed with try harder ({} required)", + try_harder_counts[x], + image_files.len(), + test_rxing_result.get_try_harder_count() + )); + failed = image_files.len() - try_harder_counts[x]; + log::info(format!( + " {} failed due to misreads, {} not detected", + try_harder_misread_counts[x], + failed - try_harder_misread_counts[x] + )); + total_found += passed_counts[x] + try_harder_counts[x]; + total_must_pass += + test_rxing_result.get_must_pass_count() + test_rxing_result.get_try_harder_count(); + total_misread += misread_counts[x] + try_harder_misread_counts[x]; + total_max_misread += test_rxing_result.get_max_misreads() + + test_rxing_result.get_max_try_harder_misreads(); + } + + let total_tests = image_files.len() * test_count * 2; + log::info(format!( + "Decoded {} images out of {} ({}, {} required)", + total_found, + total_tests, + total_found * 100 / total_tests, + total_must_pass + )); + if total_found > total_must_pass as usize { + log::warning(format!( + "+++ Test too lax by {} images", + total_found - total_must_pass as usize + )); + } else if total_found < total_must_pass as usize { + log::warning(format!( + "--- Test failed by {} images", + total_must_pass as usize - total_found + )); + } + + if total_misread < total_max_misread as usize { + log::warning(format!( + "+++ Test expects too many misreads by {} images", + total_max_misread as usize - total_misread + )); + } else if total_misread > total_max_misread as usize { + log::warning(format!( + "--- Test had too many misreads by {} images", + total_misread - total_max_misread as usize + )); + } + + // Then run through again and assert if any failed + for x in 0..test_count { + // for (int x = 0; x < testCount; x++) { + let test_rxing_result = self.test_rxing_results.get(x).unwrap(); + let label = format!( + "Rotation {} degrees: Too many images failed", + test_rxing_result.get_rotation() + ); + assert!( + passed_counts[x] >= test_rxing_result.get_must_pass_count() as usize, + "{}", + label + ); + assert!( + try_harder_counts[x] >= test_rxing_result.get_try_harder_count() as usize, + "Try harder, {}", + label, + ); + let label = format!( + "Rotation {} degrees: Too many images misread", + test_rxing_result.get_rotation() + ); + assert!( + misread_counts[x] <= test_rxing_result.get_max_misreads() as usize, + "{}", + label + ); + assert!( + try_harder_misread_counts[x] + <= test_rxing_result.get_max_try_harder_misreads() as usize, + "Try harder, {}", + label + ); + } + } + + fn decode( + &mut self, + source: &BinaryBitmap, + rotation: f32, + expected_text: &str, + expected_metadata: &HashMap, + try_harder: bool, + ) -> Result { + let suffix = format!( + " ({}rotation: {})", + if try_harder { "try harder, " } else { "" }, + rotation + ); + + let mut hints = self.hints.clone(); + if try_harder { + hints.insert(DecodeHintType::TRY_HARDER, DecodeHintValue::TryHarder(true)); + // hints.put(DecodeHintType.TRY_HARDER, Boolean.TRUE); + } + + // Try in 'pure' mode mostly to exercise PURE_BARCODE code paths for exceptions; + // not expected to pass, generally + // let mut result = None; + let mut pure_hints = HashMap::new(); + pure_hints.insert( + DecodeHintType::PURE_BARCODE, + DecodeHintValue::PureBarcode(true), + ); + let mut result = if let Ok(res) = self.barcode_reader.decode_with_hints(source, &pure_hints) + { + Some(res) + } else { + None + }; + + if result.is_none() { + result = Some(self.barcode_reader.decode_with_hints(source, &hints)?) + } + + let result = result.unwrap(); + + if &self.expected_format != result.getBarcodeFormat() { + log::info(format!( + "Format mismatch: expected '{:?}' but got '{:?}'{}", + self.expected_format, + result.getBarcodeFormat(), + suffix + )); + return Ok(false); + } + + let result_text = result.getText(); + if expected_text != result_text { + log::info(format!( + "Content mismatch: expected '{}' but got '{}'{}", + expected_text, result_text, suffix + )); + return Ok(false); + } + + let result_metadata = result.getRXingResultMetadata(); + for (key, value) in expected_metadata { + // let key = RXingResultMetadataType.valueOf(metadatum.getKey().toString()); + // Object expectedValue = metadatum.getValue(); + let actual_value = result_metadata.get(key); + if actual_value.is_none() || !(value == actual_value.unwrap()) { + log::info(format!( + "Metadata mismatch for key '{:?}': expected '{:?}' but got '{:?}'", + key, value, actual_value + )); + return Ok(false); + } + } + + Ok(true) + } + + fn read_file_as_string(file: PathBuf) -> Result { + let string_contents = if let Some(ext) = file.extension() { + if ext == "bin" { + let mut buffer: Vec = Vec::new(); + File::open(&file)?.read_to_end(&mut buffer)?; + encoding::all::ISO_8859_1 + .decode(&buffer, encoding::DecoderTrap::Replace) + .expect("decode") + } else { + read_to_string(&file).expect("ok") + } + } else { + "".to_owned() + }; + // let string_contents = read_to_string(&file)?; //new String(Files.readAllBytes(file), charset); + if string_contents.ends_with('\n') { + log::info(format!("String contents of file {} end with a newline. This may not be intended and cause a test failure",file.to_string_lossy())); + } + Ok(string_contents) + } + + fn rotate_image(original: &image::DynamicImage, degrees: f32) -> image::DynamicImage { + if degrees == 0.0 { + return original.clone(); + } + + // switch (original.getType()) { + // case BufferedImage.TYPE_BYTE_INDEXED: + // case BufferedImage.TYPE_BYTE_BINARY: + // BufferedImage argb = new BufferedImage(original.getWidth(), + // original.getHeight(), + // BufferedImage.TYPE_INT_ARGB); + // Graphics g = argb.createGraphics(); + // g.drawImage(original, 0, 0, null); + // g.dispose(); + // original = argb; + // break; + // } + + if degrees == 90.0 { + original.rotate90() + } else if degrees == 180.0 { + original.rotate180() + } else if degrees == 270.0 { + original.rotate270() + } else { + let radians = degrees.to_radians(); + + { + use image::DynamicImage; + use image::Luma; + use imageproc::geometric_transformations::*; + + let i = rotate_about_center( + &original.to_luma8(), + radians, + Interpolation::Nearest, + Luma([u8::MAX / 2; 1]), + ); + + DynamicImage::from(i) + } + } + + // let radians = degrees.to_radians(); + + // { + // use image::DynamicImage; + // use image::Luma; + // use imageproc::geometric_transformations::*; + + // let i = rotate_about_center( + // &original.to_luma8(), + // radians, + // Interpolation::Nearest, + // Luma([u8::MAX / 2; 1]), + // ); + + // DynamicImage::from(i) + // } + + // // Transform simply to find out the new bounding box (don't actually run the image through it) + // AffineTransform at = new AffineTransform(); + // at.rotate(radians, original.getWidth() / 2.0, original.getHeight() / 2.0); + // BufferedImageOp op = new AffineTransformOp(at, AffineTransformOp.TYPE_BICUBIC); + + // RectangularShape r = op.getBounds2D(original); + // int width = (int) Math.ceil(r.getWidth()); + // int height = (int) Math.ceil(r.getHeight()); + + // // Real transform, now that we know the size of the new image and how to translate after we rotate + // // to keep it centered + // at = new AffineTransform(); + // at.rotate(radians, width / 2.0, height / 2.0); + // at.translate((width - original.getWidth()) / 2.0, + // (height - original.getHeight()) / 2.0); + // op = new AffineTransformOp(at, AffineTransformOp.TYPE_BICUBIC); + + // return op.filter(original, new BufferedImage(width, height, original.getType())); + } + + fn getMeta(result: &RXingResult) -> Option> { + if let Some(RXingResultMetadataValue::Pdf417ExtraMetadata(mtd)) = result + .getRXingResultMetadata() + .get(&RXingResultMetadataType::PDF417_EXTRA_METADATA) + { + /*(PDF417RXingResultMetadata)*/ + Some(mtd.clone()) + } else { + None + } + } + + fn decode_pdf417( + source: &BinaryBitmap, + tryHarder: bool, + barcodeReader: &mut T, + ) -> Result, Exceptions> { + let mut hints = HashMap::new(); //new EnumMap<>(DecodeHintType.class); + if tryHarder { + hints.insert(DecodeHintType::TRY_HARDER, DecodeHintValue::TryHarder(true)); + } + + barcodeReader.decodeMultipleWithHints(source, &hints) + } + + fn getImageFileLists(&self) -> Result>, std::io::Error> { + let mut result: HashMap> = HashMap::new(); + for file in self.get_image_files() { + let testImageFileName = file + .file_name() + .expect("file name exists") + .to_str() + .unwrap(); + let fileBaseName = if let Some(pos) = testImageFileName.find('-') { + &testImageFileName[..pos] + } else { + testImageFileName + }; + if !result.contains_key(fileBaseName) { + result.insert(fileBaseName.to_owned(), Vec::new()); + } + result.get_mut(fileBaseName).as_mut().unwrap().push(file); + + // String fileBaseName = testImageFileName.substring(0, testImageFileName.indexOf('-')); + //let files = result.computeIfAbsent(fileBaseName, k -> new ArrayList<>()); + // files.add(file); + // result.insert(fileBaseName, vec![]); + } + + Ok(result) + } +} + +mod log { + pub fn info(data: String) { + prn("INFO", data) + } + + pub fn fine(data: String) { + prn("FINE", data) + } + + pub fn warning(data: String) { + prn("WARN", data) + } + + fn prn(level: &str, data: String) { + println!("{} :: {}", level, data) + } +}