diff --git a/one-d-reader-derive/src/lib.rs b/one-d-reader-derive/src/lib.rs index 97c5a61..c0bf14c 100644 --- a/one-d-reader-derive/src/lib.rs +++ b/one-d-reader-derive/src/lib.rs @@ -99,7 +99,7 @@ fn impl_ean_reader_macro(ast: &syn::DeriveInput) -> TokenStream { row: &crate::common::BitArray, hints: &crate::DecodingHintDictionary, ) -> Result { - self.decodeRowWithGuardRange(rowNumber, row, &Self::findStartGuardPattern(row)?, hints) + self.decodeRowWithGuardRange(rowNumber, row, &self.findStartGuardPattern(row)?, hints) } } }; diff --git a/src/oned/MultiFormatUPCEANReader.java b/src/oned/MultiFormatUPCEANReader.java deleted file mode 100644 index 36beb07..0000000 --- a/src/oned/MultiFormatUPCEANReader.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * 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. - */ - -package com.google.zxing.oned; - -import com.google.zxing.BarcodeFormat; -import com.google.zxing.DecodeHintType; -import com.google.zxing.NotFoundException; -import com.google.zxing.Reader; -import com.google.zxing.ReaderException; -import com.google.zxing.RXingResult; -import com.google.zxing.common.BitArray; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Map; - -/** - *

A reader that can read all available UPC/EAN formats. If a caller wants to try to - * read all such formats, it is most efficient to use this implementation rather than invoke - * individual readers.

- * - * @author Sean Owen - */ -public final class MultiFormatUPCEANReader extends OneDReader { - - private static final UPCEANReader[] EMPTY_READER_ARRAY = new UPCEANReader[0]; - - private final UPCEANReader[] readers; - - public MultiFormatUPCEANReader(Map hints) { - @SuppressWarnings("unchecked") - Collection possibleFormats = hints == null ? null : - (Collection) hints.get(DecodeHintType.POSSIBLE_FORMATS); - Collection readers = new ArrayList<>(); - if (possibleFormats != null) { - if (possibleFormats.contains(BarcodeFormat.EAN_13)) { - readers.add(new EAN13Reader()); - } else if (possibleFormats.contains(BarcodeFormat.UPC_A)) { - readers.add(new UPCAReader()); - } - if (possibleFormats.contains(BarcodeFormat.EAN_8)) { - readers.add(new EAN8Reader()); - } - if (possibleFormats.contains(BarcodeFormat.UPC_E)) { - readers.add(new UPCEReader()); - } - } - if (readers.isEmpty()) { - readers.add(new EAN13Reader()); - // UPC-A is covered by EAN-13 - readers.add(new EAN8Reader()); - readers.add(new UPCEReader()); - } - this.readers = readers.toArray(EMPTY_READER_ARRAY); - } - - @Override - public RXingResult decodeRow(int rowNumber, - BitArray row, - Map hints) throws NotFoundException { - // Compute this location once and reuse it on multiple implementations - int[] startGuardPattern = UPCEANReader.findStartGuardPattern(row); - for (UPCEANReader reader : readers) { - try { - RXingResult result = reader.decodeRow(rowNumber, row, startGuardPattern, hints); - // Special case: a 12-digit code encoded in UPC-A is identical to a "0" - // followed by those 12 digits encoded as EAN-13. Each will recognize such a code, - // UPC-A as a 12-digit string and EAN-13 as a 13-digit string starting with "0". - // Individually these are correct and their readers will both read such a code - // and correctly call it EAN-13, or UPC-A, respectively. - // - // In this case, if we've been looking for both types, we'd like to call it - // a UPC-A code. But for efficiency we only run the EAN-13 decoder to also read - // UPC-A. So we special case it here, and convert an EAN-13 result to a UPC-A - // result if appropriate. - // - // But, don't return UPC-A if UPC-A was not a requested format! - boolean ean13MayBeUPCA = - result.getBarcodeFormat() == BarcodeFormat.EAN_13 && - result.getText().charAt(0) == '0'; - @SuppressWarnings("unchecked") - Collection possibleFormats = - hints == null ? null : (Collection) hints.get(DecodeHintType.POSSIBLE_FORMATS); - boolean canReturnUPCA = possibleFormats == null || possibleFormats.contains(BarcodeFormat.UPC_A); - - if (ean13MayBeUPCA && canReturnUPCA) { - // Transfer the metadata across - RXingResult resultUPCA = new RXingResult(result.getText().substring(1), - result.getRawBytes(), - result.getRXingResultPoints(), - BarcodeFormat.UPC_A); - resultUPCA.putAllMetadata(result.getRXingResultMetadata()); - return resultUPCA; - } - return result; - } catch (ReaderException ignored) { - // continue - } - } - - throw NotFoundException.getNotFoundInstance(); - } - - @Override - public void reset() { - for (Reader reader : readers) { - reader.reset(); - } - } - -} diff --git a/src/oned/code_128_reader.rs b/src/oned/code_128_reader.rs index b5177af..5519bd8 100644 --- a/src/oned/code_128_reader.rs +++ b/src/oned/code_128_reader.rs @@ -39,7 +39,7 @@ impl OneDReader for Code128Reader { let mut symbologyModifier = 0; - let startPatternInfo = Self::findStartPattern(row)?; + let startPatternInfo = self.findStartPattern(row)?; let startCode = startPatternInfo[2] as u8; let mut rawCodes: Vec = Vec::with_capacity(20); //new ArrayList<>(20); @@ -78,7 +78,7 @@ impl OneDReader for Code128Reader { lastCode = code; // Decode another code from image - code = Self::decodeCode(row, &mut counters, nextStart)?; + code = self.decodeCode(row, &mut counters, nextStart)?; rawCodes.push(code); @@ -363,7 +363,7 @@ impl OneDReader for Code128Reader { } } impl Code128Reader { - fn findStartPattern(row: &BitArray) -> Result<[usize; 3], Exceptions> { + fn findStartPattern(&self, row: &BitArray) -> Result<[usize; 3], Exceptions> { let width = row.getSize(); let rowOffset = row.getNextSet(0); @@ -383,7 +383,7 @@ impl Code128Reader { let mut bestMatch = -1_isize; for startCode in CODE_START_A..=CODE_START_C { // for (int startCode = CODE_START_A; startCode <= CODE_START_C; startCode++) { - let variance = Self::patternMatchVariance( + let variance = self.patternMatchVariance( &counters, &CODE_PATTERNS[startCode as usize], MAX_INDIVIDUAL_VARIANCE, @@ -423,17 +423,18 @@ impl Code128Reader { } fn decodeCode( + &self, row: &BitArray, counters: &mut [u32; 6], rowOffset: usize, ) -> Result { - Self::recordPattern(row, rowOffset, counters)?; + self.recordPattern(row, rowOffset, counters)?; let mut bestVariance = MAX_AVG_VARIANCE; // worst variance we'll accept let mut bestMatch = -1_isize; for d in 0..CODE_PATTERNS.len() { // for (int d = 0; d < CODE_PATTERNS.len(); d++) { let pattern = &CODE_PATTERNS[d]; - let variance = Self::patternMatchVariance(counters, &pattern, MAX_INDIVIDUAL_VARIANCE); + let variance = self.patternMatchVariance(counters, &pattern, MAX_INDIVIDUAL_VARIANCE); if variance < bestVariance { bestVariance = variance; bestMatch = d as isize; diff --git a/src/oned/code_39_reader.rs b/src/oned/code_39_reader.rs index 27976b5..6fc3344 100644 --- a/src/oned/code_39_reader.rs +++ b/src/oned/code_39_reader.rs @@ -32,7 +32,7 @@ pub struct Code39Reader { usingCheckDigit: bool, extendedMode: bool, decodeRowRXingResult: String, - counters: Vec, + // counters: Vec, } impl OneDReader for Code39Reader { fn decodeRow( @@ -42,12 +42,13 @@ impl OneDReader for Code39Reader { _hints: &DecodingHintDictionary, ) -> Result { // let theCounters = self.counters; - self.counters.fill(0); + let mut counters = [0_u32;9]; + // self.counters.fill(0); // let result = self.decodeRowRXingResult; // result.setLength(0); self.decodeRowRXingResult.clear(); - let start = Self::findAsteriskPattern(row, &mut self.counters)?; + let start = Self::findAsteriskPattern(row, &mut counters)?; // Read off white space let mut nextStart = row.getNextSet(start[1] as usize); let end = row.getSize(); @@ -55,15 +56,15 @@ impl OneDReader for Code39Reader { let mut decodedChar; let mut lastStart; loop { - Self::recordPattern(row, nextStart, &mut self.counters)?; - let pattern = Self::toNarrowWidePattern(&self.counters); + self.recordPattern(row, nextStart, &mut counters)?; + let pattern = Self::toNarrowWidePattern(&counters); if pattern < 0 { return Err(Exceptions::NotFoundException("".to_owned())); } decodedChar = Self::patternToChar(pattern as u32)?; self.decodeRowRXingResult.push(decodedChar); lastStart = nextStart; - for counter in &self.counters { + for counter in &counters { // for (int counter : theCounters) { nextStart += *counter as usize; } @@ -79,7 +80,7 @@ impl OneDReader for Code39Reader { // Look for whitespace after pattern: let mut lastPatternSize = 0; - for counter in &self.counters { + for counter in &counters { // for (int counter : self.counters) { lastPatternSize += *counter; } @@ -195,7 +196,7 @@ impl Code39Reader { usingCheckDigit, extendedMode, decodeRowRXingResult: String::with_capacity(20), - counters: vec![0; 9], + // counters: vec![0; 9], } } diff --git a/src/oned/code_93_reader.rs b/src/oned/code_93_reader.rs index 4b76eeb..088fb85 100644 --- a/src/oned/code_93_reader.rs +++ b/src/oned/code_93_reader.rs @@ -51,7 +51,7 @@ impl OneDReader for Code93Reader { let mut decodedChar; let mut lastStart; loop { - Self::recordPattern(row, nextStart, &mut theCounters)?; + self.recordPattern(row, nextStart, &mut theCounters)?; let pattern = Self::toPattern(&theCounters); if pattern < 0 { return Err(Exceptions::NotFoundException("".to_owned())); diff --git a/src/oned/ean_13_reader.rs b/src/oned/ean_13_reader.rs index 3f10136..fd66124 100644 --- a/src/oned/ean_13_reader.rs +++ b/src/oned/ean_13_reader.rs @@ -58,7 +58,7 @@ impl UPCEANReader for EAN13Reader { while x < 6 && rowOffset < end { // for (int x = 0; x < 6 && rowOffset < end; x++) { - let bestMatch = Self::decodeDigit( + let bestMatch = self.decodeDigit( row, &mut counters, rowOffset, @@ -79,7 +79,7 @@ impl UPCEANReader for EAN13Reader { Self::determineFirstDigit(resultString, lgPatternFound)?; let middleRange = - Self::findGuardPattern(row, rowOffset, true, &upc_ean_reader::MIDDLE_PATTERN)?; + self.findGuardPattern(row, rowOffset, true, &upc_ean_reader::MIDDLE_PATTERN)?; rowOffset = middleRange[1]; let mut x = 0; @@ -87,7 +87,7 @@ impl UPCEANReader for EAN13Reader { while x < 6 && rowOffset < end { // for (int x = 0; x < 6 && rowOffset < end; x++) { let bestMatch = - Self::decodeDigit(row, &mut counters, rowOffset, &upc_ean_reader::L_PATTERNS)?; + self.decodeDigit(row, &mut counters, rowOffset, &upc_ean_reader::L_PATTERNS)?; resultString.push(char::from_u32('0' as u32 + bestMatch as u32).unwrap()); // for (int counter : counters) { // rowOffset += counter; diff --git a/src/oned/ean_8_reader.rs b/src/oned/ean_8_reader.rs index 01bc068..8535527 100644 --- a/src/oned/ean_8_reader.rs +++ b/src/oned/ean_8_reader.rs @@ -52,7 +52,7 @@ impl UPCEANReader for EAN8Reader { while x < 4 && rowOffset < end { // for (int x = 0; x < 4 && rowOffset < end; x++) { let bestMatch = - Self::decodeDigit(row, &mut counters, rowOffset, &upc_ean_reader::L_PATTERNS)?; + self.decodeDigit(row, &mut counters, rowOffset, &upc_ean_reader::L_PATTERNS)?; resultString.push(char::from_u32('0' as u32 + bestMatch as u32).unwrap()); // for (int counter : counters) { // rowOffset += counter; @@ -64,14 +64,14 @@ impl UPCEANReader for EAN8Reader { } let middleRange = - Self::findGuardPattern(row, rowOffset, true, &upc_ean_reader::MIDDLE_PATTERN)?; + self.findGuardPattern(row, rowOffset, true, &upc_ean_reader::MIDDLE_PATTERN)?; rowOffset = middleRange[1]; let mut x = 0; while x < 4 && rowOffset < end { // for (int x = 0; x < 4 && rowOffset < end; x++) { let bestMatch = - Self::decodeDigit(row, &mut counters, rowOffset, &upc_ean_reader::L_PATTERNS)?; + self.decodeDigit(row, &mut counters, rowOffset, &upc_ean_reader::L_PATTERNS)?; resultString.push(char::from_u32('0' as u32 + bestMatch as u32).unwrap()); // for (int counter : counters) { // rowOffset += counter; @@ -83,3 +83,9 @@ impl UPCEANReader for EAN8Reader { Ok(rowOffset) } } + +impl Default for EAN8Reader { + fn default() -> Self { + Self { } + } +} \ No newline at end of file diff --git a/src/oned/itf_reader.rs b/src/oned/itf_reader.rs index 8366060..cc5f960 100644 --- a/src/oned/itf_reader.rs +++ b/src/oned/itf_reader.rs @@ -113,7 +113,7 @@ impl OneDReader for ITFReader { let endRange = self.decodeEnd(&mut row)?; let mut result = String::with_capacity(20); //new StringBuilder(20); - Self::decodeMiddle(&row, startRange[1], endRange[0], &mut result)?; + self.decodeMiddle(&row, startRange[1], endRange[0], &mut result)?; let resultString = result; //.toString(); let allowedLengths = if let Some(DecodeHintValue::AllowedLengths(al)) = @@ -180,6 +180,7 @@ impl ITFReader { * @throws NotFoundException if decoding could not complete successfully */ fn decodeMiddle( + &self, row: &BitArray, payloadStart: usize, payloadEnd: usize, @@ -197,7 +198,7 @@ impl ITFReader { while payloadStart < payloadEnd { // Get 10 runs of black/white. - Self::recordPattern(row, payloadStart, &mut counterDigitPair)?; + self.recordPattern(row, payloadStart, &mut counterDigitPair)?; // Split them into each array for k in 0..5 { // for (int k = 0; k < 5; k++) { @@ -206,9 +207,9 @@ impl ITFReader { counterWhite[k] = counterDigitPair[twoK + 1]; } - let mut bestMatch = Self::decodeDigit(&counterBlack)?; + let mut bestMatch = self.decodeDigit(&counterBlack)?; resultString.push(char::from_u32('0' as u32 + bestMatch).unwrap()); - bestMatch = Self::decodeDigit(&counterWhite)?; + bestMatch = self.decodeDigit(&counterWhite)?; resultString.push(char::from_u32('0' as u32 + bestMatch).unwrap()); payloadStart += counterDigitPair.iter().sum::() as usize; @@ -231,7 +232,7 @@ impl ITFReader { */ fn decodeStart(&mut self, row: &BitArray) -> Result<[usize; 2], Exceptions> { let endStart = Self::skipWhiteSpace(row)?; - let startPattern = Self::findGuardPattern(row, endStart, &START_PATTERN)?; + let startPattern = self.findGuardPattern(row, endStart, &START_PATTERN)?; // Determine the width of a narrow line in pixels. We can do this by // getting the width of the start pattern and dividing by 4 because its @@ -314,10 +315,10 @@ impl ITFReader { let endStart = Self::skipWhiteSpace(row)?; let mut endPattern; endPattern = - if let Ok(ptrn) = Self::findGuardPattern(row, endStart, &END_PATTERN_REVERSED[0]) { + if let Ok(ptrn) = self.findGuardPattern(row, endStart, &END_PATTERN_REVERSED[0]) { ptrn } else { - Self::findGuardPattern(row, endStart, &END_PATTERN_REVERSED[1])? + self.findGuardPattern(row, endStart, &END_PATTERN_REVERSED[1])? }; // try { // endPattern = findGuardPattern(row, endStart, END_PATTERN_REVERSED[0]); @@ -356,6 +357,7 @@ impl ITFReader { * @throws NotFoundException if pattern is not found */ fn findGuardPattern( + &self, row: &BitArray, rowOffset: usize, pattern: &[u32], @@ -373,7 +375,7 @@ impl ITFReader { counters[counterPosition] += 1; } else { if counterPosition == patternLength - 1 { - if Self::patternMatchVariance(&counters, pattern, MAX_INDIVIDUAL_VARIANCE) + if self.patternMatchVariance(&counters, pattern, MAX_INDIVIDUAL_VARIANCE) < MAX_AVG_VARIANCE { return Ok([patternStart, x]); @@ -403,14 +405,14 @@ impl ITFReader { * @return The decoded digit * @throws NotFoundException if digit cannot be decoded */ - fn decodeDigit(counters: &[u32]) -> Result { + fn decodeDigit(&self,counters: &[u32]) -> Result { let mut bestVariance = MAX_AVG_VARIANCE; // worst variance we'll accept let mut bestMatch = -1_isize; let max = PATTERNS.len(); for i in 0..max { // for (int i = 0; i < max; i++) { let pattern = &PATTERNS[i]; - let variance = Self::patternMatchVariance(counters, pattern, MAX_INDIVIDUAL_VARIANCE); + let variance = self.patternMatchVariance(counters, pattern, MAX_INDIVIDUAL_VARIANCE); if variance < bestVariance { bestVariance = variance; bestMatch = i as isize; diff --git a/src/oned/mod.rs b/src/oned/mod.rs index 080a037..f46d2cc 100644 --- a/src/oned/mod.rs +++ b/src/oned/mod.rs @@ -52,3 +52,6 @@ pub use one_d_code_writer::*; mod coda_bar_writer; pub use coda_bar_writer::*; + +mod multi_format_upc_ean_reader; +pub use multi_format_upc_ean_reader::*; diff --git a/src/oned/multi_format_one_d_reader.rs b/src/oned/multi_format_one_d_reader.rs index 2be0a07..c1a5557 100644 --- a/src/oned/multi_format_one_d_reader.rs +++ b/src/oned/multi_format_one_d_reader.rs @@ -19,6 +19,7 @@ use super::Code128Reader; use super::Code39Reader; use super::Code93Reader; use super::ITFReader; +use super::MultiFormatUPCEANReader; use super::OneDReader; use crate::BarcodeFormat; use crate::DecodeHintValue; @@ -61,15 +62,13 @@ impl MultiFormatOneDReader { if let Some(DecodeHintValue::PossibleFormats(possibleFormats)) = hints.get(&DecodeHintType::POSSIBLE_FORMATS) { - // if let let possibleFormats = hints == null ? null : - // (Collection) hints.get(&DecodeHintType::POSSIBLE_FORMATS); - // if (possibleFormats != null) { - // if (possibleFormats.contains(&BarcodeFormat::EAN_13) || - // possibleFormats.contains(&BarcodeFormat::UPC_A) || - // possibleFormats.contains(&BarcodeFormat::EAN_8) || - // possibleFormats.contains(&BarcodeFormat::UPC_E)) { - // readers.add(new MultiFormatUPCEANReader(hints)); - // } + if possibleFormats.contains(&BarcodeFormat::EAN_13) + || possibleFormats.contains(&BarcodeFormat::UPC_A) + || possibleFormats.contains(&BarcodeFormat::EAN_8) + || possibleFormats.contains(&BarcodeFormat::UPC_E) + { + readers.push(Box::new(MultiFormatUPCEANReader::new(hints))); + } if possibleFormats.contains(&BarcodeFormat::CODE_39) { readers.push(Box::new(Code39Reader::with_use_check_digit( useCode39CheckDigit, @@ -81,7 +80,7 @@ impl MultiFormatOneDReader { if possibleFormats.contains(&BarcodeFormat::CODE_128) { readers.push(Box::new(Code128Reader {})); } - if (possibleFormats.contains(&BarcodeFormat::ITF)) { + if possibleFormats.contains(&BarcodeFormat::ITF) { readers.push(Box::new(ITFReader::default())); } if possibleFormats.contains(&BarcodeFormat::CODABAR) { @@ -95,7 +94,7 @@ impl MultiFormatOneDReader { // } } if readers.is_empty() { - // readers.push(new MultiFormatUPCEANReader(hints)); + readers.push(Box::new(MultiFormatUPCEANReader::new(hints))); readers.push(Box::new(Code39Reader::new())); readers.push(Box::new(CodaBarReader::new())); readers.push(Box::new(Code93Reader::new())); diff --git a/src/oned/multi_format_upc_ean_reader.rs b/src/oned/multi_format_upc_ean_reader.rs new file mode 100644 index 0000000..f6d6430 --- /dev/null +++ b/src/oned/multi_format_upc_ean_reader.rs @@ -0,0 +1,214 @@ +/* + * 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 crate::BarcodeFormat; +use crate::DecodeHintValue; +use crate::Exceptions; +use crate::RXingResult; +use crate::Reader; + +use super::EAN13Reader; +use super::EAN8Reader; +use super::StandIn; +use super::UPCAReader; +use super::UPCEReader; +use super::{OneDReader, UPCEANReader}; + +/** + *

A reader that can read all available UPC/EAN formats. If a caller wants to try to + * read all such formats, it is most efficient to use this implementation rather than invoke + * individual readers.

+ * + * @author Sean Owen + */ +pub struct MultiFormatUPCEANReader(Vec>); + +impl MultiFormatUPCEANReader { + pub fn new(hints: &DecodingHintDictionary) -> Self { + let mut readers: Vec> = Vec::new(); + if let Some(DecodeHintValue::PossibleFormats(possibleFormats)) = + hints.get(&DecodeHintType::POSSIBLE_FORMATS) + { + // Collection possibleFormats = hints == null ? null : + // (Collection) hints.get(DecodeHintType.POSSIBLE_FORMATS); + // Collection readers = new ArrayList<>(); + if possibleFormats.contains(&BarcodeFormat::EAN_13) { + readers.push(Box::new(EAN13Reader::default())); + } else if possibleFormats.contains(&BarcodeFormat::UPC_A) { + readers.push(Box::new(UPCAReader::default())); + } + if possibleFormats.contains(&BarcodeFormat::EAN_8) { + readers.push(Box::new(EAN8Reader::default())); + } + if possibleFormats.contains(&BarcodeFormat::UPC_E) { + readers.push(Box::new(UPCEReader::default())); + } + } + if readers.is_empty() { + readers.push(Box::new(EAN13Reader::default())); + // UPC-A is covered by EAN-13 + readers.push(Box::new(EAN8Reader::default())); + readers.push(Box::new(UPCEReader::default())); + } + + Self(readers) + } + + fn try_decode_function( + &self, + reader: &Box, + rowNumber: u32, + row: &crate::common::BitArray, + hints: &crate::DecodingHintDictionary, + startGuardPattern: &[usize; 2], + ) -> Result { + let result = reader.decodeRowWithGuardRange(rowNumber, row, startGuardPattern, hints)?; + // Special case: a 12-digit code encoded in UPC-A is identical to a "0" + // followed by those 12 digits encoded as EAN-13. Each will recognize such a code, + // UPC-A as a 12-digit string and EAN-13 as a 13-digit string starting with "0". + // Individually these are correct and their readers will both read such a code + // and correctly call it EAN-13, or UPC-A, respectively. + // + // In this case, if we've been looking for both types, we'd like to call it + // a UPC-A code. But for efficiency we only run the EAN-13 decoder to also read + // UPC-A. So we special case it here, and convert an EAN-13 result to a UPC-A + // result if appropriate. + // + // But, don't return UPC-A if UPC-A was not a requested format! + let ean13MayBeUPCA = result.getBarcodeFormat() == &BarcodeFormat::EAN_13 + && result.getText().chars().nth(0).unwrap() == '0'; + + let canReturnUPCA = if let Some(DecodeHintValue::PossibleFormats(possibleFormats)) = + hints.get(&DecodeHintType::POSSIBLE_FORMATS) + { + possibleFormats.contains(&BarcodeFormat::UPC_A) + } else { + true + }; + + if ean13MayBeUPCA && canReturnUPCA { + // Transfer the metadata across + let mut resultUPCA = RXingResult::new( + &result.getText()[1..], + result.getRawBytes().clone(), + result.getRXingResultPoints().clone(), + BarcodeFormat::UPC_A, + ); + resultUPCA.putAllMetadata(result.getRXingResultMetadata().clone()); + + return Ok(resultUPCA); + } + + Ok(result) + } +} + +impl OneDReader for MultiFormatUPCEANReader { + fn decodeRow( + &mut self, + rowNumber: u32, + row: &crate::common::BitArray, + hints: &crate::DecodingHintDictionary, + ) -> Result { + // Compute this location once and reuse it on multiple implementations + let startGuardPattern = StandIn.findStartGuardPattern(row)?; + for reader in &self.0 { + // for (UPCEANReader reader : readers) { + let try_result = + self.try_decode_function(reader, rowNumber, row, hints, &startGuardPattern); + if try_result.is_ok() { + return try_result; + } + } + + return Err(Exceptions::NotFoundException("".to_owned())); + } +} + +use crate::result_point::ResultPoint; +use crate::DecodeHintType; +use crate::DecodingHintDictionary; +use crate::RXingResultMetadataType; +use crate::RXingResultMetadataValue; +use crate::RXingResultPoint; +use std::collections::HashMap; + +impl Reader for MultiFormatUPCEANReader { + fn decode(&mut self, image: &crate::BinaryBitmap) -> Result { + self.decode_with_hints(image, &HashMap::new()) + } + + // Note that we don't try rotation without the try harder flag, even if rotation was supported. + fn decode_with_hints( + &mut self, + image: &crate::BinaryBitmap, + hints: &DecodingHintDictionary, + ) -> Result { + if let Ok(res) = self.doDecode(image, hints) { + Ok(res) + } else { + let tryHarder = hints.contains_key(&DecodeHintType::TRY_HARDER); + if tryHarder && image.isRotateSupported() { + let rotatedImage = image.rotateCounterClockwise(); + let mut result = self.doDecode(&rotatedImage, hints)?; + // Record that we found it rotated 90 degrees CCW / 270 degrees CW + let metadata = result.getRXingResultMetadata(); + let mut orientation = 270; + if metadata.contains_key(&RXingResultMetadataType::ORIENTATION) { + // But if we found it reversed in doDecode(), add in that result here: + orientation = (orientation + + if let Some(crate::RXingResultMetadataValue::Orientation(or)) = + metadata.get(&RXingResultMetadataType::ORIENTATION) + { + *or + } else { + 0 + }) + % 360; + } + result.putMetadata( + RXingResultMetadataType::ORIENTATION, + RXingResultMetadataValue::Orientation(orientation), + ); + // Update result points + // let points = result.getRXingResultPoints(); + // if points != null { + let height = rotatedImage.getHeight(); + // for point in result.getRXingResultPointsMut().iter_mut() { + let total_points = result.getRXingResultPoints().len(); + let points = result.getRXingResultPointsMut(); + for i in 0..total_points { + // for (int i = 0; i < points.length; i++) { + points[i] = RXingResultPoint::new( + height as f32 - points[i].getY() - 1.0, + points[i].getX(), + ); + } + // } + + Ok(result) + } else { + return Err(Exceptions::NotFoundException("".to_owned())); + } + } + } + fn reset(&mut self) { + for reader in self.0.iter_mut() { + // for (Reader reader : readers) { + reader.reset(); + } + } +} diff --git a/src/oned/one_d_reader.rs b/src/oned/one_d_reader.rs index bec3a67..3bf28fe 100644 --- a/src/oned/one_d_reader.rs +++ b/src/oned/one_d_reader.rs @@ -159,9 +159,7 @@ pub trait OneDReader: Reader { * @throws NotFoundException if counters cannot be filled entirely from row before running out * of pixels */ - fn recordPattern(row: &BitArray, start: usize, counters: &mut [u32]) -> Result<(), Exceptions> - where - Self: Sized, + fn recordPattern(&self, row: &BitArray, start: usize, counters: &mut [u32]) -> Result<(), Exceptions> { let numCounters = counters.len(); // Arrays.fill(counters, 0, numCounters, 0); @@ -196,12 +194,11 @@ pub trait OneDReader: Reader { } fn recordPatternInReverse( + &self, row: &BitArray, start: usize, counters: &mut [u32], ) -> Result<(), Exceptions> - where - Self: Sized, { let mut start = start; // This could be more efficient I guess @@ -217,7 +214,7 @@ pub trait OneDReader: Reader { if numTransitionsLeft >= 0 { return Err(Exceptions::NotFoundException("".to_owned())); } - Self::recordPattern(row, start + 1, counters)?; + self.recordPattern(row, start + 1, counters)?; Ok(()) } @@ -232,9 +229,7 @@ pub trait OneDReader: Reader { * @param maxIndividualVariance The most any counter can differ before we give up * @return ratio of total variance between counters and pattern compared to total pattern size */ - fn patternMatchVariance(counters: &[u32], pattern: &[u32], maxIndividualVariance: f32) -> f32 - where - Self: Sized, + fn patternMatchVariance(&self, counters: &[u32], pattern: &[u32], maxIndividualVariance: f32) -> f32 { let mut maxIndividualVariance = maxIndividualVariance; let numCounters = counters.len(); diff --git a/src/oned/upc_e_reader.rs b/src/oned/upc_e_reader.rs index 1c7a185..e2dd22f 100644 --- a/src/oned/upc_e_reader.rs +++ b/src/oned/upc_e_reader.rs @@ -52,7 +52,7 @@ impl UPCEANReader for UPCEReader { let mut x = 0; while x < 6 && rowOffset < end { // for (int x = 0; x < 6 && rowOffset < end; x++) { - let bestMatch = Self::decodeDigit(row, &mut counters, rowOffset, &L_AND_G_PATTERNS)?; + let bestMatch = self.decodeDigit(row, &mut counters, rowOffset, &L_AND_G_PATTERNS)?; resultString.push(char::from_u32('0' as u32 + bestMatch as u32 % 10).unwrap()); // for (int counter : counters) { // rowOffset += counter; @@ -72,14 +72,12 @@ impl UPCEANReader for UPCEReader { } fn checkChecksum(&self, s: &str) -> Result { - Self::checkStandardUPCEANChecksum(&convertUPCEtoUPCA(s)) + self.checkStandardUPCEANChecksum(&convertUPCEtoUPCA(s)) } - fn decodeEnd(row: &crate::common::BitArray, endStart: usize) -> Result<[usize; 2], Exceptions> - where - Self: Sized, + fn decodeEnd(&self, row: &crate::common::BitArray, endStart: usize) -> Result<[usize; 2], Exceptions> { - Self::findGuardPattern(row, endStart, true, &Self::MIDDLE_END_PATTERN) + self.findGuardPattern(row, endStart, true, &Self::MIDDLE_END_PATTERN) } } diff --git a/src/oned/upc_ean_extension_2_support.rs b/src/oned/upc_ean_extension_2_support.rs index bfbc347..da65417 100644 --- a/src/oned/upc_ean_extension_2_support.rs +++ b/src/oned/upc_ean_extension_2_support.rs @@ -88,7 +88,7 @@ impl UPCEANExtension2Support { let mut x = 0; while x < 2 && rowOffset < end { // for (int x = 0; x < 2 && rowOffset < end; x++) { - let bestMatch = StandIn::decodeDigit( + let bestMatch = StandIn.decodeDigit( row, &mut counters, rowOffset, diff --git a/src/oned/upc_ean_extension_5_support.rs b/src/oned/upc_ean_extension_5_support.rs index 5b166e6..4171442 100644 --- a/src/oned/upc_ean_extension_5_support.rs +++ b/src/oned/upc_ean_extension_5_support.rs @@ -90,7 +90,7 @@ impl UPCEANExtension5Support { let mut x = 0; while x < 5 && rowOffset < end { // for (int x = 0; x < 5 && rowOffset < end; x++) { - let bestMatch = StandIn::decodeDigit( + let bestMatch = StandIn.decodeDigit( row, &mut counters, rowOffset, diff --git a/src/oned/upc_ean_extension_support.rs b/src/oned/upc_ean_extension_support.rs index 0d0f6c9..a127315 100644 --- a/src/oned/upc_ean_extension_support.rs +++ b/src/oned/upc_ean_extension_support.rs @@ -41,7 +41,7 @@ impl UPCEANExtensionSupport { rowOffset: usize, ) -> Result { let extensionStartRange = - StandIn::findGuardPattern(row, rowOffset, false, &Self::EXTENSION_START_PATTERN)?; + StandIn.findGuardPattern(row, rowOffset, false, &Self::EXTENSION_START_PATTERN)?; if let Ok(res_1) = self .fiveSupport .decodeRow(rowNumber, row, &extensionStartRange) diff --git a/src/oned/upc_ean_reader.rs b/src/oned/upc_ean_reader.rs index ee4b44d..437f45d 100644 --- a/src/oned/upc_ean_reader.rs +++ b/src/oned/upc_ean_reader.rs @@ -116,9 +116,7 @@ pub trait UPCEANReader: OneDReader { // eanManSupport = new EANManufacturerOrgSupport(); // } - fn findStartGuardPattern(row: &BitArray) -> Result<[usize; 2], Exceptions> - where - Self: Sized, + fn findStartGuardPattern(&self, row: &BitArray) -> Result<[usize; 2], Exceptions> { let mut foundStart = false; let mut startRange = [0; 2]; //= null; @@ -127,7 +125,7 @@ pub trait UPCEANReader: OneDReader { while !foundStart { counters.fill(0); // Arrays.fill(counters, 0, START_END_PATTERN.len(), 0); - startRange = Self::findGuardPatternWithCounters( + startRange = self.findGuardPatternWithCounters( row, nextStart, false, @@ -175,8 +173,6 @@ pub trait UPCEANReader: OneDReader { startGuardRange: &[usize; 2], hints: &crate::DecodingHintDictionary, ) -> Result - where - Self: Sized, { let resultPointCallback = hints.get(&DecodeHintType::NEED_RESULT_POINT_CALLBACK); let mut symbologyIdentifier = 0; @@ -195,7 +191,7 @@ pub trait UPCEANReader: OneDReader { cb(&RXingResultPoint::new(endStart as f32, rowNumber as f32)); } - let endRange = Self::decodeEnd(row, endStart)?; + let endRange = self.decodeEnd(row, endStart)?; if let Some(DecodeHintValue::NeedResultPointCallback(cb)) = resultPointCallback { cb(&RXingResultPoint::new( @@ -324,7 +320,7 @@ pub trait UPCEANReader: OneDReader { * @throws FormatException if the string does not contain only digits */ fn checkChecksum(&self, s: &str) -> Result { - Self::checkStandardUPCEANChecksum(s) + self.checkStandardUPCEANChecksum(s) } /** @@ -335,7 +331,7 @@ pub trait UPCEANReader: OneDReader { * @return true iff string of digits passes the UPC/EAN checksum algorithm * @throws FormatException if the string does not contain only digits */ - fn checkStandardUPCEANChecksum(s: &str) -> Result { + fn checkStandardUPCEANChecksum(&self, s: &str) -> Result { let length = s.len(); if length == 0 { return Ok(false); @@ -345,7 +341,7 @@ pub trait UPCEANReader: OneDReader { // let check = Character.digit(s.charAt(length - 1), 10); let check_against = &s[..length - 1]; //s.subSequence(0, length - 1); - let calculated_checksum = Self::getStandardUPCEANChecksum(check_against)?; + let calculated_checksum = self.getStandardUPCEANChecksum(check_against)?; Ok(calculated_checksum == if check { @@ -355,7 +351,7 @@ pub trait UPCEANReader: OneDReader { }) } - fn getStandardUPCEANChecksum(s: &str) -> Result { + fn getStandardUPCEANChecksum(&self, s: &str) -> Result { let length = s.chars().count(); let mut sum = 0; let mut i = length as isize - 1; @@ -384,23 +380,20 @@ pub trait UPCEANReader: OneDReader { Ok(((1000 - sum) % 10) as u32) } - fn decodeEnd(row: &BitArray, endStart: usize) -> Result<[usize; 2], Exceptions> - where - Self: Sized, + fn decodeEnd(&self, row: &BitArray, endStart: usize) -> Result<[usize; 2], Exceptions> { - Self::findGuardPattern(row, endStart, false, &START_END_PATTERN) + self.findGuardPattern(row, endStart, false, &START_END_PATTERN) } fn findGuardPattern( + &self, row: &BitArray, rowOffset: usize, whiteFirst: bool, pattern: &[u32], ) -> Result<[usize; 2], Exceptions> - where - Self: Sized, { - Self::findGuardPatternWithCounters( + self.findGuardPatternWithCounters( row, rowOffset, whiteFirst, @@ -421,14 +414,13 @@ pub trait UPCEANReader: OneDReader { * @throws NotFoundException if pattern is not found */ fn findGuardPatternWithCounters( + &self, row: &BitArray, rowOffset: usize, whiteFirst: bool, pattern: &[u32], counters: &mut [u32], ) -> Result<[usize; 2], Exceptions> - where - Self: Sized, { let width = row.getSize(); let rowOffset = if whiteFirst { @@ -446,7 +438,7 @@ pub trait UPCEANReader: OneDReader { counters[counterPosition] += 1; } else { if counterPosition == patternLength - 1 { - if Self::patternMatchVariance(counters, pattern, MAX_INDIVIDUAL_VARIANCE) + if self.patternMatchVariance(counters, pattern, MAX_INDIVIDUAL_VARIANCE) < MAX_AVG_VARIANCE { return Ok([patternStart, x]); @@ -482,15 +474,14 @@ pub trait UPCEANReader: OneDReader { * @throws NotFoundException if digit cannot be decoded */ fn decodeDigit( + &self, row: &BitArray, counters: &mut [u32; 4], rowOffset: usize, patterns: &[[u32; 4]], ) -> Result - where - Self: Sized, { - Self::recordPattern(row, rowOffset, counters)?; + self.recordPattern(row, rowOffset, counters)?; let mut bestVariance = MAX_AVG_VARIANCE; // worst variance we'll accept let mut bestMatch = -1_isize; let max = patterns.len(); @@ -498,7 +489,7 @@ pub trait UPCEANReader: OneDReader { // for (int i = 0; i < max; i++) { let pattern = &patterns[i]; let variance: f32 = - Self::patternMatchVariance(counters, pattern, MAX_INDIVIDUAL_VARIANCE); + self.patternMatchVariance(counters, pattern, MAX_INDIVIDUAL_VARIANCE); if variance < bestVariance { bestVariance = variance; bestMatch = i as isize; @@ -536,8 +527,8 @@ pub trait UPCEANReader: OneDReader { ) -> Result; } -pub(crate) struct StandIn; -impl UPCEANReader for StandIn { +pub(crate) struct StandInStruct; +impl UPCEANReader for StandInStruct { fn getBarcodeFormat(&self) -> BarcodeFormat { todo!() } @@ -551,7 +542,7 @@ impl UPCEANReader for StandIn { todo!() } } -impl OneDReader for StandIn { +impl OneDReader for StandInStruct { fn decodeRow( &mut self, _rowNumber: u32, @@ -562,7 +553,7 @@ impl OneDReader for StandIn { } } -impl Reader for StandIn { +impl Reader for StandInStruct { fn decode(&mut self, _image: &crate::BinaryBitmap) -> Result { todo!() } @@ -575,3 +566,5 @@ impl Reader for StandIn { todo!() } } + +pub(crate) const StandIn : StandInStruct = StandInStruct{}; \ No newline at end of file diff --git a/tests/EAN8BlackBox1TestCase.rs b/tests/EAN8BlackBox1TestCase.rs index 9d71589..ab75ca3 100644 --- a/tests/EAN8BlackBox1TestCase.rs +++ b/tests/EAN8BlackBox1TestCase.rs @@ -25,8 +25,8 @@ mod common; fn ean8_black_box1_test_case() { let mut tester = common::AbstractBlackBoxTestCase::new( "test_resources/blackbox/ean8-1", - // MultiFormatReader::default(), - EAN8Reader {}, + MultiFormatReader::default(), + // EAN8Reader {}, BarcodeFormat::EAN_8, ); diff --git a/tests/UPCEANExtensionBlackBox1TestCase.rs b/tests/UPCEANExtensionBlackBox1TestCase.rs index 87df322..0bf71db 100644 --- a/tests/UPCEANExtensionBlackBox1TestCase.rs +++ b/tests/UPCEANExtensionBlackBox1TestCase.rs @@ -25,8 +25,8 @@ mod common; fn upceanextension_black_box1_test_case() { let mut tester = common::AbstractBlackBoxTestCase::new( "test_resources/blackbox/upcean-extension-1", - // MultiFormatReader::default(), - EAN13Reader {}, + MultiFormatReader::default(), + // EAN13Reader {}, BarcodeFormat::EAN_13, ); // super("src/test/resources/blackbox/upcean-extension-1", new MultiFormatReader(), BarcodeFormat.EAN_13); diff --git a/tests/ean_13_blackbox_tests.rs b/tests/ean_13_blackbox_tests.rs index 486ec41..4dec3e9 100644 --- a/tests/ean_13_blackbox_tests.rs +++ b/tests/ean_13_blackbox_tests.rs @@ -25,8 +25,8 @@ mod common; fn ean13_black_box1_test_case() { let mut tester = common::AbstractBlackBoxTestCase::new( "test_resources/blackbox/ean13-1", - // MultiFormatReader::default(), - EAN13Reader {}, + MultiFormatReader::default(), + // EAN13Reader {}, BarcodeFormat::EAN_13, ); @@ -46,8 +46,8 @@ fn ean13_black_box1_test_case() { fn ean13_black_box2_test_case() { let mut tester = common::AbstractBlackBoxTestCase::new( "test_resources/blackbox/ean13-2", - // MultiFormatReader::default(), - EAN13Reader {}, + MultiFormatReader::default(), + // EAN13Reader {}, BarcodeFormat::EAN_13, ); @@ -65,8 +65,8 @@ fn ean13_black_box2_test_case() { fn ean13_black_box3_test_case() { let mut tester = common::AbstractBlackBoxTestCase::new( "test_resources/blackbox/ean13-3", - // MultiFormatReader::default(), - EAN13Reader {}, + MultiFormatReader::default(), + // EAN13Reader {}, BarcodeFormat::EAN_13, ); @@ -85,8 +85,8 @@ fn ean13_black_box3_test_case() { fn ean13_black_box4_test_case() { let mut tester = common::AbstractBlackBoxTestCase::new( "test_resources/blackbox/ean13-4", - // MultiFormatReader::default(), - EAN13Reader {}, + MultiFormatReader::default(), + // EAN13Reader {}, BarcodeFormat::EAN_13, ); // super("src/test/resources/blackbox/ean13-4", new MultiFormatReader(), BarcodeFormat.EAN_13); @@ -104,8 +104,8 @@ fn ean13_black_box4_test_case() { fn ean13_black_box5_blurry_test_case() { let mut tester = common::AbstractBlackBoxTestCase::new( "test_resources/blackbox/ean13-5", - // MultiFormatReader::default(), - EAN13Reader {}, + MultiFormatReader::default(), + // EAN13Reader {}, BarcodeFormat::EAN_13, ); // super("src/test/resources/blackbox/ean13-5", new MultiFormatReader(), BarcodeFormat.EAN_13); diff --git a/tests/upc_a_blackbox_tests.rs b/tests/upc_a_blackbox_tests.rs index 465801e..fa13493 100644 --- a/tests/upc_a_blackbox_tests.rs +++ b/tests/upc_a_blackbox_tests.rs @@ -28,8 +28,8 @@ mod common; fn upcablack_box1_test_case() { let mut tester = common::AbstractBlackBoxTestCase::new( "test_resources/blackbox/upca-1", - // MultiFormatReader::default(), - UPCAReader::default(), + MultiFormatReader::default(), + // UPCAReader::default(), BarcodeFormat::UPC_A, ); @@ -47,8 +47,8 @@ fn upcablack_box1_test_case() { fn upcablack_box2_test_case() { let mut tester = common::AbstractBlackBoxTestCase::new( "test_resources/blackbox/upca-2", - // MultiFormatReader::default(), - UPCAReader::default(), + MultiFormatReader::default(), + // UPCAReader::default(), BarcodeFormat::UPC_A, ); @@ -66,8 +66,8 @@ fn upcablack_box2_test_case() { fn upcablack_box3_reflective_test_case() { let mut tester = common::AbstractBlackBoxTestCase::new( "test_resources/blackbox/upca-3", - // MultiFormatReader::default(), - UPCAReader::default(), + MultiFormatReader::default(), + // UPCAReader::default(), BarcodeFormat::UPC_A, ); @@ -85,8 +85,8 @@ fn upcablack_box3_reflective_test_case() { fn upcablack_box4_test_case() { let mut tester = common::AbstractBlackBoxTestCase::new( "test_resources/blackbox/upca-4", - // MultiFormatReader::default(), - UPCAReader::default(), + MultiFormatReader::default(), + // UPCAReader::default(), BarcodeFormat::UPC_A, ); @@ -104,8 +104,8 @@ fn upcablack_box4_test_case() { fn upcablack_box5_test_case() { let mut tester = common::AbstractBlackBoxTestCase::new( "test_resources/blackbox/upca-5", - // MultiFormatReader::default(), - UPCAReader::default(), + MultiFormatReader::default(), + // UPCAReader::default(), BarcodeFormat::UPC_A, ); @@ -124,8 +124,8 @@ fn upcablack_box5_test_case() { fn upcablack_box6_blurry_test_case() { let mut tester = common::AbstractBlackBoxTestCase::new( "test_resources/blackbox/upca-6", - // MultiFormatReader::default(), - UPCAReader::default(), + MultiFormatReader::default(), + // UPCAReader::default(), BarcodeFormat::UPC_A, ); diff --git a/tests/upc_e_blackbox_tests.rs b/tests/upc_e_blackbox_tests.rs index e6266e0..626d388 100644 --- a/tests/upc_e_blackbox_tests.rs +++ b/tests/upc_e_blackbox_tests.rs @@ -28,8 +28,8 @@ mod common; fn upceblack_box1_test_case() { let mut tester = common::AbstractBlackBoxTestCase::new( "test_resources/blackbox/upce-1", - // MultiFormatReader::default(), - UPCEReader::default(), + MultiFormatReader::default(), + // UPCEReader::default(), BarcodeFormat::UPC_E, ); @@ -47,8 +47,8 @@ fn upceblack_box1_test_case() { fn upceblack_box2_test_case() { let mut tester = common::AbstractBlackBoxTestCase::new( "test_resources/blackbox/upce-2", - // MultiFormatReader::default(), - UPCEReader::default(), + MultiFormatReader::default(), + // UPCEReader::default(), BarcodeFormat::UPC_E, ); @@ -66,8 +66,8 @@ fn upceblack_box2_test_case() { fn upceblack_box3_reflective_test_case() { let mut tester = common::AbstractBlackBoxTestCase::new( "test_resources/blackbox/upce-3", - // MultiFormatReader::default(), - UPCEReader::default(), + MultiFormatReader::default(), + // UPCEReader::default(), BarcodeFormat::UPC_E, );