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    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
32// These two values are critical for determining how permissive the decoding will be.
33// We've arrived at these values through a lot of trial and error. Setting them any higher
34// lets false positives creep in quickly.
35pub const MAX_AVG_VARIANCE: f32 = 0.48;
36pub const MAX_INDIVIDUAL_VARIANCE: f32 = 0.7;
37
38/**
39 * Start/end guard pattern.
40 */
41pub const START_END_PATTERN: [u32; 3] = [1, 1, 1];
42
43/**
44 * Pattern marking the middle of a UPC/EAN pattern, separating the two halves.
45 */
46pub const MIDDLE_PATTERN: [u32; 5] = [1, 1, 1, 1, 1];
47/**
48 * end guard pattern.
49 */
50pub const END_PATTERN: [u32; 6] = [1, 1, 1, 1, 1, 1];
51/**
52 * "Odd", or "L" patterns used to encode UPC/EAN digits.
53 */
54pub const L_PATTERNS: [[u32; 4]; 10] = [
55    [3, 2, 1, 1], // 0
56    [2, 2, 2, 1], // 1
57    [2, 1, 2, 2], // 2
58    [1, 4, 1, 1], // 3
59    [1, 1, 3, 2], // 4
60    [1, 2, 3, 1], // 5
61    [1, 1, 1, 4], // 6
62    [1, 3, 1, 2], // 7
63    [1, 2, 1, 3], // 8
64    [3, 1, 1, 2], // 9
65];
66
67/**
68 * As above but also including the "even", or "G" patterns used to encode UPC/EAN digits.
69 */
70pub 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
95/**
96 * <p>Encapsulates functionality and implementation that is common to UPC and EAN families
97 * of one-dimensional barcodes.</p>
98 *
99 * @author dswitkin@google.com (Daniel Switkin)
100 * @author Sean Owen
101 * @author alasdair@google.com (Alasdair Mackintosh)
102 */
103pub 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            // Make sure there is a quiet zone at least as big as the start pattern before the barcode.
123            // If this check would run off the left edge of the image, do not accept this barcode,
124            // as it is very likely to be a false positive.
125            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    /**
135     * <p>Like {@link #decodeRow(int, BitArray, Map)}, but
136     * allows caller to inform method about where the UPC/EAN start pattern is
137     * found. This allows this to be computed once and reused across many implementations.</p>
138     *
139     * @param rowNumber row index into the image
140     * @param row encoding of the row of the barcode image
141     * @param startGuardRange start/end column where the opening start pattern was found
142     * @param hints optional hints that influence decoding
143     * @return {@link RXingResult} encapsulating the result of decoding a barcode in the row
144     * @throws NotFoundException if no potential barcode is found
145     * @throws ChecksumException if a potential barcode is found but does not pass its checksum
146     * @throws FormatException if a potential barcode is found but format is invalid
147     */
148    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        // Make sure there is a quiet zone at least as big as the end pattern after the barcode. The
182        // spec might want more whitespace, but in practice this is the maximum we can count on.
183        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        // UPC/EAN should never be less than 8 chars anyway
192        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(), // no natural byte representation for these barcodes
206            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    /**
269     * @param s string of digits to check
270     * @return {@link #checkStandardUPCEANChecksum(CharSequence)}
271     * @throws FormatException if the string does not contain only digits
272     */
273    fn checkChecksum(&self, s: &str) -> Result<bool> {
274        self.checkStandardUPCEANChecksum(s)
275    }
276
277    /**
278     * Computes the UPC/EAN checksum on a string of digits, and reports
279     * whether the checksum is correct or not.
280     *
281     * @param s string of digits to check
282     * @return true iff string of digits passes the UPC/EAN checksum algorithm
283     * @throws FormatException if the string does not contain only digits
284     */
285    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]; //s.subSequence(0, length - 1);
297        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            // for (int i = length - 1; i >= 0; i -= 2) {
313            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            // for (int i = length - 2; i >= 0; i -= 2) {
329            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    /**
365     * @param row row of black/white values to search
366     * @param rowOffset position to start search
367     * @param whiteFirst if true, indicates that the pattern specifies white/black/white/...
368     * pixel counts, otherwise, it is interpreted as black/white/black/...
369     * @param pattern pattern of counts of number of black and white pixels that are being
370     * searched for as a pattern
371     * @param counters array of counters, as long as pattern, to re-use
372     * @return start/end horizontal offset of guard pattern, as an array of two ints
373     * @throws NotFoundException if pattern is not found
374     */
375    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            // for (int x = rowOffset; x < width; x++) {
395            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    /**
426     * Attempts to decode a single UPC/EAN-encoded digit.
427     *
428     * @param row row of black/white values to decode
429     * @param counters the counts of runs of observed black/white/black/... values
430     * @param rowOffset horizontal offset to start decoding from
431     * @param patterns the set of patterns to use to decode -- sometimes different encodings
432     * for the digits 0-9 are used, and this indicates the encodings for 0 to 9 that should
433     * be used
434     * @return horizontal offset of first pixel beyond the decoded digit
435     * @throws NotFoundException if digit cannot be decoded
436     */
437    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; // worst variance we'll accept
446        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    /**
464     * Get the format of this decoder.
465     *
466     * @return The 1D format.
467     */
468    fn getBarcodeFormat(&self) -> BarcodeFormat;
469
470    /**
471     * Subclasses override this to decode the portion of a barcode between the start
472     * and end guard patterns.
473     *
474     * @param row row of black/white values to search
475     * @param startRange start/end offset of start guard pattern
476     * @param resultString {@link StringBuilder} to append decoded chars to
477     * @return horizontal offset of first pixel after the "middle" that was decoded
478     * @throws NotFoundException if decoding could not complete successfully
479     */
480    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 {};