decodeRow method
Like {@link #decodeRow(int, BitArray, Map)}, but allows caller to inform method about where the UPC/EAN start pattern is found. This allows this to be computed once and reused across many implementations.
@param rowNumber row index into the image @param row encoding of the row of the barcode image @param startGuardRange start/end column where the opening start pattern was found @param hints optional hints that influence decoding @return Result encapsulating the result of decoding a barcode in the row @throws NotFoundException if no potential barcode is found @throws ChecksumException if a potential barcode is found but does not pass its checksum @throws FormatException if a potential barcode is found but format is invalid
Implementation
@override
Result decodeRow(
int rowNumber,
BitArray row,
Map<DecodeHintType, Object>? hints, [
List<int>? startGuardRange,
]) {
startGuardRange ??= findStartGuardPattern(row);
final resultPointCallback =
hints?[DecodeHintType.NEED_RESULT_POINT_CALLBACK]
as ResultPointCallback?;
int symbologyIdentifier = 0;
if (resultPointCallback != null) {
resultPointCallback.foundPossibleResultPoint(
ResultPoint(
(startGuardRange[0] + startGuardRange[1]) / 2.0,
rowNumber.toDouble(),
),
);
}
final result = _decodeRowStringBuffer;
result.clear();
final endStart = decodeMiddle(row, startGuardRange, result);
if (resultPointCallback != null) {
resultPointCallback.foundPossibleResultPoint(
ResultPoint(endStart.toDouble(), rowNumber.toDouble()),
);
}
final endRange = decodeEnd(row, endStart);
if (resultPointCallback != null) {
resultPointCallback.foundPossibleResultPoint(
ResultPoint((endRange[0] + endRange[1]) / 2.0, rowNumber.toDouble()),
);
}
// Make sure there is a quiet zone at least as big as the end pattern after the barcode. The
// spec might want more whitespace, but in practice this is the maximum we can count on.
final end = endRange[1];
final quietEnd = end + (end - endRange[0]);
if (quietEnd >= row.size || !row.isRange(end, quietEnd, false)) {
throw NotFoundException.instance;
}
final resultString = result.toString();
// UPC/EAN should never be less than 8 chars anyway
if (resultString.length < 8) {
throw FormatsException.instance;
}
if (!checkChecksum(resultString)) {
throw ChecksumException.getChecksumInstance();
}
final left = (startGuardRange[1] + startGuardRange[0]) / 2.0;
final right = (endRange[1] + endRange[0]) / 2.0;
final format = barcodeFormat;
final decodeResult = Result(
resultString,
null, // no natural byte representation for these barcodes
[
ResultPoint(left, rowNumber.toDouble()),
ResultPoint(right, rowNumber.toDouble())
],
format,
);
int extensionLength = 0;
try {
final extensionResult =
_extensionReader.decodeRow(rowNumber, row, endRange[1]);
decodeResult.putMetadata(
ResultMetadataType.UPC_EAN_EXTENSION,
extensionResult.text,
);
decodeResult.putAllMetadata(extensionResult.resultMetadata);
decodeResult.addResultPoints(extensionResult.resultPoints);
extensionLength = extensionResult.text.length;
} on ReaderException catch (_) {
// continue
}
final allowedExtensions =
hints?[DecodeHintType.ALLOWED_EAN_EXTENSIONS] as List<int>?;
if (allowedExtensions != null) {
bool valid = false;
for (int length in allowedExtensions) {
if (extensionLength == length) {
valid = true;
break;
}
}
if (!valid) {
throw NotFoundException.instance;
}
}
if (format == BarcodeFormat.EAN_13 || format == BarcodeFormat.UPC_A) {
final countryID = _eanManSupport.lookupCountryIdentifier(resultString);
if (countryID != null) {
decodeResult.putMetadata(
ResultMetadataType.POSSIBLE_COUNTRY,
countryID,
);
}
}
if (format == BarcodeFormat.EAN_8) {
symbologyIdentifier = 4;
}
decodeResult.putMetadata(
ResultMetadataType.SYMBOLOGY_IDENTIFIER,
']E$symbologyIdentifier',
);
return decodeResult;
}