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();
+}