diff --git a/src/oned/ITFReader.java b/src/oned/ITFReader.java deleted file mode 100644 index a957d9c..0000000 --- a/src/oned/ITFReader.java +++ /dev/null @@ -1,379 +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.FormatException; -import com.google.zxing.NotFoundException; -import com.google.zxing.RXingResult; -import com.google.zxing.RXingResultMetadataType; -import com.google.zxing.RXingResultPoint; -import com.google.zxing.common.BitArray; - -import java.util.Map; - -/** - *

Implements decoding of the ITF format, or Interleaved Two of Five.

- * - *

This Reader will scan ITF barcodes of certain lengths only. - * At the moment it reads length 6, 8, 10, 12, 14, 16, 18, 20, 24, and 44 as these have appeared "in the wild". Not all - * lengths are scanned, especially shorter ones, to avoid false positives. This in turn is due to a lack of - * required checksum function.

- * - *

The checksum is optional and is not applied by this Reader. The consumer of the decoded - * value will have to apply a checksum if required.

- * - *

http://en.wikipedia.org/wiki/Interleaved_2_of_5 - * is a great reference for Interleaved 2 of 5 information.

- * - * @author kevin.osullivan@sita.aero, SITA Lab. - */ -public final class ITFReader extends OneDReader { - - private static final float MAX_AVG_VARIANCE = 0.38f; - private static final float MAX_INDIVIDUAL_VARIANCE = 0.5f; - - private static final int W = 3; // Pixel width of a 3x wide line - private static final int w = 2; // Pixel width of a 2x wide line - private static final int N = 1; // Pixed width of a narrow line - - /** Valid ITF lengths. Anything longer than the largest value is also allowed. */ - private static final int[] DEFAULT_ALLOWED_LENGTHS = {6, 8, 10, 12, 14}; - - // Stores the actual narrow line width of the image being decoded. - private int narrowLineWidth = -1; - - /** - * Start/end guard pattern. - * - * Note: The end pattern is reversed because the row is reversed before - * searching for the END_PATTERN - */ - private static final int[] START_PATTERN = {N, N, N, N}; - private static final int[][] END_PATTERN_REVERSED = { - {N, N, w}, // 2x - {N, N, W} // 3x - }; - - // See ITFWriter.PATTERNS - - /** - * Patterns of Wide / Narrow lines to indicate each digit - */ - private static final int[][] PATTERNS = { - {N, N, w, w, N}, // 0 - {w, N, N, N, w}, // 1 - {N, w, N, N, w}, // 2 - {w, w, N, N, N}, // 3 - {N, N, w, N, w}, // 4 - {w, N, w, N, N}, // 5 - {N, w, w, N, N}, // 6 - {N, N, N, w, w}, // 7 - {w, N, N, w, N}, // 8 - {N, w, N, w, N}, // 9 - {N, N, W, W, N}, // 0 - {W, N, N, N, W}, // 1 - {N, W, N, N, W}, // 2 - {W, W, N, N, N}, // 3 - {N, N, W, N, W}, // 4 - {W, N, W, N, N}, // 5 - {N, W, W, N, N}, // 6 - {N, N, N, W, W}, // 7 - {W, N, N, W, N}, // 8 - {N, W, N, W, N} // 9 - }; - - @Override - public RXingResult decodeRow(int rowNumber, BitArray row, Map hints) - throws FormatException, NotFoundException { - - // Find out where the Middle section (payload) starts & ends - int[] startRange = decodeStart(row); - int[] endRange = decodeEnd(row); - - StringBuilder result = new StringBuilder(20); - decodeMiddle(row, startRange[1], endRange[0], result); - String resultString = result.toString(); - - int[] allowedLengths = null; - if (hints != null) { - allowedLengths = (int[]) hints.get(DecodeHintType.ALLOWED_LENGTHS); - - } - if (allowedLengths == null) { - allowedLengths = DEFAULT_ALLOWED_LENGTHS; - } - - // To avoid false positives with 2D barcodes (and other patterns), make - // an assumption that the decoded string must be a 'standard' length if it's short - int length = resultString.length(); - boolean lengthOK = false; - int maxAllowedLength = 0; - for (int allowedLength : allowedLengths) { - if (length == allowedLength) { - lengthOK = true; - break; - } - if (allowedLength > maxAllowedLength) { - maxAllowedLength = allowedLength; - } - } - if (!lengthOK && length > maxAllowedLength) { - lengthOK = true; - } - if (!lengthOK) { - throw FormatException.getFormatInstance(); - } - - RXingResult resultObject = new RXingResult( - resultString, - null, // no natural byte representation for these barcodes - new RXingResultPoint[] {new RXingResultPoint(startRange[1], rowNumber), - new RXingResultPoint(endRange[0], rowNumber)}, - BarcodeFormat.ITF); - resultObject.putMetadata(RXingResultMetadataType.SYMBOLOGY_IDENTIFIER, "]I0"); - return resultObject; - } - - /** - * @param row row of black/white values to search - * @param payloadStart offset of start pattern - * @param resultString {@link StringBuilder} to append decoded chars to - * @throws NotFoundException if decoding could not complete successfully - */ - private static void decodeMiddle(BitArray row, - int payloadStart, - int payloadEnd, - StringBuilder resultString) throws NotFoundException { - - // Digits are interleaved in pairs - 5 black lines for one digit, and the - // 5 - // interleaved white lines for the second digit. - // Therefore, need to scan 10 lines and then - // split these into two arrays - int[] counterDigitPair = new int[10]; - int[] counterBlack = new int[5]; - int[] counterWhite = new int[5]; - - while (payloadStart < payloadEnd) { - - // Get 10 runs of black/white. - recordPattern(row, payloadStart, counterDigitPair); - // Split them into each array - for (int k = 0; k < 5; k++) { - int twoK = 2 * k; - counterBlack[k] = counterDigitPair[twoK]; - counterWhite[k] = counterDigitPair[twoK + 1]; - } - - int bestMatch = decodeDigit(counterBlack); - resultString.append((char) ('0' + bestMatch)); - bestMatch = decodeDigit(counterWhite); - resultString.append((char) ('0' + bestMatch)); - - for (int counterDigit : counterDigitPair) { - payloadStart += counterDigit; - } - } - } - - /** - * Identify where the start of the middle / payload section starts. - * - * @param row row of black/white values to search - * @return Array, containing index of start of 'start block' and end of - * 'start block' - */ - private int[] decodeStart(BitArray row) throws NotFoundException { - int endStart = skipWhiteSpace(row); - int[] startPattern = 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 - // made up of 4 narrow lines. - this.narrowLineWidth = (startPattern[1] - startPattern[0]) / 4; - - validateQuietZone(row, startPattern[0]); - - return startPattern; - } - - /** - * The start & end patterns must be pre/post fixed by a quiet zone. This - * zone must be at least 10 times the width of a narrow line. Scan back until - * we either get to the start of the barcode or match the necessary number of - * quiet zone pixels. - * - * Note: Its assumed the row is reversed when using this method to find - * quiet zone after the end pattern. - * - * ref: http://www.barcode-1.net/i25code.html - * - * @param row bit array representing the scanned barcode. - * @param startPattern index into row of the start or end pattern. - * @throws NotFoundException if the quiet zone cannot be found - */ - private void validateQuietZone(BitArray row, int startPattern) throws NotFoundException { - - int quietCount = this.narrowLineWidth * 10; // expect to find this many pixels of quiet zone - - // if there are not so many pixel at all let's try as many as possible - quietCount = Math.min(quietCount, startPattern); - - for (int i = startPattern - 1; quietCount > 0 && i >= 0; i--) { - if (row.get(i)) { - break; - } - quietCount--; - } - if (quietCount != 0) { - // Unable to find the necessary number of quiet zone pixels. - throw NotFoundException.getNotFoundInstance(); - } - } - - /** - * Skip all whitespace until we get to the first black line. - * - * @param row row of black/white values to search - * @return index of the first black line. - * @throws NotFoundException Throws exception if no black lines are found in the row - */ - private static int skipWhiteSpace(BitArray row) throws NotFoundException { - int width = row.getSize(); - int endStart = row.getNextSet(0); - if (endStart == width) { - throw NotFoundException.getNotFoundInstance(); - } - - return endStart; - } - - /** - * Identify where the end of the middle / payload section ends. - * - * @param row row of black/white values to search - * @return Array, containing index of start of 'end block' and end of 'end - * block' - */ - private int[] decodeEnd(BitArray row) throws NotFoundException { - - // For convenience, reverse the row and then - // search from 'the start' for the end block - row.reverse(); - try { - int endStart = skipWhiteSpace(row); - int[] endPattern; - try { - endPattern = findGuardPattern(row, endStart, END_PATTERN_REVERSED[0]); - } catch (NotFoundException nfe) { - endPattern = findGuardPattern(row, endStart, END_PATTERN_REVERSED[1]); - } - - // The start & end patterns must be pre/post fixed by a quiet zone. This - // zone must be at least 10 times the width of a narrow line. - // ref: http://www.barcode-1.net/i25code.html - validateQuietZone(row, endPattern[0]); - - // Now recalculate the indices of where the 'endblock' starts & stops to - // accommodate - // the reversed nature of the search - int temp = endPattern[0]; - endPattern[0] = row.getSize() - endPattern[1]; - endPattern[1] = row.getSize() - temp; - - return endPattern; - } finally { - // Put the row back the right way. - row.reverse(); - } - } - - /** - * @param row row of black/white values to search - * @param rowOffset position to start search - * @param pattern pattern of counts of number of black and white pixels that are - * being searched for as a pattern - * @return start/end horizontal offset of guard pattern, as an array of two - * ints - * @throws NotFoundException if pattern is not found - */ - private static int[] findGuardPattern(BitArray row, - int rowOffset, - int[] pattern) throws NotFoundException { - int patternLength = pattern.length; - int[] counters = new int[patternLength]; - int width = row.getSize(); - boolean isWhite = false; - - int counterPosition = 0; - int patternStart = rowOffset; - for (int x = rowOffset; x < width; x++) { - if (row.get(x) != isWhite) { - counters[counterPosition]++; - } else { - if (counterPosition == patternLength - 1) { - if (patternMatchVariance(counters, pattern, MAX_INDIVIDUAL_VARIANCE) < MAX_AVG_VARIANCE) { - return new int[]{patternStart, x}; - } - patternStart += counters[0] + counters[1]; - System.arraycopy(counters, 2, counters, 0, counterPosition - 1); - counters[counterPosition - 1] = 0; - counters[counterPosition] = 0; - counterPosition--; - } else { - counterPosition++; - } - counters[counterPosition] = 1; - isWhite = !isWhite; - } - } - throw NotFoundException.getNotFoundInstance(); - } - - /** - * Attempts to decode a sequence of ITF black/white lines into single - * digit. - * - * @param counters the counts of runs of observed black/white/black/... values - * @return The decoded digit - * @throws NotFoundException if digit cannot be decoded - */ - private static int decodeDigit(int[] counters) throws NotFoundException { - float bestVariance = MAX_AVG_VARIANCE; // worst variance we'll accept - int bestMatch = -1; - int max = PATTERNS.length; - for (int i = 0; i < max; i++) { - int[] pattern = PATTERNS[i]; - float variance = patternMatchVariance(counters, pattern, MAX_INDIVIDUAL_VARIANCE); - if (variance < bestVariance) { - bestVariance = variance; - bestMatch = i; - } else if (variance == bestVariance) { - // if we find a second 'best match' with the same variance, we can not reliably report to have a suitable match - bestMatch = -1; - } - } - if (bestMatch >= 0) { - return bestMatch % 10; - } else { - throw NotFoundException.getNotFoundInstance(); - } - } - -} diff --git a/src/oned/itf_reader.rs b/src/oned/itf_reader.rs new file mode 100644 index 0000000..21f4b5d --- /dev/null +++ b/src/oned/itf_reader.rs @@ -0,0 +1,428 @@ +/* + * 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 one_d_reader_derive::OneDReader; + +use crate::{common::BitArray, BarcodeFormat, DecodeHintValue, Exceptions, RXingResult}; + +use super::OneDReader; + +const MAX_AVG_VARIANCE: f32 = 0.38; +const MAX_INDIVIDUAL_VARIANCE: f32 = 0.5; + +const W: u32 = 3; // Pixel width of a 3x wide line +const W_LOWER: u32 = 2; // Pixel width of a 2x wide line +const N: u32 = 1; // Pixed width of a narrow line + +/** Valid ITF lengths. Anything longer than the largest value is also allowed. */ +const DEFAULT_ALLOWED_LENGTHS: [u32; 5] = [6, 8, 10, 12, 14]; + +/** + * Start/end guard pattern. + * + * Note: The end pattern is reversed because the row is reversed before + * searching for the END_PATTERN + */ +const START_PATTERN: [u32; 4] = [N, N, N, N]; +const END_PATTERN_REVERSED: [[u32; 3]; 2] = [ + [N, N, W_LOWER], // 2x + [N, N, W], // 3x +]; + +// See ITFWriter.PATTERNS + +/** + * Patterns of Wide / Narrow lines to indicate each digit + */ +const PATTERNS: [[u32; 5]; 20] = [ + [N, N, W_LOWER, W_LOWER, N], // 0 + [W_LOWER, N, N, N, W_LOWER], // 1 + [N, W_LOWER, N, N, W_LOWER], // 2 + [W_LOWER, W_LOWER, N, N, N], // 3 + [N, N, W_LOWER, N, W_LOWER], // 4 + [W_LOWER, N, W_LOWER, N, N], // 5 + [N, W_LOWER, W_LOWER, N, N], // 6 + [N, N, N, W_LOWER, W_LOWER], // 7 + [W_LOWER, N, N, W_LOWER, N], // 8 + [N, W_LOWER, N, W_LOWER, N], // 9 + [N, N, W, W, N], // 0 + [W, N, N, N, W], // 1 + [N, W, N, N, W], // 2 + [W, W, N, N, N], // 3 + [N, N, W, N, W], // 4 + [W, N, W, N, N], // 5 + [N, W, W, N, N], // 6 + [N, N, N, W, W], // 7 + [W, N, N, W, N], // 8 + [N, W, N, W, N], // 9 +]; + +/** + *

Implements decoding of the ITF format, or Interleaved Two of Five.

+ * + *

This Reader will scan ITF barcodes of certain lengths only. + * At the moment it reads length 6, 8, 10, 12, 14, 16, 18, 20, 24, and 44 as these have appeared "in the wild". Not all + * lengths are scanned, especially shorter ones, to avoid false positives. This in turn is due to a lack of + * required checksum function.

+ * + *

The checksum is optional and is not applied by this Reader. The consumer of the decoded + * value will have to apply a checksum if required.

+ * + *

http://en.wikipedia.org/wiki/Interleaved_2_of_5 + * is a great reference for Interleaved 2 of 5 information.

+ * + * @author kevin.osullivan@sita.aero, SITA Lab. + */ +#[derive(OneDReader)] +pub struct ITFReader { + // Stores the actual narrow line width of the image being decoded. + narrowLineWidth: i32, +} + +impl Default for ITFReader { + fn default() -> Self { + Self { + narrowLineWidth: -1, + } + } +} + +impl OneDReader for ITFReader { + fn decodeRow( + &mut self, + rowNumber: u32, + row: &crate::common::BitArray, + hints: &crate::DecodingHintDictionary, + ) -> Result { + // Find out where the Middle section (payload) starts & ends + let mut row = row.clone(); + let startRange = self.decodeStart(&row)?; + 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)?; + let resultString = result; //.toString(); + + let allowedLengths = if let Some(DecodeHintValue::AllowedLengths(al)) = + hints.get(&DecodeHintType::ALLOWED_LENGTHS) + { + al.clone() + } else { + DEFAULT_ALLOWED_LENGTHS.to_vec() + }; + // int[] allowedLengths = null; + // if (hints != null) { + // allowedLengths = (int[]) hints.get(DecodeHintType.ALLOWED_LENGTHS); + + // } + // if (allowedLengths == null) { + // allowedLengths = DEFAULT_ALLOWED_LENGTHS; + // } + + // To avoid false positives with 2D barcodes (and other patterns), make + // an assumption that the decoded string must be a 'standard' length if it's short + let length = resultString.chars().count(); + let mut lengthOK = false; + let mut maxAllowedLength = 0; + for allowedLength in allowedLengths { + // for (int allowedLength : allowedLengths) { + if length == allowedLength as usize { + lengthOK = true; + break; + } + if allowedLength > maxAllowedLength { + maxAllowedLength = allowedLength; + } + } + if !lengthOK && length > maxAllowedLength as usize { + lengthOK = true; + } + if !lengthOK { + return Err(Exceptions::FormatException("".to_owned())); + } + + let mut resultObject = RXingResult::new( + &resultString, + Vec::new(), // no natural byte representation for these barcodes + vec![ + RXingResultPoint::new(startRange[1] as f32, rowNumber as f32), + RXingResultPoint::new(endRange[0] as f32, rowNumber as f32), + ], + BarcodeFormat::ITF, + ); + + resultObject.putMetadata( + RXingResultMetadataType::SYMBOLOGY_IDENTIFIER, + RXingResultMetadataValue::SymbologyIdentifier("]I0".to_owned()), + ); + + Ok(resultObject) + } +} +impl ITFReader { + /** + * @param row row of black/white values to search + * @param payloadStart offset of start pattern + * @param resultString {@link StringBuilder} to append decoded chars to + * @throws NotFoundException if decoding could not complete successfully + */ + fn decodeMiddle( + row: &BitArray, + payloadStart: usize, + payloadEnd: usize, + resultString: &mut String, + ) -> Result<(), Exceptions> { + let mut payloadStart = payloadStart; + // Digits are interleaved in pairs - 5 black lines for one digit, and the + // 5 + // interleaved white lines for the second digit. + // Therefore, need to scan 10 lines and then + // split these into two arrays + let mut counterDigitPair = [0_u32; 10]; //new int[10]; + let mut counterBlack = [0_u32; 5]; //new int[5]; + let mut counterWhite = [0_u32; 5]; //new int[5]; + + while payloadStart < payloadEnd { + // Get 10 runs of black/white. + Self::recordPattern(row, payloadStart, &mut counterDigitPair)?; + // Split them into each array + for k in 0..5 { + // for (int k = 0; k < 5; k++) { + let twoK = 2 * k; + counterBlack[k] = counterDigitPair[twoK]; + counterWhite[k] = counterDigitPair[twoK + 1]; + } + + let mut bestMatch = Self::decodeDigit(&counterBlack)?; + resultString.push(char::from_u32('0' as u32 + bestMatch).unwrap()); + bestMatch = Self::decodeDigit(&counterWhite)?; + resultString.push(char::from_u32('0' as u32 + bestMatch).unwrap()); + + payloadStart += counterDigitPair.iter().sum::() as usize; + + // for counterDigit in counterDigitPair { + // // for (int counterDigit : counterDigitPair) { + // payloadStart += counterDigit; + // } + } + + Ok(()) + } + + /** + * Identify where the start of the middle / payload section starts. + * + * @param row row of black/white values to search + * @return Array, containing index of start of 'start block' and end of + * 'start block' + */ + fn decodeStart(&mut self, row: &BitArray) -> Result<[usize; 2], Exceptions> { + let endStart = Self::skipWhiteSpace(row)?; + 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 + // made up of 4 narrow lines. + self.narrowLineWidth = (startPattern[1] - startPattern[0]) as i32 / 4; + + self.validateQuietZone(row, startPattern[0])?; + + Ok(startPattern) + } + + /** + * The start & end patterns must be pre/post fixed by a quiet zone. This + * zone must be at least 10 times the width of a narrow line. Scan back until + * we either get to the start of the barcode or match the necessary number of + * quiet zone pixels. + * + * Note: Its assumed the row is reversed when using this method to find + * quiet zone after the end pattern. + * + * ref: http://www.barcode-1.net/i25code.html + * + * @param row bit array representing the scanned barcode. + * @param startPattern index into row of the start or end pattern. + * @throws NotFoundException if the quiet zone cannot be found + */ + fn validateQuietZone(&self, row: &BitArray, startPattern: usize) -> Result<(), Exceptions> { + let mut quietCount = self.narrowLineWidth * 10; // expect to find this many pixels of quiet zone + + // if there are not so many pixel at all let's try as many as possible + quietCount = quietCount.min(startPattern as i32); + + let mut i = startPattern as isize- 1 ; + while quietCount > 0 && i >= 0 { + // for (int i = startPattern - 1; quietCount > 0 && i >= 0; i--) { + if row.get(i as usize) { + break; + } + quietCount -= 1; + i -= 1; + } + + if quietCount != 0 { + // Unable to find the necessary number of quiet zone pixels. + Err(Exceptions::NotFoundException("".to_owned())) + } else { + Ok(()) + } + } + + /** + * Skip all whitespace until we get to the first black line. + * + * @param row row of black/white values to search + * @return index of the first black line. + * @throws NotFoundException Throws exception if no black lines are found in the row + */ + fn skipWhiteSpace(row: &BitArray) -> Result { + let width = row.getSize(); + let endStart = row.getNextSet(0); + if endStart == width { + return Err(Exceptions::NotFoundException("".to_owned())); + } + + Ok(endStart) + } + + /** + * Identify where the end of the middle / payload section ends. + * + * @param row row of black/white values to search + * @return Array, containing index of start of 'end block' and end of 'end + * block' + */ + fn decodeEnd(&self, row: &mut BitArray) -> Result<[usize; 2], Exceptions> { + // For convenience, reverse the row and then + // search from 'the start' for the end block + row.reverse(); + let interim_function = || -> Result<[usize; 2], Exceptions> { + let endStart = Self::skipWhiteSpace(row)?; + let mut endPattern; + endPattern = + if let Ok(ptrn) = Self::findGuardPattern(row, endStart, &END_PATTERN_REVERSED[0]) { + ptrn + } else { + Self::findGuardPattern(row, endStart, &END_PATTERN_REVERSED[1])? + }; + // try { + // endPattern = findGuardPattern(row, endStart, END_PATTERN_REVERSED[0]); + // } catch (NotFoundException nfe) { + // endPattern = findGuardPattern(row, endStart, END_PATTERN_REVERSED[1]); + // } + + // The start & end patterns must be pre/post fixed by a quiet zone. This + // zone must be at least 10 times the width of a narrow line. + // ref: http://www.barcode-1.net/i25code.html + self.validateQuietZone(row, endPattern[0])?; + + // Now recalculate the indices of where the 'endblock' starts & stops to + // accommodate + // the reversed nature of the search + let temp = endPattern[0]; + endPattern[0] = row.getSize() - endPattern[1]; + endPattern[1] = row.getSize() - temp; + + Ok(endPattern) + }; + let res = interim_function(); + // Put the row back the right way. + row.reverse(); + + res + } + + /** + * @param row row of black/white values to search + * @param rowOffset position to start search + * @param pattern pattern of counts of number of black and white pixels that are + * being searched for as a pattern + * @return start/end horizontal offset of guard pattern, as an array of two + * ints + * @throws NotFoundException if pattern is not found + */ + fn findGuardPattern( + row: &BitArray, + rowOffset: usize, + pattern: &[u32], + ) -> Result<[usize; 2], Exceptions> { + let patternLength = pattern.len(); + let mut counters = vec![0u32; patternLength]; //new int[patternLength]; + let width = row.getSize(); + let mut isWhite = false; + + let mut counterPosition = 0; + let mut patternStart = rowOffset; + for x in rowOffset..width { + // for (int x = rowOffset; x < width; x++) { + if row.get(x) != isWhite { + counters[counterPosition] += 1; + } else { + if counterPosition == patternLength - 1 { + if Self::patternMatchVariance(&counters, pattern, MAX_INDIVIDUAL_VARIANCE) + < MAX_AVG_VARIANCE + { + return Ok([patternStart, x]); + } + patternStart += (counters[0] + counters[1]) as usize; + let slc = &counters[2..(counterPosition - 1 + 2)].to_vec(); + counters[..(counterPosition - 1)].copy_from_slice(slc); + // System.arraycopy(counters, 2, counters, 0, counterPosition - 1); + counters[counterPosition - 1] = 0; + counters[counterPosition] = 0; + counterPosition -= 1; + } else { + counterPosition += 1; + } + counters[counterPosition] = 1; + isWhite = !isWhite; + } + } + return Err(Exceptions::NotFoundException("".to_owned())); + } + + /** + * Attempts to decode a sequence of ITF black/white lines into single + * digit. + * + * @param counters the counts of runs of observed black/white/black/... values + * @return The decoded digit + * @throws NotFoundException if digit cannot be decoded + */ + fn decodeDigit(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); + if variance < bestVariance { + bestVariance = variance; + bestMatch = i as isize; + } else if variance == bestVariance { + // if we find a second 'best match' with the same variance, we can not reliably report to have a suitable match + bestMatch = -1; + } + } + if bestMatch >= 0 { + Ok(bestMatch as u32 % 10) + } else { + Err(Exceptions::NotFoundException("".to_owned())) + } + } +} diff --git a/src/oned/mod.rs b/src/oned/mod.rs index c0ed462..b2bf3a4 100644 --- a/src/oned/mod.rs +++ b/src/oned/mod.rs @@ -20,3 +20,6 @@ pub use code_93_reader::*; mod code_128_reader; pub use code_128_reader::*; + +mod itf_reader; +pub use itf_reader::*; diff --git a/src/oned/multi_format_one_d_reader.rs b/src/oned/multi_format_one_d_reader.rs index fce26c0..2be0a07 100644 --- a/src/oned/multi_format_one_d_reader.rs +++ b/src/oned/multi_format_one_d_reader.rs @@ -18,6 +18,7 @@ use super::CodaBarReader; use super::Code128Reader; use super::Code39Reader; use super::Code93Reader; +use super::ITFReader; use super::OneDReader; use crate::BarcodeFormat; use crate::DecodeHintValue; @@ -80,9 +81,9 @@ impl MultiFormatOneDReader { if possibleFormats.contains(&BarcodeFormat::CODE_128) { readers.push(Box::new(Code128Reader {})); } - // if (possibleFormats.contains(&BarcodeFormat::ITF)) { - // readers.add(new ITFReader()); - // } + if (possibleFormats.contains(&BarcodeFormat::ITF)) { + readers.push(Box::new(ITFReader::default())); + } if possibleFormats.contains(&BarcodeFormat::CODABAR) { readers.push(Box::new(CodaBarReader::new())); } @@ -99,7 +100,7 @@ impl MultiFormatOneDReader { readers.push(Box::new(CodaBarReader::new())); readers.push(Box::new(Code93Reader::new())); readers.push(Box::new(Code128Reader {})); - // readers.push(new ITFReader()); + readers.push(Box::new(ITFReader::default())); // readers.push(new RSS14Reader()); // readers.push(new RSSExpandedReader()); } diff --git a/tests/ITFBlackBox1TestCase.java b/tests/ITFBlackBox1TestCase.java deleted file mode 100644 index 393b622..0000000 --- a/tests/ITFBlackBox1TestCase.java +++ /dev/null @@ -1,34 +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.MultiFormatReader; -import com.google.zxing.common.AbstractBlackBoxTestCase; - -/** - * @author kevin.osullivan@sita.aero - */ -public final class ITFBlackBox1TestCase extends AbstractBlackBoxTestCase { - - public ITFBlackBox1TestCase() { - super("src/test/resources/blackbox/itf-1", new MultiFormatReader(), BarcodeFormat.ITF); - addTest(14, 14, 0.0f); - addTest(14, 14, 180.0f); - } - -} diff --git a/tests/ITFBlackBox2TestCase.java b/tests/ITFBlackBox2TestCase.java deleted file mode 100644 index 5ef0212..0000000 --- a/tests/ITFBlackBox2TestCase.java +++ /dev/null @@ -1,34 +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.MultiFormatReader; -import com.google.zxing.common.AbstractBlackBoxTestCase; - -/** - * @author dswitkin@google.com (Daniel Switkin) - */ -public final class ITFBlackBox2TestCase extends AbstractBlackBoxTestCase { - - public ITFBlackBox2TestCase() { - super("src/test/resources/blackbox/itf-2", new MultiFormatReader(), BarcodeFormat.ITF); - addTest(13, 13, 0.0f); - addTest(13, 13, 180.0f); - } - -} diff --git a/tests/itf_blackbox.rs b/tests/itf_blackbox.rs new file mode 100644 index 0000000..9a7a929 --- /dev/null +++ b/tests/itf_blackbox.rs @@ -0,0 +1,58 @@ +/* + * 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 rxing::{ + oned::{CodaBarReader, Code39Reader, OneDReader}, + BarcodeFormat, MultiFormatReader, +}; + +mod common; + +/** + * @author kevin.osullivan@sita.aero + */ +#[test] +fn itfblack_box1_test_case() { + let mut tester = common::AbstractBlackBoxTestCase::new( + "test_resources/blackbox/itf-1", + MultiFormatReader::default(), + BarcodeFormat::ITF, + ); + + // super("src/test/resources/blackbox/itf-1", new MultiFormatReader(), BarcodeFormat.ITF); + tester.add_test(14, 14, 0.0); + tester.add_test(14, 14, 180.0); + + tester.test_black_box(); +} + +/** + * @author dswitkin@google.com (Daniel Switkin) + */ +#[test] +fn itfblack_box2_test_case() { + let mut tester = common::AbstractBlackBoxTestCase::new( + "test_resources/blackbox/itf-2", + MultiFormatReader::default(), + BarcodeFormat::ITF, + ); + + // super("src/test/resources/blackbox/itf-2", new MultiFormatReader(), BarcodeFormat.ITF); + tester.add_test(13, 13, 0.0); + tester.add_test(13, 13, 180.0); + + tester.test_black_box(); +}