1use crate::{
18 BarcodeFormat, Binarizer, Exceptions, RXingResult, RXingResultMetadataType,
19 RXingResultMetadataValue, Reader,
20 common::{BitArray, Result},
21 point,
22};
23
24use super::{EANManufacturerOrgSupport, OneDReader, UPCEANExtensionSupport, one_d_reader};
25
26use once_cell::sync::Lazy;
27
28pub static EAN_MANUFACTURER_SUPPORT: Lazy<EANManufacturerOrgSupport> =
29 Lazy::new(EANManufacturerOrgSupport::default);
30pub static UPC_EAN_EXTENSION_SUPPORT: Lazy<UPCEANExtensionSupport> =
31 Lazy::new(UPCEANExtensionSupport::default);
32
33pub const MAX_AVG_VARIANCE: f32 = 0.48;
37pub const MAX_INDIVIDUAL_VARIANCE: f32 = 0.7;
38
39pub const START_END_PATTERN: [u32; 3] = [1, 1, 1];
43
44pub const MIDDLE_PATTERN: [u32; 5] = [1, 1, 1, 1, 1];
48pub const END_PATTERN: [u32; 6] = [1, 1, 1, 1, 1, 1];
52pub const L_PATTERNS: [[u32; 4]; 10] = [
56 [3, 2, 1, 1], [2, 2, 2, 1], [2, 1, 2, 2], [1, 4, 1, 1], [1, 1, 3, 2], [1, 2, 3, 1], [1, 1, 1, 4], [1, 3, 1, 2], [1, 2, 1, 3], [3, 1, 1, 2], ];
67
68pub const L_AND_G_PATTERNS: [[u32; 4]; 20] = {
72 let mut new_array = [[0_u32; 4]; 20];
73 let mut i = 0;
74 while i < 10 {
75 new_array[i] = L_PATTERNS[i];
76 i += 1;
77 }
78 let mut i = 10;
79 while i < 20 {
80 let widths = &L_PATTERNS[i - 10];
81 let mut reversedWidths = [0_u32; 4];
82 let mut j = 0;
83 while j < 4 {
84 reversedWidths[j] = widths[4 - j - 1];
85
86 j += 1;
87 }
88 new_array[i] = reversedWidths;
89
90 i += 1;
91 }
92
93 new_array
94};
95
96pub trait UPCEANReader: OneDReader {
105 fn find_start_guard_pattern(&self, row: &BitArray) -> Result<[usize; 2]> {
106 let mut foundStart = false;
107 let mut startRange = [0; 2];
108 let mut nextStart = 0;
109 let mut counters = [0_u32; 3];
110 while !foundStart {
111 counters.fill(0);
112
113 startRange = self.findGuardPatternWithCounters(
114 row,
115 nextStart,
116 false,
117 &START_END_PATTERN,
118 &mut counters,
119 )?;
120 let start = startRange[0];
121 nextStart = startRange[1];
122
123 let quietStart = start as isize - (nextStart as isize - start as isize);
127 if quietStart >= 0 {
128 foundStart = row.isRange(quietStart as usize, start, false)?;
129 }
130 }
131
132 Ok(startRange)
133 }
134
135 fn decodeRowWithGuardRange(
150 &self,
151 rowNumber: u32,
152 row: &BitArray,
153 startGuardRange: &[usize; 2],
154 hints: &crate::DecodeHints,
155 ) -> Result<RXingResult> {
156 let resultPointCallback = &hints.NeedResultPointCallback;
157 let mut symbologyIdentifier = 0;
158
159 if let Some(cb) = resultPointCallback {
160 cb(point(
161 (startGuardRange[0] + startGuardRange[1]) as f32 / 2.0,
162 rowNumber as f32,
163 ));
164 }
165
166 let mut result = String::new();
167 let endStart = self.decodeMiddle(row, startGuardRange, &mut result)?;
168
169 if let Some(cb) = resultPointCallback {
170 cb(point(endStart as f32, rowNumber as f32));
171 }
172
173 let endRange = self.decodeEnd(row, endStart)?;
174
175 if let Some(cb) = resultPointCallback {
176 cb(point(
177 (endRange[0] + endRange[1]) as f32 / 2.0,
178 rowNumber as f32,
179 ));
180 }
181
182 let end = endRange[1];
185 let quietEnd = end + (end - endRange[0]);
186 if quietEnd >= row.get_size() || !row.isRange(end, quietEnd, false)? {
187 return Err(Exceptions::NOT_FOUND);
188 }
189
190 let resultString = result;
191
192 if resultString.chars().count() < 8 {
194 return Err(Exceptions::FORMAT);
195 }
196
197 if !self.checkChecksum(&resultString)? {
198 return Err(Exceptions::CHECKSUM);
199 }
200
201 let left = (startGuardRange[1] + startGuardRange[0]) as f32 / 2.0;
202 let right: f32 = (endRange[1] + endRange[0]) as f32 / 2.0;
203 let format = self.getBarcodeFormat();
204 let mut decodeRXingResult = RXingResult::new(
205 &resultString,
206 Vec::new(), vec![
208 point(left, rowNumber as f32),
209 point(right, rowNumber as f32),
210 ],
211 format,
212 );
213
214 let mut extensionLength = 0;
215
216 let mut attempt = || -> Result<()> {
217 let extensionRXingResult =
218 UPC_EAN_EXTENSION_SUPPORT.decodeRow(rowNumber, row, endRange[1])?;
219
220 decodeRXingResult.putMetadata(
221 RXingResultMetadataType::UPC_EAN_EXTENSION,
222 RXingResultMetadataValue::UpcEanExtension(
223 extensionRXingResult.getText().to_owned(),
224 ),
225 );
226 decodeRXingResult.putAllMetadata(extensionRXingResult.getRXingResultMetadata().clone());
227 decodeRXingResult.addPoints(&mut extensionRXingResult.getPoints().to_vec());
228 extensionLength = extensionRXingResult.getText().chars().count();
229 Ok(())
230 };
231
232 let _try_result = attempt();
233
234 if let Some(allowedExtensions) = &hints.AllowedEanExtensions {
235 let mut valid = false;
236 for length in allowedExtensions {
237 if extensionLength == *length as usize {
238 valid = true;
239 break;
240 }
241 }
242 if !valid {
243 return Err(Exceptions::NOT_FOUND);
244 }
245 }
246
247 if format == BarcodeFormat::EAN_13 || format == BarcodeFormat::UPC_A {
248 let countryID = EAN_MANUFACTURER_SUPPORT.lookupCountryIdentifier(&resultString);
249 if let Some(cid) = countryID {
250 decodeRXingResult.putMetadata(
251 RXingResultMetadataType::POSSIBLE_COUNTRY,
252 RXingResultMetadataValue::PossibleCountry(cid.to_owned()),
253 );
254 }
255 }
256
257 if format == BarcodeFormat::EAN_8 {
258 symbologyIdentifier = 4;
259 }
260
261 decodeRXingResult.putMetadata(
262 RXingResultMetadataType::SYMBOLOGY_IDENTIFIER,
263 RXingResultMetadataValue::SymbologyIdentifier(format!("]E{symbologyIdentifier}")),
264 );
265
266 Ok(decodeRXingResult)
267 }
268
269 fn checkChecksum(&self, s: &str) -> Result<bool> {
275 self.checkStandardUPCEANChecksum(s)
276 }
277
278 fn checkStandardUPCEANChecksum(&self, s: &str) -> Result<bool> {
287 let s = s.chars().collect::<Vec<_>>();
288 let length = s.len();
289 if length == 0 {
290 return Ok(false);
291 }
292 let char_in_question = *s.get(length - 1).ok_or(Exceptions::INDEX_OUT_OF_BOUNDS)?;
293 let check = char_in_question.is_ascii_digit();
294
295 let check_against = &s[..length - 1]; let calculated_checksum = self.getStandardUPCEANChecksum(check_against)?;
297
298 Ok(calculated_checksum
299 == if check {
300 char_in_question.to_digit(10).ok_or(Exceptions::PARSE)?
301 } else {
302 u32::MAX
303 })
304 }
305
306 fn getStandardUPCEANChecksum(&self, s: &[char]) -> Result<u32> {
307 let length = s.len();
308 let mut sum = 0;
309 let mut i = length as isize - 1;
310 while i >= 0 {
311 let digit =
313 (*s.get(i as usize).ok_or(Exceptions::INDEX_OUT_OF_BOUNDS)? as i32) - ('0' as i32);
314 if !(0..=9).contains(&digit) {
315 return Err(Exceptions::FORMAT);
316 }
317 sum += digit;
318
319 i -= 2;
320 }
321 sum *= 3;
322 let mut i = length as isize - 2;
323 while i >= 0 {
324 let digit =
326 (*s.get(i as usize).ok_or(Exceptions::INDEX_OUT_OF_BOUNDS)? as i32) - ('0' as i32);
327 if !(0..=9).contains(&digit) {
328 return Err(Exceptions::FORMAT);
329 }
330 sum += digit;
331
332 i -= 2;
333 }
334 Ok(((1000 - sum) % 10) as u32)
335 }
336
337 fn decodeEnd(&self, row: &BitArray, endStart: usize) -> Result<[usize; 2]> {
338 self.findGuardPattern(row, endStart, false, &START_END_PATTERN)
339 }
340
341 fn findGuardPattern<const N: usize>(
342 &self,
343 row: &BitArray,
344 rowOffset: usize,
345 whiteFirst: bool,
346 pattern: &[u32; N],
347 ) -> Result<[usize; 2]> {
348 self.findGuardPatternWithCounters(row, rowOffset, whiteFirst, pattern, &mut [0u32; N])
349 }
350
351 fn findGuardPatternWithCounters<const N: usize>(
363 &self,
364 row: &BitArray,
365 rowOffset: usize,
366 whiteFirst: bool,
367 pattern: &[u32; N],
368 counters: &mut [u32; N],
369 ) -> Result<[usize; 2]> {
370 let width = row.get_size();
371 let rowOffset = if whiteFirst {
372 row.getNextUnset(rowOffset)
373 } else {
374 row.getNextSet(rowOffset)
375 };
376 let mut counterPosition = 0;
377 let mut patternStart = rowOffset;
378 let patternLength = N;
379 let mut isWhite = whiteFirst;
380 for x in rowOffset..width {
381 if row.get(x) != isWhite {
383 counters[counterPosition] += 1;
384 } else {
385 if counterPosition == patternLength - 1 {
386 if one_d_reader::pattern_match_variance(
387 counters,
388 pattern,
389 MAX_INDIVIDUAL_VARIANCE,
390 ) < MAX_AVG_VARIANCE
391 {
392 return Ok([patternStart, x]);
393 }
394 patternStart += (counters[0] + counters[1]) as usize;
395
396 counters.copy_within(2..(counterPosition - 1 + 2), 0);
397
398 counters[counterPosition - 1] = 0;
399 counters[counterPosition] = 0;
400 counterPosition -= 1;
401 } else {
402 counterPosition += 1;
403 }
404 counters[counterPosition] = 1;
405 isWhite = !isWhite;
406 }
407 }
408
409 Err(Exceptions::NOT_FOUND)
410 }
411
412 fn decodeDigit(
425 &self,
426 row: &BitArray,
427 counters: &mut [u32; 4],
428 rowOffset: usize,
429 patterns: &[[u32; 4]],
430 ) -> Result<usize> {
431 one_d_reader::record_pattern(row, rowOffset, counters)?;
432 let mut bestVariance = MAX_AVG_VARIANCE; let mut bestMatch = -1_isize;
434 let max = patterns.len();
435 for (i, pattern) in patterns.iter().enumerate().take(max) {
436 let variance: f32 =
437 one_d_reader::pattern_match_variance(counters, pattern, MAX_INDIVIDUAL_VARIANCE);
438 if variance < bestVariance {
439 bestVariance = variance;
440 bestMatch = i as isize;
441 }
442 }
443 if bestMatch >= 0 {
444 Ok(bestMatch as usize)
445 } else {
446 Err(Exceptions::NOT_FOUND)
447 }
448 }
449
450 fn getBarcodeFormat(&self) -> BarcodeFormat;
456
457 fn decodeMiddle(
468 &self,
469 row: &BitArray,
470 startRange: &[usize; 2],
471 resultString: &mut String,
472 ) -> Result<usize>;
473}
474
475pub(crate) struct StandInStruct;
476impl UPCEANReader for StandInStruct {
477 fn getBarcodeFormat(&self) -> BarcodeFormat {
478 unimplemented!()
479 }
480
481 fn decodeMiddle(
482 &self,
483 _row: &BitArray,
484 _startRange: &[usize; 2],
485 _resultString: &mut String,
486 ) -> Result<usize> {
487 unimplemented!()
488 }
489}
490impl OneDReader for StandInStruct {
491 fn decode_row(
492 &mut self,
493 _rowNumber: u32,
494 _row: &BitArray,
495 _hints: &crate::DecodeHints,
496 ) -> Result<RXingResult> {
497 unimplemented!()
498 }
499}
500
501impl Reader for StandInStruct {
502 fn decode<B: Binarizer>(&mut self, _image: &mut crate::BinaryBitmap<B>) -> Result<RXingResult> {
503 unimplemented!()
504 }
505
506 fn decode_with_hints<B: Binarizer>(
507 &mut self,
508 _image: &mut crate::BinaryBitmap<B>,
509 _hints: &crate::DecodeHints,
510 ) -> Result<RXingResult> {
511 unimplemented!()
512 }
513}
514
515pub(crate) const STAND_IN: StandInStruct = StandInStruct {};