Skip to main content

rxing/oned/
upc_ean_reader.rs

1/*
2 * Copyright 2008 ZXing authors
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17use 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
33// These two values are critical for determining how permissive the decoding will be.
34// We've arrived at these values through a lot of trial and error. Setting them any higher
35// lets false positives creep in quickly.
36pub const MAX_AVG_VARIANCE: f32 = 0.48;
37pub const MAX_INDIVIDUAL_VARIANCE: f32 = 0.7;
38
39/**
40 * Start/end guard pattern.
41 */
42pub const START_END_PATTERN: [u32; 3] = [1, 1, 1];
43
44/**
45 * Pattern marking the middle of a UPC/EAN pattern, separating the two halves.
46 */
47pub const MIDDLE_PATTERN: [u32; 5] = [1, 1, 1, 1, 1];
48/**
49 * end guard pattern.
50 */
51pub const END_PATTERN: [u32; 6] = [1, 1, 1, 1, 1, 1];
52/**
53 * "Odd", or "L" patterns used to encode UPC/EAN digits.
54 */
55pub const L_PATTERNS: [[u32; 4]; 10] = [
56    [3, 2, 1, 1], // 0
57    [2, 2, 2, 1], // 1
58    [2, 1, 2, 2], // 2
59    [1, 4, 1, 1], // 3
60    [1, 1, 3, 2], // 4
61    [1, 2, 3, 1], // 5
62    [1, 1, 1, 4], // 6
63    [1, 3, 1, 2], // 7
64    [1, 2, 1, 3], // 8
65    [3, 1, 1, 2], // 9
66];
67
68/**
69 * As above but also including the "even", or "G" patterns used to encode UPC/EAN digits.
70 */
71pub 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
96/**
97 * <p>Encapsulates functionality and implementation that is common to UPC and EAN families
98 * of one-dimensional barcodes.</p>
99 *
100 * @author dswitkin@google.com (Daniel Switkin)
101 * @author Sean Owen
102 * @author alasdair@google.com (Alasdair Mackintosh)
103 */
104pub 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            // Make sure there is a quiet zone at least as big as the start pattern before the barcode.
124            // If this check would run off the left edge of the image, do not accept this barcode,
125            // as it is very likely to be a false positive.
126            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    /**
136     * <p>Like {@link #decodeRow(int, BitArray, Map)}, but
137     * allows caller to inform method about where the UPC/EAN start pattern is
138     * found. This allows this to be computed once and reused across many implementations.</p>
139     *
140     * @param rowNumber row index into the image
141     * @param row encoding of the row of the barcode image
142     * @param startGuardRange start/end column where the opening start pattern was found
143     * @param hints optional hints that influence decoding
144     * @return {@link RXingResult} encapsulating the result of decoding a barcode in the row
145     * @throws NotFoundException if no potential barcode is found
146     * @throws ChecksumException if a potential barcode is found but does not pass its checksum
147     * @throws FormatException if a potential barcode is found but format is invalid
148     */
149    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        // Make sure there is a quiet zone at least as big as the end pattern after the barcode. The
183        // spec might want more whitespace, but in practice this is the maximum we can count on.
184        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        // UPC/EAN should never be less than 8 chars anyway
193        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(), // no natural byte representation for these barcodes
207            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    /**
270     * @param s string of digits to check
271     * @return {@link #checkStandardUPCEANChecksum(CharSequence)}
272     * @throws FormatException if the string does not contain only digits
273     */
274    fn checkChecksum(&self, s: &str) -> Result<bool> {
275        self.checkStandardUPCEANChecksum(s)
276    }
277
278    /**
279     * Computes the UPC/EAN checksum on a string of digits, and reports
280     * whether the checksum is correct or not.
281     *
282     * @param s string of digits to check
283     * @return true iff string of digits passes the UPC/EAN checksum algorithm
284     * @throws FormatException if the string does not contain only digits
285     */
286    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]; //s.subSequence(0, length - 1);
296        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            // for (int i = length - 1; i >= 0; i -= 2) {
312            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            // for (int i = length - 2; i >= 0; i -= 2) {
325            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    /**
352     * @param row row of black/white values to search
353     * @param rowOffset position to start search
354     * @param whiteFirst if true, indicates that the pattern specifies white/black/white/...
355     * pixel counts, otherwise, it is interpreted as black/white/black/...
356     * @param pattern pattern of counts of number of black and white pixels that are being
357     * searched for as a pattern
358     * @param counters array of counters, as long as pattern, to re-use
359     * @return start/end horizontal offset of guard pattern, as an array of two ints
360     * @throws NotFoundException if pattern is not found
361     */
362    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            // for (int x = rowOffset; x < width; x++) {
382            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    /**
413     * Attempts to decode a single UPC/EAN-encoded digit.
414     *
415     * @param row row of black/white values to decode
416     * @param counters the counts of runs of observed black/white/black/... values
417     * @param rowOffset horizontal offset to start decoding from
418     * @param patterns the set of patterns to use to decode -- sometimes different encodings
419     * for the digits 0-9 are used, and this indicates the encodings for 0 to 9 that should
420     * be used
421     * @return horizontal offset of first pixel beyond the decoded digit
422     * @throws NotFoundException if digit cannot be decoded
423     */
424    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; // worst variance we'll accept
433        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    /**
451     * Get the format of this decoder.
452     *
453     * @return The 1D format.
454     */
455    fn getBarcodeFormat(&self) -> BarcodeFormat;
456
457    /**
458     * Subclasses override this to decode the portion of a barcode between the start
459     * and end guard patterns.
460     *
461     * @param row row of black/white values to search
462     * @param startRange start/end offset of start guard pattern
463     * @param resultString {@link StringBuilder} to append decoded chars to
464     * @return horizontal offset of first pixel after the "middle" that was decoded
465     * @throws NotFoundException if decoding could not complete successfully
466     */
467    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 {};