rss_14 ported but does not pass integration

This commit is contained in:
Henry Schimke
2022-12-13 17:39:12 -06:00
parent 63252a6297
commit ab4dab420b
17 changed files with 1138 additions and 935 deletions

View File

@@ -14,6 +14,7 @@
* limitations under the License.
*/
use super::rss::RSS14Reader;
use super::CodaBarReader;
use super::Code128Reader;
use super::Code39Reader;
@@ -86,9 +87,9 @@ impl MultiFormatOneDReader {
if possibleFormats.contains(&BarcodeFormat::CODABAR) {
readers.push(Box::new(CodaBarReader::new()));
}
// if (possibleFormats.contains(&BarcodeFormat::RSS_14)) {
// readers.add(new RSS14Reader());
// }
if possibleFormats.contains(&BarcodeFormat::RSS_14) {
readers.push(Box::new(RSS14Reader::new()));
}
// if (possibleFormats.contains(&BarcodeFormat::RSS_EXPANDED)) {
// readers.add(new RSSExpandedReader());
// }
@@ -100,7 +101,7 @@ impl MultiFormatOneDReader {
readers.push(Box::new(Code93Reader::new()));
readers.push(Box::new(Code128Reader {}));
readers.push(Box::new(ITFReader::default()));
// readers.push(new RSS14Reader());
readers.push(Box::new(RSS14Reader::new()));
// readers.push(new RSSExpandedReader());
}

View File

@@ -1,140 +0,0 @@
/*
* Copyright (C) 2010 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.rss;
import com.google.zxing.NotFoundException;
import com.google.zxing.common.detector.MathUtils;
import com.google.zxing.oned.OneDReader;
/**
* Superclass of {@link OneDReader} implementations that read barcodes in the RSS family
* of formats.
*/
public abstract class AbstractRSSReader extends OneDReader {
private static final float MAX_AVG_VARIANCE = 0.2f;
private static final float MAX_INDIVIDUAL_VARIANCE = 0.45f;
private static final float MIN_FINDER_PATTERN_RATIO = 9.5f / 12.0f;
private static final float MAX_FINDER_PATTERN_RATIO = 12.5f / 14.0f;
private final int[] decodeFinderCounters;
private final int[] dataCharacterCounters;
private final float[] oddRoundingErrors;
private final float[] evenRoundingErrors;
private final int[] oddCounts;
private final int[] evenCounts;
protected AbstractRSSReader() {
decodeFinderCounters = new int[4];
dataCharacterCounters = new int[8];
oddRoundingErrors = new float[4];
evenRoundingErrors = new float[4];
oddCounts = new int[dataCharacterCounters.length / 2];
evenCounts = new int[dataCharacterCounters.length / 2];
}
protected final int[] getDecodeFinderCounters() {
return decodeFinderCounters;
}
protected final int[] getDataCharacterCounters() {
return dataCharacterCounters;
}
protected final float[] getOddRoundingErrors() {
return oddRoundingErrors;
}
protected final float[] getEvenRoundingErrors() {
return evenRoundingErrors;
}
protected final int[] getOddCounts() {
return oddCounts;
}
protected final int[] getEvenCounts() {
return evenCounts;
}
protected static int parseFinderValue(int[] counters,
int[][] finderPatterns) throws NotFoundException {
for (int value = 0; value < finderPatterns.length; value++) {
if (patternMatchVariance(counters, finderPatterns[value], MAX_INDIVIDUAL_VARIANCE) <
MAX_AVG_VARIANCE) {
return value;
}
}
throw NotFoundException.getNotFoundInstance();
}
/**
* @param array values to sum
* @return sum of values
* @deprecated call {@link MathUtils#sum(int[])}
*/
@Deprecated
protected static int count(int[] array) {
return MathUtils.sum(array);
}
protected static void increment(int[] array, float[] errors) {
int index = 0;
float biggestError = errors[0];
for (int i = 1; i < array.length; i++) {
if (errors[i] > biggestError) {
biggestError = errors[i];
index = i;
}
}
array[index]++;
}
protected static void decrement(int[] array, float[] errors) {
int index = 0;
float biggestError = errors[0];
for (int i = 1; i < array.length; i++) {
if (errors[i] < biggestError) {
biggestError = errors[i];
index = i;
}
}
array[index]--;
}
protected static boolean isFinderPattern(int[] counters) {
int firstTwoSum = counters[0] + counters[1];
int sum = firstTwoSum + counters[2] + counters[3];
float ratio = firstTwoSum / (float) sum;
if (ratio >= MIN_FINDER_PATTERN_RATIO && ratio <= MAX_FINDER_PATTERN_RATIO) {
// passes ratio test in spec, but see if the counts are unreasonable
int minCounter = Integer.MAX_VALUE;
int maxCounter = Integer.MIN_VALUE;
for (int counter : counters) {
if (counter > maxCounter) {
maxCounter = counter;
}
if (counter < minCounter) {
minCounter = counter;
}
}
return maxCounter < 10 * minCounter;
}
return false;
}
}

View File

@@ -1,59 +0,0 @@
/*
* Copyright 2009 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.rss;
/**
* Encapsulates a since character value in an RSS barcode, including its checksum information.
*/
public class DataCharacter {
private final int value;
private final int checksumPortion;
public DataCharacter(int value, int checksumPortion) {
this.value = value;
this.checksumPortion = checksumPortion;
}
public final int getValue() {
return value;
}
public final int getChecksumPortion() {
return checksumPortion;
}
@Override
public final String toString() {
return value + "(" + checksumPortion + ')';
}
@Override
public final boolean equals(Object o) {
if (!(o instanceof DataCharacter)) {
return false;
}
DataCharacter that = (DataCharacter) o;
return value == that.value && checksumPortion == that.checksumPortion;
}
@Override
public final int hashCode() {
return value ^ checksumPortion;
}
}

View File

@@ -1,65 +0,0 @@
/*
* Copyright 2009 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.rss;
import com.google.zxing.RXingResultPoint;
/**
* Encapsulates an RSS barcode finder pattern, including its start/end position and row.
*/
public final class FinderPattern {
private final int value;
private final int[] startEnd;
private final RXingResultPoint[] resultPoints;
public FinderPattern(int value, int[] startEnd, int start, int end, int rowNumber) {
this.value = value;
this.startEnd = startEnd;
this.resultPoints = new RXingResultPoint[] {
new RXingResultPoint(start, rowNumber),
new RXingResultPoint(end, rowNumber),
};
}
public int getValue() {
return value;
}
public int[] getStartEnd() {
return startEnd;
}
public RXingResultPoint[] getRXingResultPoints() {
return resultPoints;
}
@Override
public boolean equals(Object o) {
if (!(o instanceof FinderPattern)) {
return false;
}
FinderPattern that = (FinderPattern) o;
return value == that.value;
}
@Override
public int hashCode() {
return value;
}
}

View File

@@ -1,41 +0,0 @@
/*
* Copyright 2009 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.rss;
final class Pair extends DataCharacter {
private final FinderPattern finderPattern;
private int count;
Pair(int value, int checksumPortion, FinderPattern finderPattern) {
super(value, checksumPortion);
this.finderPattern = finderPattern;
}
FinderPattern getFinderPattern() {
return finderPattern;
}
int getCount() {
return count;
}
void incrementCount() {
count++;
}
}

View File

@@ -1,471 +0,0 @@
/*
* Copyright 2009 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.rss;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.DecodeHintType;
import com.google.zxing.NotFoundException;
import com.google.zxing.RXingResult;
import com.google.zxing.RXingResultMetadataType;
import com.google.zxing.RXingResultPoint;
import com.google.zxing.RXingResultPointCallback;
import com.google.zxing.common.BitArray;
import com.google.zxing.common.detector.MathUtils;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
/**
* Decodes RSS-14, including truncated and stacked variants. See ISO/IEC 24724:2006.
*/
public final class RSS14Reader extends AbstractRSSReader {
private static final int[] OUTSIDE_EVEN_TOTAL_SUBSET = {1,10,34,70,126};
private static final int[] INSIDE_ODD_TOTAL_SUBSET = {4,20,48,81};
private static final int[] OUTSIDE_GSUM = {0,161,961,2015,2715};
private static final int[] INSIDE_GSUM = {0,336,1036,1516};
private static final int[] OUTSIDE_ODD_WIDEST = {8,6,4,3,1};
private static final int[] INSIDE_ODD_WIDEST = {2,4,6,8};
private static final int[][] FINDER_PATTERNS = {
{3,8,2,1},
{3,5,5,1},
{3,3,7,1},
{3,1,9,1},
{2,7,4,1},
{2,5,6,1},
{2,3,8,1},
{1,5,7,1},
{1,3,9,1},
};
private final List<Pair> possibleLeftPairs;
private final List<Pair> possibleRightPairs;
public RSS14Reader() {
possibleLeftPairs = new ArrayList<>();
possibleRightPairs = new ArrayList<>();
}
@Override
public RXingResult decodeRow(int rowNumber,
BitArray row,
Map<DecodeHintType,?> hints) throws NotFoundException {
Pair leftPair = decodePair(row, false, rowNumber, hints);
addOrTally(possibleLeftPairs, leftPair);
row.reverse();
Pair rightPair = decodePair(row, true, rowNumber, hints);
addOrTally(possibleRightPairs, rightPair);
row.reverse();
for (Pair left : possibleLeftPairs) {
if (left.getCount() > 1) {
for (Pair right : possibleRightPairs) {
if (right.getCount() > 1 && checkChecksum(left, right)) {
return constructRXingResult(left, right);
}
}
}
}
throw NotFoundException.getNotFoundInstance();
}
private static void addOrTally(Collection<Pair> possiblePairs, Pair pair) {
if (pair == null) {
return;
}
boolean found = false;
for (Pair other : possiblePairs) {
if (other.getValue() == pair.getValue()) {
other.incrementCount();
found = true;
break;
}
}
if (!found) {
possiblePairs.add(pair);
}
}
@Override
public void reset() {
possibleLeftPairs.clear();
possibleRightPairs.clear();
}
private static RXingResult constructRXingResult(Pair leftPair, Pair rightPair) {
long symbolValue = 4537077L * leftPair.getValue() + rightPair.getValue();
String text = String.valueOf(symbolValue);
StringBuilder buffer = new StringBuilder(14);
for (int i = 13 - text.length(); i > 0; i--) {
buffer.append('0');
}
buffer.append(text);
int checkDigit = 0;
for (int i = 0; i < 13; i++) {
int digit = buffer.charAt(i) - '0';
checkDigit += (i & 0x01) == 0 ? 3 * digit : digit;
}
checkDigit = 10 - (checkDigit % 10);
if (checkDigit == 10) {
checkDigit = 0;
}
buffer.append(checkDigit);
RXingResultPoint[] leftPoints = leftPair.getFinderPattern().getRXingResultPoints();
RXingResultPoint[] rightPoints = rightPair.getFinderPattern().getRXingResultPoints();
RXingResult result = new RXingResult(
buffer.toString(),
null,
new RXingResultPoint[] { leftPoints[0], leftPoints[1], rightPoints[0], rightPoints[1], },
BarcodeFormat.RSS_14);
result.putMetadata(RXingResultMetadataType.SYMBOLOGY_IDENTIFIER, "]e0");
return result;
}
private static boolean checkChecksum(Pair leftPair, Pair rightPair) {
int checkValue = (leftPair.getChecksumPortion() + 16 * rightPair.getChecksumPortion()) % 79;
int targetCheckValue =
9 * leftPair.getFinderPattern().getValue() + rightPair.getFinderPattern().getValue();
if (targetCheckValue > 72) {
targetCheckValue--;
}
if (targetCheckValue > 8) {
targetCheckValue--;
}
return checkValue == targetCheckValue;
}
private Pair decodePair(BitArray row, boolean right, int rowNumber, Map<DecodeHintType,?> hints) {
try {
int[] startEnd = findFinderPattern(row, right);
FinderPattern pattern = parseFoundFinderPattern(row, rowNumber, right, startEnd);
RXingResultPointCallback resultPointCallback = hints == null ? null :
(RXingResultPointCallback) hints.get(DecodeHintType.NEED_RESULT_POINT_CALLBACK);
if (resultPointCallback != null) {
startEnd = pattern.getStartEnd();
float center = (startEnd[0] + startEnd[1] - 1) / 2.0f;
if (right) {
// row is actually reversed
center = row.getSize() - 1 - center;
}
resultPointCallback.foundPossibleRXingResultPoint(new RXingResultPoint(center, rowNumber));
}
DataCharacter outside = decodeDataCharacter(row, pattern, true);
DataCharacter inside = decodeDataCharacter(row, pattern, false);
return new Pair(1597 * outside.getValue() + inside.getValue(),
outside.getChecksumPortion() + 4 * inside.getChecksumPortion(),
pattern);
} catch (NotFoundException ignored) {
return null;
}
}
private DataCharacter decodeDataCharacter(BitArray row, FinderPattern pattern, boolean outsideChar)
throws NotFoundException {
int[] counters = getDataCharacterCounters();
Arrays.fill(counters, 0);
if (outsideChar) {
recordPatternInReverse(row, pattern.getStartEnd()[0], counters);
} else {
recordPattern(row, pattern.getStartEnd()[1], counters);
// reverse it
for (int i = 0, j = counters.length - 1; i < j; i++, j--) {
int temp = counters[i];
counters[i] = counters[j];
counters[j] = temp;
}
}
int numModules = outsideChar ? 16 : 15;
float elementWidth = MathUtils.sum(counters) / (float) numModules;
int[] oddCounts = this.getOddCounts();
int[] evenCounts = this.getEvenCounts();
float[] oddRoundingErrors = this.getOddRoundingErrors();
float[] evenRoundingErrors = this.getEvenRoundingErrors();
for (int i = 0; i < counters.length; i++) {
float value = counters[i] / elementWidth;
int count = (int) (value + 0.5f); // Round
if (count < 1) {
count = 1;
} else if (count > 8) {
count = 8;
}
int offset = i / 2;
if ((i & 0x01) == 0) {
oddCounts[offset] = count;
oddRoundingErrors[offset] = value - count;
} else {
evenCounts[offset] = count;
evenRoundingErrors[offset] = value - count;
}
}
adjustOddEvenCounts(outsideChar, numModules);
int oddSum = 0;
int oddChecksumPortion = 0;
for (int i = oddCounts.length - 1; i >= 0; i--) {
oddChecksumPortion *= 9;
oddChecksumPortion += oddCounts[i];
oddSum += oddCounts[i];
}
int evenChecksumPortion = 0;
int evenSum = 0;
for (int i = evenCounts.length - 1; i >= 0; i--) {
evenChecksumPortion *= 9;
evenChecksumPortion += evenCounts[i];
evenSum += evenCounts[i];
}
int checksumPortion = oddChecksumPortion + 3 * evenChecksumPortion;
if (outsideChar) {
if ((oddSum & 0x01) != 0 || oddSum > 12 || oddSum < 4) {
throw NotFoundException.getNotFoundInstance();
}
int group = (12 - oddSum) / 2;
int oddWidest = OUTSIDE_ODD_WIDEST[group];
int evenWidest = 9 - oddWidest;
int vOdd = RSSUtils.getRSSvalue(oddCounts, oddWidest, false);
int vEven = RSSUtils.getRSSvalue(evenCounts, evenWidest, true);
int tEven = OUTSIDE_EVEN_TOTAL_SUBSET[group];
int gSum = OUTSIDE_GSUM[group];
return new DataCharacter(vOdd * tEven + vEven + gSum, checksumPortion);
} else {
if ((evenSum & 0x01) != 0 || evenSum > 10 || evenSum < 4) {
throw NotFoundException.getNotFoundInstance();
}
int group = (10 - evenSum) / 2;
int oddWidest = INSIDE_ODD_WIDEST[group];
int evenWidest = 9 - oddWidest;
int vOdd = RSSUtils.getRSSvalue(oddCounts, oddWidest, true);
int vEven = RSSUtils.getRSSvalue(evenCounts, evenWidest, false);
int tOdd = INSIDE_ODD_TOTAL_SUBSET[group];
int gSum = INSIDE_GSUM[group];
return new DataCharacter(vEven * tOdd + vOdd + gSum, checksumPortion);
}
}
private int[] findFinderPattern(BitArray row, boolean rightFinderPattern)
throws NotFoundException {
int[] counters = getDecodeFinderCounters();
counters[0] = 0;
counters[1] = 0;
counters[2] = 0;
counters[3] = 0;
int width = row.getSize();
boolean isWhite = false;
int rowOffset = 0;
while (rowOffset < width) {
isWhite = !row.get(rowOffset);
if (rightFinderPattern == isWhite) {
// Will encounter white first when searching for right finder pattern
break;
}
rowOffset++;
}
int counterPosition = 0;
int patternStart = rowOffset;
for (int x = rowOffset; x < width; x++) {
if (row.get(x) != isWhite) {
counters[counterPosition]++;
} else {
if (counterPosition == 3) {
if (isFinderPattern(counters)) {
return new int[]{patternStart, x};
}
patternStart += counters[0] + counters[1];
counters[0] = counters[2];
counters[1] = counters[3];
counters[2] = 0;
counters[3] = 0;
counterPosition--;
} else {
counterPosition++;
}
counters[counterPosition] = 1;
isWhite = !isWhite;
}
}
throw NotFoundException.getNotFoundInstance();
}
private FinderPattern parseFoundFinderPattern(BitArray row, int rowNumber, boolean right, int[] startEnd)
throws NotFoundException {
// Actually we found elements 2-5
boolean firstIsBlack = row.get(startEnd[0]);
int firstElementStart = startEnd[0] - 1;
// Locate element 1
while (firstElementStart >= 0 && firstIsBlack != row.get(firstElementStart)) {
firstElementStart--;
}
firstElementStart++;
int firstCounter = startEnd[0] - firstElementStart;
// Make 'counters' hold 1-4
int[] counters = getDecodeFinderCounters();
System.arraycopy(counters, 0, counters, 1, counters.length - 1);
counters[0] = firstCounter;
int value = parseFinderValue(counters, FINDER_PATTERNS);
int start = firstElementStart;
int end = startEnd[1];
if (right) {
// row is actually reversed
start = row.getSize() - 1 - start;
end = row.getSize() - 1 - end;
}
return new FinderPattern(value, new int[] {firstElementStart, startEnd[1]}, start, end, rowNumber);
}
private void adjustOddEvenCounts(boolean outsideChar, int numModules) throws NotFoundException {
int oddSum = MathUtils.sum(getOddCounts());
int evenSum = MathUtils.sum(getEvenCounts());
boolean incrementOdd = false;
boolean decrementOdd = false;
boolean incrementEven = false;
boolean decrementEven = false;
if (outsideChar) {
if (oddSum > 12) {
decrementOdd = true;
} else if (oddSum < 4) {
incrementOdd = true;
}
if (evenSum > 12) {
decrementEven = true;
} else if (evenSum < 4) {
incrementEven = true;
}
} else {
if (oddSum > 11) {
decrementOdd = true;
} else if (oddSum < 5) {
incrementOdd = true;
}
if (evenSum > 10) {
decrementEven = true;
} else if (evenSum < 4) {
incrementEven = true;
}
}
int mismatch = oddSum + evenSum - numModules;
boolean oddParityBad = (oddSum & 0x01) == (outsideChar ? 1 : 0);
boolean evenParityBad = (evenSum & 0x01) == 1;
/*if (mismatch == 2) {
if (!(oddParityBad && evenParityBad)) {
throw ReaderException.getInstance();
}
decrementOdd = true;
decrementEven = true;
} else if (mismatch == -2) {
if (!(oddParityBad && evenParityBad)) {
throw ReaderException.getInstance();
}
incrementOdd = true;
incrementEven = true;
} else */
switch (mismatch) {
case 1:
if (oddParityBad) {
if (evenParityBad) {
throw NotFoundException.getNotFoundInstance();
}
decrementOdd = true;
} else {
if (!evenParityBad) {
throw NotFoundException.getNotFoundInstance();
}
decrementEven = true;
}
break;
case -1:
if (oddParityBad) {
if (evenParityBad) {
throw NotFoundException.getNotFoundInstance();
}
incrementOdd = true;
} else {
if (!evenParityBad) {
throw NotFoundException.getNotFoundInstance();
}
incrementEven = true;
}
break;
case 0:
if (oddParityBad) {
if (!evenParityBad) {
throw NotFoundException.getNotFoundInstance();
}
// Both bad
if (oddSum < evenSum) {
incrementOdd = true;
decrementEven = true;
} else {
decrementOdd = true;
incrementEven = true;
}
} else {
if (evenParityBad) {
throw NotFoundException.getNotFoundInstance();
}
// Nothing to do!
}
break;
default:
throw NotFoundException.getNotFoundInstance();
}
if (incrementOdd) {
if (decrementOdd) {
throw NotFoundException.getNotFoundInstance();
}
increment(getOddCounts(), getOddRoundingErrors());
}
if (decrementOdd) {
decrement(getOddCounts(), getOddRoundingErrors());
}
if (incrementEven) {
if (decrementEven) {
throw NotFoundException.getNotFoundInstance();
}
increment(getEvenCounts(), getOddRoundingErrors());
}
if (decrementEven) {
decrement(getEvenCounts(), getEvenRoundingErrors());
}
}
}

View File

@@ -1,87 +0,0 @@
/*
* Copyright 2009 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.rss;
/** Adapted from listings in ISO/IEC 24724 Appendix B and Appendix G. */
public final class RSSUtils {
private RSSUtils() {}
public static int getRSSvalue(int[] widths, int maxWidth, boolean noNarrow) {
int n = 0;
for (int width : widths) {
n += width;
}
int val = 0;
int narrowMask = 0;
int elements = widths.length;
for (int bar = 0; bar < elements - 1; bar++) {
int elmWidth;
for (elmWidth = 1, narrowMask |= 1 << bar;
elmWidth < widths[bar];
elmWidth++, narrowMask &= ~(1 << bar)) {
int subVal = combins(n - elmWidth - 1, elements - bar - 2);
if (noNarrow && (narrowMask == 0) &&
(n - elmWidth - (elements - bar - 1) >= elements - bar - 1)) {
subVal -= combins(n - elmWidth - (elements - bar),
elements - bar - 2);
}
if (elements - bar - 1 > 1) {
int lessVal = 0;
for (int mxwElement = n - elmWidth - (elements - bar - 2);
mxwElement > maxWidth; mxwElement--) {
lessVal += combins(n - elmWidth - mxwElement - 1,
elements - bar - 3);
}
subVal -= lessVal * (elements - 1 - bar);
} else if (n - elmWidth > maxWidth) {
subVal--;
}
val += subVal;
}
n -= elmWidth;
}
return val;
}
private static int combins(int n, int r) {
int maxDenom;
int minDenom;
if (n - r > r) {
minDenom = r;
maxDenom = n - r;
} else {
minDenom = n - r;
maxDenom = r;
}
int val = 1;
int j = 1;
for (int i = n; i > maxDenom; i--) {
val *= i;
if (j <= minDenom) {
val /= j;
j++;
}
}
while (j <= minDenom) {
val /= j;
j++;
}
return val;
}
}

View File

@@ -0,0 +1,134 @@
/*
* Copyright (C) 2010 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::{oned::OneDReader, Exceptions};
/**
* Superclass of {@link OneDReader} implementations that read barcodes in the RSS family
* of formats.
*/
pub trait AbstractRSSReaderTrait: OneDReader {
const MAX_AVG_VARIANCE: f32 = 0.2;
const MAX_INDIVIDUAL_VARIANCE: f32 = 0.45;
const MIN_FINDER_PATTERN_RATIO: f32 = 9.5 / 12.0;
const MAX_FINDER_PATTERN_RATIO: f32 = 12.5 / 14.0;
// private final int[] decodeFinderCounters;
// private final int[] dataCharacterCounters;
// private final float[] oddRoundingErrors;
// private final float[] evenRoundingErrors;
// private final int[] oddCounts;
// private final int[] evenCounts;
// protected AbstractRSSReader() {
// decodeFinderCounters = new int[4];
// dataCharacterCounters = new int[8];
// oddRoundingErrors = new float[4];
// evenRoundingErrors = new float[4];
// oddCounts = new int[dataCharacterCounters.length / 2];
// evenCounts = new int[dataCharacterCounters.length / 2];
// }
fn getDecodeFinderCounters(&mut self) -> &mut [u32];
fn getDataCharacterCounters(&mut self) -> &mut [u32];
fn getOddRoundingErrors(&mut self) -> &mut [f32];
fn getEvenRoundingErrors(&mut self) -> &mut [f32];
fn getOddCounts(&mut self) -> &mut [u32];
fn getEvenCounts(&mut self) -> &mut [u32];
fn parseFinderValue(
&self,
counters: &[u32],
finderPatterns: &[[u32; 4]; 9],
) -> Result<u32, Exceptions> {
for value in 0..finderPatterns.len() {
// for (int value = 0; value < finderPatterns.length; value++) {
if self.patternMatchVariance(
counters,
&finderPatterns[value],
Self::MAX_INDIVIDUAL_VARIANCE,
) < Self::MAX_AVG_VARIANCE
{
return Ok(value as u32);
}
}
Err(Exceptions::NotFoundException("".to_owned()))
}
/**
* @param array values to sum
* @return sum of values
* @deprecated call {@link MathUtils#sum(int[])}
*/
#[deprecated]
fn count(array: &[u32]) -> u32 {
array.iter().sum::<u32>()
}
fn increment(array: &mut [u32], errors: &[f32]) {
let mut index = 0;
let mut biggestError = errors[0];
for i in 1..array.len() {
// for (int i = 1; i < array.length; i++) {
if errors[i] > biggestError {
biggestError = errors[i];
index = i;
}
}
array[index] += 1;
}
fn decrement(array: &mut [u32], errors: &[f32]) {
let mut index = 0;
let mut biggestError = errors[0];
for i in 1..array.len() {
// for (int i = 1; i < array.length; i++) {
if errors[i] < biggestError {
biggestError = errors[i];
index = i;
}
}
array[index] -= 1;
}
fn isFinderPattern(&self, counters: &[u32]) -> bool {
let firstTwoSum = counters[0] + counters[1];
let sum = firstTwoSum + counters[2] + counters[3];
let ratio: f32 = (firstTwoSum as f32) / (sum as f32);
if ratio >= Self::MIN_FINDER_PATTERN_RATIO && ratio <= Self::MAX_FINDER_PATTERN_RATIO {
// passes ratio test in spec, but see if the counts are unreasonable
let mut minCounter = u32::MAX;
let mut maxCounter = u32::MIN;
for counter in counters {
// for (int counter : counters) {
if *counter > maxCounter {
maxCounter = *counter;
}
if *counter < minCounter {
minCounter = *counter;
}
}
return maxCounter < 10 * minCounter;
}
return false;
}
}

View File

@@ -0,0 +1,54 @@
/*
* Copyright 2009 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
use std::{fmt::Display, hash::Hash};
pub(crate) trait DataCharacterTrait: Display + Eq + PartialEq + Hash {
fn getValue(&self) -> u32;
fn getChecksumPortion(&self) -> u32;
}
/**
* Encapsulates a since character value in an RSS barcode, including its checksum information.
*/
#[derive(Hash, PartialEq, Eq)]
pub struct DataCharacter {
value: u32,
checksumPortion: u32,
}
impl DataCharacterTrait for DataCharacter {
fn getValue(&self) -> u32 {
self.value
}
fn getChecksumPortion(&self) -> u32 {
self.checksumPortion
}
}
impl DataCharacter {
pub fn new(value: u32, checksumPortion: u32) -> Self {
Self {
value,
checksumPortion,
}
}
}
impl Display for DataCharacter {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}({})", self.value, self.checksumPortion)
}
}

View File

@@ -0,0 +1,65 @@
/*
* Copyright 2009 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
use std::hash::Hash;
use crate::RXingResultPoint;
/**
* Encapsulates an RSS barcode finder pattern, including its start/end position and row.
*/
pub struct FinderPattern {
value: u32,
startEnd: [usize; 2],
resultPoints: Vec<RXingResultPoint>,
}
impl FinderPattern {
pub fn new(value: u32, startEnd: [usize; 2], start: usize, end: usize, rowNumber: u32) -> Self {
Self {
value,
startEnd,
resultPoints: vec![
RXingResultPoint::new(start as f32, rowNumber as f32),
RXingResultPoint::new(end as f32, rowNumber as f32),
],
}
}
pub fn getValue(&self) -> u32 {
self.value
}
pub fn getStartEnd(&self) -> &[usize] {
&self.startEnd
}
pub fn getRXingResultPoints(&self) -> &[RXingResultPoint] {
&self.resultPoints
}
}
impl PartialEq for FinderPattern {
fn eq(&self, other: &Self) -> bool {
self.value == other.value
}
}
impl Eq for FinderPattern {}
impl Hash for FinderPattern {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.value.hash(state);
}
}

View File

@@ -1 +1,18 @@
pub mod expanded;
mod finder_pattern;
pub use finder_pattern::*;
mod pair;
pub use pair::*;
mod data_character;
pub use data_character::*;
pub mod rss_utils;
mod abstract_rss_reader;
pub use abstract_rss_reader::*;
mod rss_14_reader;
pub use rss_14_reader::*;

63
src/oned/rss/pair.rs Normal file
View File

@@ -0,0 +1,63 @@
use std::fmt::Display;
use super::{DataCharacter, DataCharacterTrait, FinderPattern};
/*
* Copyright 2009 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.
*/
#[derive(Hash, Eq, PartialEq)]
pub struct Pair {
finderPattern: FinderPattern,
count: u32,
internal_data_character: DataCharacter,
}
impl DataCharacterTrait for Pair {
fn getValue(&self) -> u32 {
self.internal_data_character.getValue()
}
fn getChecksumPortion(&self) -> u32 {
self.internal_data_character.getChecksumPortion()
}
}
impl Pair {
pub fn new(value: u32, checksumPortion: u32, finderPattern: FinderPattern) -> Self {
Self {
finderPattern,
count: 0,
internal_data_character: DataCharacter::new(value, checksumPortion),
}
}
pub fn getFinderPattern(&self) -> &FinderPattern {
&self.finderPattern
}
pub fn getCount(&self) -> u32 {
self.count
}
pub fn incrementCount(&mut self) {
self.count += 1;
}
}
impl Display for Pair {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.internal_data_character.fmt(f)
}
}

View File

@@ -0,0 +1,641 @@
/*
* Copyright 2009 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
use std::collections::HashMap;
use crate::{
common::{detector::MathUtils, BitArray},
oned::OneDReader,
BarcodeFormat, DecodeHintType, DecodingHintDictionary, Exceptions, RXingResult,
RXingResultMetadataType, RXingResultMetadataValue, RXingResultPoint, Reader, ResultPoint,
};
use super::{
rss_utils, AbstractRSSReaderTrait, DataCharacter, DataCharacterTrait, FinderPattern, Pair,
};
/**
* Decodes RSS-14, including truncated and stacked variants. See ISO/IEC 24724:2006.
*/
pub struct RSS14Reader {
possibleLeftPairs: Vec<Pair>,
possibleRightPairs: Vec<Pair>,
decodeFinderCounters: [u32; 4],
dataCharacterCounters: [u32; 8],
oddRoundingErrors: [f32; 4],
evenRoundingErrors: [f32; 4],
oddCounts: [u32; 4],
evenCounts: [u32; 4],
}
impl AbstractRSSReaderTrait for RSS14Reader {
fn getDecodeFinderCounters(&mut self) -> &mut [u32] {
&mut self.decodeFinderCounters
}
fn getDataCharacterCounters(&mut self) -> &mut [u32] {
&mut self.dataCharacterCounters
}
fn getOddRoundingErrors(&mut self) -> &mut [f32] {
&mut self.oddRoundingErrors
}
fn getEvenRoundingErrors(&mut self) -> &mut [f32] {
&mut self.evenRoundingErrors
}
fn getOddCounts(&mut self) -> &mut [u32] {
&mut self.oddCounts
}
fn getEvenCounts(&mut self) -> &mut [u32] {
&mut self.evenCounts
}
}
impl OneDReader for RSS14Reader {
fn decodeRow(
&mut self,
rowNumber: u32,
row: &crate::common::BitArray,
hints: &crate::DecodingHintDictionary,
) -> Result<crate::RXingResult, crate::Exceptions> {
let mut row = row.clone();
let leftPair = self.decodePair(&row, false, rowNumber, hints);
Self::addOrTally(&mut self.possibleLeftPairs, leftPair);
row.reverse();
let rightPair = self.decodePair(&row, true, rowNumber, hints);
Self::addOrTally(&mut self.possibleRightPairs, rightPair);
row.reverse();
for left in &self.possibleLeftPairs {
// for (Pair left : possibleLeftPairs) {
if left.getCount() > 1 {
for right in &self.possibleRightPairs {
// for (Pair right : possibleRightPairs) {
if right.getCount() > 1 && self.checkChecksum(&left, &right) {
return Ok(self.constructRXingResult(&left, &right));
}
}
}
}
return Err(Exceptions::NotFoundException("".to_owned()));
}
}
impl Reader for RSS14Reader {
fn decode(&mut self, image: &crate::BinaryBitmap) -> Result<crate::RXingResult, Exceptions> {
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<crate::RXingResult, Exceptions> {
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) {
self.possibleLeftPairs.clear();
self.possibleRightPairs.clear();
}
}
impl RSS14Reader {
const OUTSIDE_EVEN_TOTAL_SUBSET: [u32; 5] = [1, 10, 34, 70, 126];
const INSIDE_ODD_TOTAL_SUBSET: [u32; 4] = [4, 20, 48, 81];
const OUTSIDE_GSUM: [u32; 5] = [0, 161, 961, 2015, 2715];
const INSIDE_GSUM: [u32; 4] = [0, 336, 1036, 1516];
const OUTSIDE_ODD_WIDEST: [u32; 5] = [8, 6, 4, 3, 1];
const INSIDE_ODD_WIDEST: [u32; 4] = [2, 4, 6, 8];
const FINDER_PATTERNS: [[u32; 4]; 9] = [
[3, 8, 2, 1],
[3, 5, 5, 1],
[3, 3, 7, 1],
[3, 1, 9, 1],
[2, 7, 4, 1],
[2, 5, 6, 1],
[2, 3, 8, 1],
[1, 5, 7, 1],
[1, 3, 9, 1],
];
pub fn new() -> Self {
Self {
possibleLeftPairs: Vec::new(),
possibleRightPairs: Vec::new(),
decodeFinderCounters: [0u32; 4],
dataCharacterCounters: [0u32; 8],
oddRoundingErrors: [0.0; 4],
evenRoundingErrors: [0.0; 4],
oddCounts: [0u32; 4], //vec![0u32;4],
evenCounts: [0u32; 4], //vec![0u32;4],
}
}
fn addOrTally(possiblePairs: &mut Vec<Pair>, pair: Option<Pair>) {
let Some(pair) = pair else {
return;
};
let mut found = false;
for other in possiblePairs.iter_mut() {
// for (Pair other : possiblePairs) {
if other.getValue() == pair.getValue() {
other.incrementCount();
found = true;
break;
}
}
if !found {
possiblePairs.push(pair);
}
}
fn constructRXingResult(&self, leftPair: &Pair, rightPair: &Pair) -> RXingResult {
let symbolValue: u64 = 4537077 * leftPair.getValue() as u64 + rightPair.getValue() as u64;
let text = symbolValue.to_string();
let mut buffer = String::with_capacity(14);
let mut i = 13 - text.chars().count() as isize;
while i > 0 {
// for (int i = 13 - text.length(); i > 0; i--) {
buffer.push('0');
i -= 1;
}
buffer.push_str(&text);
let mut checkDigit = 0;
for i in 0..13 {
// for (int i = 0; i < 13; i++) {
let digit = buffer.chars().nth(i).unwrap() as u32 - '0' as u32;
checkDigit += if (i & 0x01) == 0 { 3 * digit } else { digit };
}
checkDigit = 10 - (checkDigit % 10);
if checkDigit == 10 {
checkDigit = 0;
}
buffer.push_str(&checkDigit.to_string());
let leftPoints = leftPair.getFinderPattern().getRXingResultPoints();
let rightPoints = rightPair.getFinderPattern().getRXingResultPoints();
let mut result = RXingResult::new(
&buffer,
Vec::new(),
vec![leftPoints[0], leftPoints[1], rightPoints[0], rightPoints[1]],
BarcodeFormat::RSS_14,
);
result.putMetadata(
RXingResultMetadataType::SYMBOLOGY_IDENTIFIER,
RXingResultMetadataValue::SymbologyIdentifier("]e0".to_owned()),
);
result
}
fn checkChecksum(&self, leftPair: &Pair, rightPair: &Pair) -> bool {
let checkValue = (leftPair.getChecksumPortion() + 16 * rightPair.getChecksumPortion()) % 79;
let mut targetCheckValue =
9 * leftPair.getFinderPattern().getValue() + rightPair.getFinderPattern().getValue();
if targetCheckValue > 72 {
targetCheckValue -= 1;
}
if targetCheckValue > 8 {
targetCheckValue -= 1;
}
checkValue == targetCheckValue
}
fn decodePair(
&mut self,
row: &BitArray,
right: bool,
rowNumber: u32,
hints: &DecodingHintDictionary,
) -> Option<Pair> {
let pos_pair = || -> Result<Pair, Exceptions> {
let startEnd = self.findFinderPattern(row, right)?;
let pattern = self.parseFoundFinderPattern(row, rowNumber, right, &startEnd)?;
// RXingResultPointCallback resultPointCallback = hints == null ? null :
// (RXingResultPointCallback) hints.get(DecodeHintType.NEED_RESULT_POINT_CALLBACK);
// if (resultPointCallback != null) {
// startEnd = pattern.getStartEnd();
// float center = (startEnd[0] + startEnd[1] - 1) / 2.0f;
// if (right) {
// // row is actually reversed
// center = row.getSize() - 1 - center;
// }
// resultPointCallback.foundPossibleRXingResultPoint(new RXingResultPoint(center, rowNumber));
// }
let outside = self.decodeDataCharacter(row, &pattern, true)?;
let inside = self.decodeDataCharacter(row, &pattern, false)?;
// todo!("must add callback");
Ok(Pair::new(
1597 * outside.getValue() + inside.getValue(),
outside.getChecksumPortion() + 4 * inside.getChecksumPortion(),
pattern,
))
}();
if pos_pair.is_err() {
None
} else {
Some(pos_pair.unwrap())
}
}
fn decodeDataCharacter(
&mut self,
row: &BitArray,
pattern: &FinderPattern,
outsideChar: bool,
) -> Result<DataCharacter, Exceptions> {
let mut counters = [0_u32; 8]; //self.getDataCharacterCounters();
//Arrays.fill(counters, 0);
// counters.fill(0);
if outsideChar {
self.recordPatternInReverse(row, pattern.getStartEnd()[0], &mut counters)?;
} else {
self.recordPattern(row, pattern.getStartEnd()[1], &mut counters)?;
// reverse it
let mut i = 0;
let mut j = counters.len();
while i < j {
// for (int i = 0, j = counters.length - 1; i < j; i++, j--) {
let temp = counters[i];
counters[i] = counters[j];
counters[j] = temp;
i += 1;
j -= 1;
}
}
let numModules = if outsideChar { 16 } else { 15 };
let elementWidth: f32 = counters.iter().sum::<u32>() as f32 / numModules as f32;
let mut oddCounts = [0u32; 4]; //self.getOddCounts();
let mut evenCounts = [0u32; 4]; // self.getEvenCounts();
let mut oddRoundingErrors = [0f32; 4]; // self.getOddRoundingErrors();
let mut evenRoundingErrors = [0f32; 4]; // self.getEvenRoundingErrors();
for i in 0..counters.len() {
// for (int i = 0; i < counters.length; i++) {
let value: f32 = counters[i] as f32 / elementWidth;
let mut count = (value + 0.5) as u32; // Round
if count < 1 {
count = 1;
} else if count > 8 {
count = 8;
}
let offset = i / 2;
if (i & 0x01) == 0 {
oddCounts[offset] = count;
oddRoundingErrors[offset] = value - count as f32;
} else {
evenCounts[offset] = count;
evenRoundingErrors[offset] = value - count as f32;
}
}
self.adjustOddEvenCounts(outsideChar, numModules)?;
let mut oddSum = 0;
let mut oddChecksumPortion = 0;
for i in (0..oddCounts.len()).rev() {
// for (int i = oddCounts.length - 1; i >= 0; i--) {
oddChecksumPortion *= 9;
oddChecksumPortion += oddCounts[i];
oddSum += oddCounts[i];
}
let mut evenChecksumPortion = 0;
let mut evenSum = 0;
for i in (0..evenCounts.len()).rev() {
// for (int i = evenCounts.length - 1; i >= 0; i--) {
evenChecksumPortion *= 9;
evenChecksumPortion += evenCounts[i];
evenSum += evenCounts[i];
}
let checksumPortion = oddChecksumPortion + 3 * evenChecksumPortion;
self.oddCounts = oddCounts;
self.evenCounts = evenCounts;
self.oddRoundingErrors = oddRoundingErrors;
self.evenRoundingErrors = evenRoundingErrors;
if outsideChar {
if (oddSum & 0x01) != 0 || oddSum > 12 || oddSum < 4 {
return Err(Exceptions::NotFoundException("".to_owned()));
}
let group = ((12 - oddSum) / 2) as usize;
let oddWidest = Self::OUTSIDE_ODD_WIDEST[group];
let evenWidest = 9 - oddWidest;
let vOdd = rss_utils::getRSSvalue(&oddCounts, oddWidest, false);
let vEven = rss_utils::getRSSvalue(&evenCounts, evenWidest, true);
let tEven = Self::OUTSIDE_EVEN_TOTAL_SUBSET[group];
let gSum = Self::OUTSIDE_GSUM[group];
return Ok(DataCharacter::new(
vOdd * tEven + vEven + gSum,
checksumPortion,
));
} else {
if (evenSum & 0x01) != 0 || evenSum > 10 || evenSum < 4 {
return Err(Exceptions::NotFoundException("".to_owned()));
}
let group = ((10 - evenSum) / 2) as usize;
let oddWidest = Self::INSIDE_ODD_WIDEST[group];
let evenWidest = 9 - oddWidest;
let vOdd = rss_utils::getRSSvalue(&oddCounts, oddWidest, true);
let vEven = rss_utils::getRSSvalue(&evenCounts, evenWidest, false);
let tOdd = Self::INSIDE_ODD_TOTAL_SUBSET[group];
let gSum = Self::INSIDE_GSUM[group];
return Ok(DataCharacter::new(
vEven * tOdd + vOdd + gSum,
checksumPortion,
));
}
}
fn findFinderPattern(
&mut self,
row: &BitArray,
rightFinderPattern: bool,
) -> Result<[usize; 2], Exceptions> {
// let counters = self.getDecodeFinderCounters();
// counters[0] = 0;
// counters[1] = 0;
// counters[2] = 0;
// counters[3] = 0;
let mut counters = [0u32; 4];
let width = row.getSize();
let mut isWhite = false;
let mut rowOffset = 0;
while rowOffset < width {
isWhite = !row.get(rowOffset);
if rightFinderPattern == isWhite {
// Will encounter white first when searching for right finder pattern
break;
}
rowOffset += 1;
}
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 == 3 {
if self.isFinderPattern(&counters) {
return Ok([patternStart, x]);
}
patternStart += (counters[0] + counters[1]) as usize;
counters[0] = counters[2];
counters[1] = counters[3];
counters[2] = 0;
counters[3] = 0;
counterPosition -= 1;
} else {
counterPosition += 1;
}
counters[counterPosition] = 1;
isWhite = !isWhite;
}
}
return Err(Exceptions::NotFoundException("".to_owned()));
}
fn parseFoundFinderPattern(
&mut self,
row: &BitArray,
rowNumber: u32,
right: bool,
startEnd: &[usize],
) -> Result<FinderPattern, Exceptions> {
// Actually we found elements 2-5
let firstIsBlack = row.get(startEnd[0]);
let mut firstElementStart = startEnd[0] as isize - 1;
// Locate element 1
while firstElementStart >= 0 && firstIsBlack != row.get(firstElementStart as usize) {
firstElementStart -= 1;
}
firstElementStart += 1;
let firstCounter = startEnd[0] - firstElementStart as usize;
// Make 'counters' hold 1-4
// let counters = self.getDecodeFinderCounters();
// System.arraycopy(counters, 0, counters, 1, counters.length - 1);
// counters.fill(0);
let mut counters = [0u32; 4];
counters[0] = firstCounter as u32;
let value = self.parseFinderValue(&counters, &Self::FINDER_PATTERNS)?;
let mut start = firstElementStart as usize;
let mut end = startEnd[1];
if right {
// row is actually reversed
start = row.getSize() - 1 - start;
end = row.getSize() - 1 - end;
}
Ok(FinderPattern::new(
value,
[firstElementStart as usize, startEnd[1]],
start,
end,
rowNumber,
))
}
fn adjustOddEvenCounts(
&mut self,
outsideChar: bool,
numModules: u32,
) -> Result<(), Exceptions> {
let oddSum = self.getOddCounts().iter().sum::<u32>(); //MathUtils.sum(getOddCounts());
let evenSum = self.getEvenCounts().iter().sum::<u32>(); //MathUtils.sum(getEvenCounts());
let mut incrementOdd = false;
let mut decrementOdd = false;
let mut incrementEven = false;
let mut decrementEven = false;
if outsideChar {
if oddSum > 12 {
decrementOdd = true;
} else if oddSum < 4 {
incrementOdd = true;
}
if evenSum > 12 {
decrementEven = true;
} else if evenSum < 4 {
incrementEven = true;
}
} else {
if oddSum > 11 {
decrementOdd = true;
} else if oddSum < 5 {
incrementOdd = true;
}
if evenSum > 10 {
decrementEven = true;
} else if evenSum < 4 {
incrementEven = true;
}
}
let mismatch = oddSum as i32 + evenSum as i32 - numModules as i32;
let oddParityBad = (oddSum & 0x01) == (if outsideChar { 1 } else { 0 });
let evenParityBad = (evenSum & 0x01) == 1;
/*if (mismatch == 2) {
if (!(oddParityBad && evenParityBad)) {
throw ReaderException.getInstance();
}
decrementOdd = true;
decrementEven = true;
} else if (mismatch == -2) {
if (!(oddParityBad && evenParityBad)) {
throw ReaderException.getInstance();
}
incrementOdd = true;
incrementEven = true;
} else */
match mismatch {
1 => {
if oddParityBad {
if evenParityBad {
return Err(Exceptions::NotFoundException("".to_owned()));
}
decrementOdd = true;
} else {
if !evenParityBad {
return Err(Exceptions::NotFoundException("".to_owned()));
}
decrementEven = true;
}
}
-1 => {
if oddParityBad {
if evenParityBad {
return Err(Exceptions::NotFoundException("".to_owned()));
}
incrementOdd = true;
} else {
if !evenParityBad {
return Err(Exceptions::NotFoundException("".to_owned()));
}
incrementEven = true;
}
}
0 => {
if oddParityBad {
if !evenParityBad {
return Err(Exceptions::NotFoundException("".to_owned()));
}
// Both bad
if oddSum < evenSum {
incrementOdd = true;
decrementEven = true;
} else {
decrementOdd = true;
incrementEven = true;
}
} else {
if evenParityBad {
return Err(Exceptions::NotFoundException("".to_owned()));
}
// Nothing to do!
}
}
_ => return Err(Exceptions::NotFoundException("".to_owned())),
}
if incrementOdd {
if decrementOdd {
return Err(Exceptions::NotFoundException("".to_owned()));
}
Self::increment(&mut self.oddCounts, &self.oddRoundingErrors);
}
if decrementOdd {
Self::decrement(&mut self.oddCounts, &self.oddRoundingErrors);
}
if incrementEven {
if decrementEven {
return Err(Exceptions::NotFoundException("".to_owned()));
}
Self::increment(&mut self.evenCounts, &self.evenRoundingErrors);
}
if decrementEven {
Self::decrement(&mut self.evenCounts, &self.evenRoundingErrors);
}
Ok(())
}
}

99
src/oned/rss/rss_utils.rs Normal file
View File

@@ -0,0 +1,99 @@
/*
* Copyright 2009 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.
*/
/** Adapted from listings in ISO/IEC 24724 Appendix B and Appendix G. */
pub fn getRSSvalue(widths: &[u32], maxWidth: u32, noNarrow: bool) -> u32 {
let mut n = 0;
// for width in widths {
// // for (int width : widths) {
// n += width;
// }
n += widths.iter().sum::<u32>();
let mut val = 0;
let mut narrowMask = 0;
let elements = widths.len() as u32;
for bar in 0..(elements - 1) {
// for (int bar = 0; bar < elements - 1; bar++) {
// let elmWidth;
let mut elmWidth = 1;
narrowMask |= 1 << bar;
while elmWidth < widths[bar as usize] {
// for (elmWidth = 1, narrowMask |= 1 << bar;
// elmWidth < widths[bar];
// elmWidth++, narrowMask &= ~(1 << bar)) {
let mut subVal = combins(n - elmWidth - 1, elements - bar - 2);
if noNarrow
&& (narrowMask == 0)
&& (n - elmWidth - (elements - bar - 1) >= elements - bar - 1)
{
subVal -= combins(n - elmWidth - (elements - bar), elements - bar - 2);
}
if elements - bar - 1 > 1 {
let mut lessVal = 0;
let mut mxwElement = n - elmWidth - (elements - bar - 2);
while mxwElement > maxWidth {
// for (int mxwElement = n - elmWidth - (elements - bar - 2);
// mxwElement > maxWidth; mxwElement--) {
lessVal += combins(n - elmWidth - mxwElement - 1, elements - bar - 3);
mxwElement -= 1;
}
subVal -= lessVal * (elements - 1 - bar);
} else if n - elmWidth > maxWidth {
subVal -= 1;
}
val += subVal;
elmWidth += 1;
narrowMask &= !(1 << bar)
}
n -= elmWidth;
}
return val;
}
fn combins(n: u32, r: u32) -> u32 {
let maxDenom;
let minDenom;
if n - r > r {
minDenom = r;
maxDenom = n - r;
} else {
minDenom = n - r;
maxDenom = r;
}
let mut val = 1;
let mut j = 1;
let mut i = n;
while i > maxDenom {
// for (int i = n; i > maxDenom; i--) {
val *= i;
if j <= minDenom {
val /= j;
j += 1;
}
i -= 1;
}
while j <= minDenom {
val /= j;
j += 1;
}
val
}

View File

@@ -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.rss;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.MultiFormatReader;
import com.google.zxing.common.AbstractBlackBoxTestCase;
/**
* @author Sean Owen
*/
public final class RSS14BlackBox1TestCase extends AbstractBlackBoxTestCase {
public RSS14BlackBox1TestCase() {
super("src/test/resources/blackbox/rss14-1", new MultiFormatReader(), BarcodeFormat.RSS_14);
addTest(6, 6, 0.0f);
addTest(6, 6, 180.0f);
}
}

View File

@@ -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.rss;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.MultiFormatReader;
import com.google.zxing.common.AbstractBlackBoxTestCase;
/**
* @author Sean Owen
*/
public final class RSS14BlackBox2TestCase extends AbstractBlackBoxTestCase {
public RSS14BlackBox2TestCase() {
super("src/test/resources/blackbox/rss14-2", new MultiFormatReader(), BarcodeFormat.RSS_14);
addTest(4, 8, 1, 1, 0.0f);
addTest(3, 8, 0, 1, 180.0f);
}
}

60
tests/rss_14_blackbox.rs Normal file
View File

@@ -0,0 +1,60 @@
/*
* 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::{maxicode::MaxiCodeReader, BarcodeFormat, DecodeHintType, MultiFormatReader};
mod common;
/**
* @author Sean Owen
*/
#[test]
fn rss14_black_box1_test_case() {
let mut tester = common::AbstractBlackBoxTestCase::new(
"test_resources/blackbox/rss14-1",
MultiFormatReader::default(),
BarcodeFormat::RSS_14,
);
// super("src/test/resources/blackbox/rss14-1", new MultiFormatReader(), BarcodeFormat.RSS_14);
tester.add_test(6, 6, 0.0);
tester.add_test(6, 6, 180.0);
tester.test_black_box()
}
/**
* @author Sean Owen
*/
#[test]
fn rss14_black_box2_test_case() {
let mut tester = common::AbstractBlackBoxTestCase::new(
"test_resources/blackbox/rss14-2",
MultiFormatReader::default(),
BarcodeFormat::RSS_14,
);
// super("src/test/resources/blackbox/rss14-2", new MultiFormatReader(), BarcodeFormat.RSS_14);
tester.add_test_complex(4, 8, 1, 1, 0.0);
tester.add_test_complex(3, 8, 0, 1, 180.0);
tester.test_black_box()
}