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