rxing/pdf417/decoder/
detection_result_row_indicator_column.rs1use crate::pdf417::pdf_417_common;
18
19use super::{
20 BarcodeMetadata, BarcodeValue, Codeword, DetectionRXingResultColumn,
21 DetectionRXingResultColumnTrait,
22};
23
24pub trait DetectionRXingResultRowIndicatorColumn: DetectionRXingResultColumnTrait {
28 fn adjustCompleteIndicatorColumnRowNumbers(&mut self, barcodeMetadata: &BarcodeMetadata)
33 -> u32;
34 fn getRowHeights(&mut self) -> Option<Vec<u32>>;
35 fn getBarcodeMetadata(&mut self) -> Option<BarcodeMetadata>;
36 fn isLeft(&self) -> bool;
37}
38
39impl DetectionRXingResultRowIndicatorColumn for DetectionRXingResultColumn {
40 fn adjustCompleteIndicatorColumnRowNumbers(
45 &mut self,
46 barcodeMetadata: &BarcodeMetadata,
47 ) -> u32 {
48 setRowNumbers(self.getCodewordsMut());
49
50 let isLeft = matches!(self.isLeft, Some(true));
51
52 removeIncorrectCodewords(self.getCodewordsMut(), barcodeMetadata, isLeft);
53
54 let boundingBox = self.getBoundingBox();
55 let top = if self.isLeft() {
56 boundingBox.getTopLeft()
57 } else {
58 boundingBox.getTopRight()
59 };
60 let bottom = if self.isLeft() {
61 boundingBox.getBottomLeft()
62 } else {
63 boundingBox.getBottomRight()
64 };
65
66 let firstRow = self.imageRowToCodewordIndex(top.y as u32);
67 let lastRow = self.imageRowToCodewordIndex(bottom.y as u32);
68 let averageRowHeight: f64 =
71 (lastRow as f64 - firstRow as f64) / barcodeMetadata.getRowCount() as f64;
72 let mut barcodeRow = -1;
73 let mut maxRowHeight = 1;
74 let mut currentRowHeight = 0;
75 for codewordsRow in firstRow..lastRow {
76 if let Some(codeword) = self.getCodewordsMut()[codewordsRow] {
77 let rowDifference = codeword.getRowNumber() - barcodeRow;
78
79 if rowDifference == 0 {
82 currentRowHeight += 1;
83 } else if rowDifference == 1 {
84 maxRowHeight = std::cmp::max(maxRowHeight, currentRowHeight);
85 currentRowHeight = 1;
86 barcodeRow = codeword.getRowNumber();
87 } else if rowDifference < 0
88 || codeword.getRowNumber() >= barcodeMetadata.getRowCount() as i32
89 || rowDifference > codewordsRow as i32
90 {
91 self.getCodewordsMut()[codewordsRow] = None;
92 } else {
93 let checkedRows = if maxRowHeight > 2 {
94 (maxRowHeight - 2) * rowDifference
95 } else {
96 rowDifference
97 };
98 let mut closePreviousCodewordFound = checkedRows >= codewordsRow as i32;
99 let mut i = 1;
100 while i <= checkedRows && !closePreviousCodewordFound {
101 closePreviousCodewordFound =
104 self.getCodewords()[codewordsRow - i as usize].is_some();
105
106 i += 1;
107 }
108 if closePreviousCodewordFound {
109 self.getCodewordsMut()[codewordsRow] = None;
110 } else {
111 barcodeRow = codeword.getRowNumber();
112 currentRowHeight = 1;
113 }
114 }
115 } else {
116 continue;
117 }
118 }
119 (averageRowHeight + 0.5) as u32
120 }
121
122 fn getRowHeights(&mut self) -> Option<Vec<u32>> {
123 if let Some(barcodeMetadata) = self.getBarcodeMetadata() {
124 adjustIncompleteIndicatorColumnRowNumbers(self, &barcodeMetadata);
125 let mut result = vec![0; barcodeMetadata.getRowCount() as usize];
126 for codeword in self.getCodewords().iter().flatten() {
127 let rowNumber = codeword.getRowNumber() as usize;
129 if rowNumber >= result.len() {
130 continue;
132 }
133 result[rowNumber] += 1;
134 }
140 Some(result)
141 } else {
142 None
143 }
144 }
145
146 fn getBarcodeMetadata(&mut self) -> Option<BarcodeMetadata> {
147 let isLeft = matches!(self.isLeft, Some(true));
148 let codewords = self.getCodewordsMut();
149 let mut barcodeColumnCount = BarcodeValue::new();
150 let mut barcodeRowCountUpperPart = BarcodeValue::new();
151 let mut barcodeRowCountLowerPart = BarcodeValue::new();
152 let mut barcodeECLevel = BarcodeValue::new();
153 for codeword in codewords.iter_mut().flatten() {
154 codeword.setRowNumberAsRowIndicatorColumn();
157 let rowIndicatorValue = codeword.getValue() % 30;
158 let mut codewordRowNumber = codeword.getRowNumber();
159 if !isLeft {
160 codewordRowNumber += 2;
161 }
162 match codewordRowNumber % 3 {
163 0 => barcodeRowCountUpperPart.setValue(rowIndicatorValue * 3 + 1),
164 1 => {
165 barcodeECLevel.setValue(rowIndicatorValue / 3);
166 barcodeRowCountLowerPart.setValue(rowIndicatorValue % 3);
167 }
168 2 => barcodeColumnCount.setValue(rowIndicatorValue + 1),
169 _ => {}
170 }
171 }
175 if barcodeColumnCount.getValue().is_empty()
177 || barcodeRowCountUpperPart.getValue().is_empty()
178 || barcodeRowCountLowerPart.getValue().is_empty()
179 || barcodeECLevel.getValue().is_empty()
180 || barcodeColumnCount.getValue()[0] < 1
181 || barcodeRowCountUpperPart.getValue()[0] + barcodeRowCountLowerPart.getValue()[0]
182 < pdf_417_common::MIN_ROWS_IN_BARCODE
183 || barcodeRowCountUpperPart.getValue()[0] + barcodeRowCountLowerPart.getValue()[0]
184 > pdf_417_common::MAX_ROWS_IN_BARCODE
185 {
186 return None;
187 }
188 let barcodeMetadata = BarcodeMetadata::new(
189 barcodeColumnCount.getValue()[0],
190 barcodeRowCountUpperPart.getValue()[0],
191 barcodeRowCountLowerPart.getValue()[0],
192 barcodeECLevel.getValue()[0],
193 );
194 removeIncorrectCodewords(codewords, &barcodeMetadata, isLeft);
195
196 Some(barcodeMetadata)
197 }
198
199 fn isLeft(&self) -> bool {
200 matches!(self.isLeft, Some(true))
201 }
202}
203
204fn setRowNumbers(code_words: &mut [Option<Codeword>]) {
205 for codeword in code_words.iter_mut().flatten() {
206 codeword.setRowNumberAsRowIndicatorColumn();
207 }
208}
209
210fn removeIncorrectCodewords(
211 codewords: &mut [Option<Codeword>],
212 barcodeMetadata: &BarcodeMetadata,
213 isLeft: bool,
214) {
215 for codeword_row in codewords.iter_mut() {
218 if let Some(codeword) = codeword_row {
219 let rowIndicatorValue = codeword.getValue() % 30;
220 let mut codewordRowNumber = codeword.getRowNumber();
221 if codewordRowNumber > barcodeMetadata.getRowCount() as i32 {
222 *codeword_row = None;
223 continue;
224 }
225 if !isLeft {
226 codewordRowNumber += 2;
227 }
228 match codewordRowNumber % 3 {
229 0 if rowIndicatorValue * 3 + 1 != barcodeMetadata.getRowCountUpperPart() => {
230 *codeword_row = None;
231 }
232 1 if rowIndicatorValue / 3 != barcodeMetadata.getErrorCorrectionLevel()
233 || rowIndicatorValue % 3 != barcodeMetadata.getRowCountLowerPart() =>
234 {
235 *codeword_row = None;
236 }
237 2 if rowIndicatorValue + 1 != barcodeMetadata.getColumnCount() => {
238 *codeword_row = None;
239 }
240 _ => {}
241 }
242 } else {
243 continue;
244 }
245 }
246}
247
248fn adjustIncompleteIndicatorColumnRowNumbers(
252 col: &mut DetectionRXingResultColumn,
253 barcodeMetadata: &BarcodeMetadata,
254) -> i32 {
255 let boundingBox = col.getBoundingBox();
256 let top = if col.isLeft() {
257 boundingBox.getTopLeft()
258 } else {
259 boundingBox.getTopRight()
260 };
261 let bottom = if col.isLeft() {
262 boundingBox.getBottomLeft()
263 } else {
264 boundingBox.getBottomRight()
265 };
266 let firstRow = col.imageRowToCodewordIndex(top.y as u32);
267 let lastRow = col.imageRowToCodewordIndex(bottom.y as u32);
268 let averageRowHeight: f64 =
269 (lastRow as f64 - firstRow as f64) / barcodeMetadata.getRowCount() as f64;
270 let codewords = col.getCodewordsMut();
271 let mut barcodeRow = -1;
272 let mut maxRowHeight = 1;
273 let mut currentRowHeight = 0;
274 for codword_opt in codewords.iter_mut().take(lastRow).skip(firstRow) {
277 if let Some(codeword) = codword_opt {
280 codeword.setRowNumberAsRowIndicatorColumn();
281
282 let rowDifference = codeword.getRowNumber() - barcodeRow;
283
284 if rowDifference == 0 {
287 currentRowHeight += 1;
288 } else if rowDifference == 1 {
289 maxRowHeight = maxRowHeight.max(currentRowHeight);
290 currentRowHeight = 1;
291 barcodeRow = codeword.getRowNumber();
292 } else if codeword.getRowNumber() >= barcodeMetadata.getRowCount() as i32 {
293 *codword_opt = None;
294 } else {
295 barcodeRow = codeword.getRowNumber();
296 currentRowHeight = 1;
297 }
298 } else {
299 continue;
300 }
301 }
302 (averageRowHeight + 0.5) as i32
303}