use crate::pdf417::pdf_417_common;
use super::{
BarcodeMetadata, BarcodeValue, Codeword, DetectionRXingResultColumn,
DetectionRXingResultColumnTrait,
};
pub trait DetectionRXingResultRowIndicatorColumn: DetectionRXingResultColumnTrait {
fn adjustCompleteIndicatorColumnRowNumbers(&mut self, barcodeMetadata: &BarcodeMetadata)
-> u32;
fn getRowHeights(&mut self) -> Option<Vec<u32>>;
fn getBarcodeMetadata(&mut self) -> Option<BarcodeMetadata>;
fn isLeft(&self) -> bool;
}
impl DetectionRXingResultRowIndicatorColumn for DetectionRXingResultColumn {
fn adjustCompleteIndicatorColumnRowNumbers(
&mut self,
barcodeMetadata: &BarcodeMetadata,
) -> u32 {
setRowNumbers(self.getCodewordsMut());
let isLeft = matches!(self.isLeft, Some(true));
removeIncorrectCodewords(self.getCodewordsMut(), barcodeMetadata, isLeft);
let boundingBox = self.getBoundingBox();
let top = if self.isLeft() {
boundingBox.getTopLeft()
} else {
boundingBox.getTopRight()
};
let bottom = if self.isLeft() {
boundingBox.getBottomLeft()
} else {
boundingBox.getBottomRight()
};
let firstRow = self.imageRowToCodewordIndex(top.y as u32);
let lastRow = self.imageRowToCodewordIndex(bottom.y as u32);
let averageRowHeight: f64 =
(lastRow as f64 - firstRow as f64) / barcodeMetadata.getRowCount() as f64;
let mut barcodeRow = -1;
let mut maxRowHeight = 1;
let mut currentRowHeight = 0;
for codewordsRow in firstRow..lastRow {
if let Some(codeword) = self.getCodewordsMut()[codewordsRow] {
let rowDifference = codeword.getRowNumber() - barcodeRow;
if rowDifference == 0 {
currentRowHeight += 1;
} else if rowDifference == 1 {
maxRowHeight = std::cmp::max(maxRowHeight, currentRowHeight);
currentRowHeight = 1;
barcodeRow = codeword.getRowNumber();
} else if rowDifference < 0
|| codeword.getRowNumber() >= barcodeMetadata.getRowCount() as i32
|| rowDifference > codewordsRow as i32
{
self.getCodewordsMut()[codewordsRow] = None;
} else {
let checkedRows = if maxRowHeight > 2 {
(maxRowHeight - 2) * rowDifference
} else {
rowDifference
};
let mut closePreviousCodewordFound = checkedRows >= codewordsRow as i32;
let mut i = 1;
while i <= checkedRows && !closePreviousCodewordFound {
closePreviousCodewordFound =
self.getCodewords()[codewordsRow - i as usize].is_some();
i += 1;
}
if closePreviousCodewordFound {
self.getCodewordsMut()[codewordsRow] = None;
} else {
barcodeRow = codeword.getRowNumber();
currentRowHeight = 1;
}
}
} else {
continue;
}
}
(averageRowHeight + 0.5) as u32
}
fn getRowHeights(&mut self) -> Option<Vec<u32>> {
if let Some(barcodeMetadata) = self.getBarcodeMetadata() {
adjustIncompleteIndicatorColumnRowNumbers(self, &barcodeMetadata);
let mut result = vec![0; barcodeMetadata.getRowCount() as usize];
for codeword in self.getCodewords().iter().flatten() {
let rowNumber = codeword.getRowNumber() as usize;
if rowNumber >= result.len() {
continue;
}
result[rowNumber] += 1;
}
Some(result)
} else {
None
}
}
fn getBarcodeMetadata(&mut self) -> Option<BarcodeMetadata> {
let isLeft = matches!(self.isLeft, Some(true));
let codewords = self.getCodewordsMut();
let mut barcodeColumnCount = BarcodeValue::new();
let mut barcodeRowCountUpperPart = BarcodeValue::new();
let mut barcodeRowCountLowerPart = BarcodeValue::new();
let mut barcodeECLevel = BarcodeValue::new();
for codeword in codewords.iter_mut().flatten() {
codeword.setRowNumberAsRowIndicatorColumn();
let rowIndicatorValue = codeword.getValue() % 30;
let mut codewordRowNumber = codeword.getRowNumber();
if !isLeft {
codewordRowNumber += 2;
}
match codewordRowNumber % 3 {
0 => barcodeRowCountUpperPart.setValue(rowIndicatorValue * 3 + 1),
1 => {
barcodeECLevel.setValue(rowIndicatorValue / 3);
barcodeRowCountLowerPart.setValue(rowIndicatorValue % 3);
}
2 => barcodeColumnCount.setValue(rowIndicatorValue + 1),
_ => {}
}
}
if barcodeColumnCount.getValue().is_empty()
|| barcodeRowCountUpperPart.getValue().is_empty()
|| barcodeRowCountLowerPart.getValue().is_empty()
|| barcodeECLevel.getValue().is_empty()
|| barcodeColumnCount.getValue()[0] < 1
|| barcodeRowCountUpperPart.getValue()[0] + barcodeRowCountLowerPart.getValue()[0]
< pdf_417_common::MIN_ROWS_IN_BARCODE
|| barcodeRowCountUpperPart.getValue()[0] + barcodeRowCountLowerPart.getValue()[0]
> pdf_417_common::MAX_ROWS_IN_BARCODE
{
return None;
}
let barcodeMetadata = BarcodeMetadata::new(
barcodeColumnCount.getValue()[0],
barcodeRowCountUpperPart.getValue()[0],
barcodeRowCountLowerPart.getValue()[0],
barcodeECLevel.getValue()[0],
);
removeIncorrectCodewords(codewords, &barcodeMetadata, isLeft);
Some(barcodeMetadata)
}
fn isLeft(&self) -> bool {
matches!(self.isLeft, Some(true))
}
}
fn setRowNumbers(code_words: &mut [Option<Codeword>]) {
for codeword in code_words.iter_mut().flatten() {
codeword.setRowNumberAsRowIndicatorColumn();
}
}
fn removeIncorrectCodewords(
codewords: &mut [Option<Codeword>],
barcodeMetadata: &BarcodeMetadata,
isLeft: bool,
) {
for codeword_row in codewords.iter_mut() {
if let Some(codeword) = codeword_row {
let rowIndicatorValue = codeword.getValue() % 30;
let mut codewordRowNumber = codeword.getRowNumber();
if codewordRowNumber > barcodeMetadata.getRowCount() as i32 {
*codeword_row = None;
continue;
}
if !isLeft {
codewordRowNumber += 2;
}
match codewordRowNumber % 3 {
0 if rowIndicatorValue * 3 + 1 != barcodeMetadata.getRowCountUpperPart() => {
*codeword_row = None;
}
1 if rowIndicatorValue / 3 != barcodeMetadata.getErrorCorrectionLevel()
|| rowIndicatorValue % 3 != barcodeMetadata.getRowCountLowerPart() =>
{
*codeword_row = None;
}
2 if rowIndicatorValue + 1 != barcodeMetadata.getColumnCount() => {
*codeword_row = None;
}
_ => {}
}
} else {
continue;
}
}
}
fn adjustIncompleteIndicatorColumnRowNumbers(
col: &mut DetectionRXingResultColumn,
barcodeMetadata: &BarcodeMetadata,
) -> i32 {
let boundingBox = col.getBoundingBox();
let top = if col.isLeft() {
boundingBox.getTopLeft()
} else {
boundingBox.getTopRight()
};
let bottom = if col.isLeft() {
boundingBox.getBottomLeft()
} else {
boundingBox.getBottomRight()
};
let firstRow = col.imageRowToCodewordIndex(top.y as u32);
let lastRow = col.imageRowToCodewordIndex(bottom.y as u32);
let averageRowHeight: f64 =
(lastRow as f64 - firstRow as f64) / barcodeMetadata.getRowCount() as f64;
let codewords = col.getCodewordsMut();
let mut barcodeRow = -1;
let mut maxRowHeight = 1;
let mut currentRowHeight = 0;
for codword_opt in codewords.iter_mut().take(lastRow).skip(firstRow) {
if let Some(codeword) = codword_opt {
codeword.setRowNumberAsRowIndicatorColumn();
let rowDifference = codeword.getRowNumber() - barcodeRow;
if rowDifference == 0 {
currentRowHeight += 1;
} else if rowDifference == 1 {
maxRowHeight = maxRowHeight.max(currentRowHeight);
currentRowHeight = 1;
barcodeRow = codeword.getRowNumber();
} else if codeword.getRowNumber() >= barcodeMetadata.getRowCount() as i32 {
*codword_opt = None;
} else {
barcodeRow = codeword.getRowNumber();
currentRowHeight = 1;
}
} else {
continue;
}
}
(averageRowHeight + 0.5) as i32
}