Skip to main content

rxing/qrcode/decoder/
version.rs

1/*
2 * Copyright 2007 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 std::fmt;
18
19use crate::{
20    Exceptions,
21    common::{BitMatrix, Result},
22    qrcode::cpp_port::Type,
23};
24
25use super::{ErrorCorrectionLevel, FormatInformation};
26
27use once_cell::sync::Lazy;
28
29pub type VersionRef = &'static Version;
30
31pub static VERSIONS: Lazy<Box<[Version]>> = Lazy::new(Version::buildVersions);
32pub static MICRO_VERSIONS: Lazy<Box<[Version]>> = Lazy::new(Version::build_micro_versions);
33pub static MODEL1_VERSIONS: Lazy<Box<[Version]>> = Lazy::new(Version::build_model1_versions);
34pub static RMQR_VERSIONS: Lazy<Box<[Version]>> = Lazy::new(Version::build_rmqr_versions);
35
36/**
37 * See ISO 18004:2006 Annex D.
38 * Element i represents the raw version bits that specify version i + 7
39 */
40pub const VERSION_DECODE_INFO: [u32; 34] = [
41    0x07C94, 0x085BC, 0x09A99, 0x0A4D3, 0x0BBF6, 0x0C762, 0x0D847, 0x0E60D, 0x0F928, 0x10B78,
42    0x1145D, 0x12A17, 0x13532, 0x149A6, 0x15683, 0x168C9, 0x177EC, 0x18EC4, 0x191E1, 0x1AFAB,
43    0x1B08E, 0x1CC1A, 0x1D33F, 0x1ED75, 0x1F250, 0x209D5, 0x216F0, 0x228BA, 0x2379F, 0x24B0B,
44    0x2542E, 0x26A64, 0x27541, 0x28C69,
45];
46
47// const VERSIONS: &'static[Version] = &Version::buildVersions();
48/**
49 * See ISO 18004:2006 Annex D
50 *
51 * @author Sean Owen
52 */
53#[derive(Debug)]
54pub struct Version {
55    //   private static final Version[] VERSIONS = buildVersions();
56    versionNumber: u32,
57    alignmentPatternCenters: Box<[u32]>,
58    ecBlocks: Box<[ECBlocks]>,
59    totalCodewords: u32,
60    pub(crate) qr_type: Type,
61}
62impl Version {
63    pub(super) fn new(
64        versionNumber: u32,
65        alignmentPatternCenters: Box<[u32]>,
66        ecBlocks: [ECBlocks; 4],
67    ) -> Self {
68        let mut total = 0;
69        let ecCodewords = ecBlocks[1].getECCodewordsPerBlock();
70        let ecbArray = ecBlocks[1].getECBlocks();
71        // let mut i = 0;
72        for ecb in ecbArray {
73            // while i < ecbArray.len() {
74            total += ecb.getCount() * (ecb.getDataCodewords() + ecCodewords);
75            // i += 1;
76        }
77
78        Self {
79            versionNumber,
80            alignmentPatternCenters,
81            qr_type: if ecBlocks[0].getECCodewordsPerBlock() != 0 {
82                Type::Model2
83            } else {
84                Type::RectMicro
85            },
86            ecBlocks: Box::new(ecBlocks),
87            totalCodewords: total,
88        }
89    }
90
91    pub(super) fn without_alignment_patterns(
92        versionNumber: u32,
93        ecBlocks: Box<[ECBlocks]>,
94    ) -> Self {
95        let mut total = 0;
96        let ecCodewords = ecBlocks[0].getECCodewordsPerBlock();
97        let ecbArray = ecBlocks[0].getECBlocks();
98        for ecb_array_element in ecbArray {
99            total +=
100                ecb_array_element.getCount() * (ecb_array_element.getDataCodewords() + ecCodewords);
101        }
102
103        let symbol_type = if ecBlocks[0].getECCodewordsPerBlock() < 7
104            || ecBlocks[0].getECCodewordsPerBlock() == 8
105        {
106            Type::Micro
107        } else {
108            Type::Model1
109        };
110
111        Self {
112            versionNumber,
113            alignmentPatternCenters: Box::default(),
114            ecBlocks,
115            totalCodewords: total,
116            qr_type: symbol_type,
117        }
118    }
119
120    pub const fn getVersionNumber(&self) -> u32 {
121        self.versionNumber
122    }
123
124    pub const fn getAlignmentPatternCenters(&self) -> &[u32] {
125        &self.alignmentPatternCenters
126    }
127
128    pub const fn getTotalCodewords(&self) -> u32 {
129        self.totalCodewords
130    }
131
132    pub fn getDimensionForVersion(&self) -> u32 {
133        Self::DimensionOfVersion(self.versionNumber, self.qr_type == Type::Micro)
134        // 17 + 4 * self.versionNumber
135    }
136
137    pub const fn getECBlocksForLevel(&self, ecLevel: ErrorCorrectionLevel) -> &ECBlocks {
138        if ecLevel.get_ordinal() as usize >= self.ecBlocks.len() {
139            return &self.ecBlocks[ecLevel.get_ordinal() as usize % self.ecBlocks.len()];
140        }
141        &self.ecBlocks[ecLevel.get_ordinal() as usize]
142    }
143
144    /**
145     * <p>Deduces version information purely from QR Code dimensions.</p>
146     *
147     * @param dimension dimension in modules
148     * @return Version for a QR Code of that dimension
149     * @throws FormatException if dimension is not 1 mod 4
150     */
151    pub fn getProvisionalVersionForDimension(dimension: u32) -> Result<VersionRef> {
152        if dimension % 4 != 1 {
153            return Err(Exceptions::format_with("dimension incorrect"));
154        }
155        Self::getVersionForNumber((dimension - 17) / 4)
156    }
157
158    pub fn getVersionForNumber(versionNumber: u32) -> Result<VersionRef> {
159        if !(1..=40).contains(&versionNumber) {
160            return Err(Exceptions::illegal_argument_with("version out of spec"));
161        }
162        Ok(&VERSIONS[versionNumber as usize - 1])
163    }
164
165    pub fn decodeVersionInformation(versionBits: u32) -> Result<VersionRef> {
166        let mut bestDifference = u32::MAX;
167        let mut bestVersion = 0;
168        for i in 0..VERSION_DECODE_INFO.len() as u32 {
169            let targetVersion = VERSION_DECODE_INFO[i as usize];
170            // Do the version info bits match exactly? done.
171            if targetVersion == versionBits {
172                return Self::getVersionForNumber(i + 7);
173            }
174            // Otherwise see if this is the closest to a real version info bit string
175            // we have seen so far
176            let bitsDifference = FormatInformation::numBitsDiffering(versionBits, targetVersion);
177            if bitsDifference < bestDifference {
178                bestVersion = i + 7;
179                bestDifference = bitsDifference;
180            }
181        }
182        // We can tolerate up to 3 bits of error since no two version info codewords will
183        // differ in less than 8 bits.
184        if bestDifference <= 3 {
185            return Self::getVersionForNumber(bestVersion);
186        }
187        // If we didn't find a close enough match, fail
188        Err(Exceptions::NOT_FOUND)
189    }
190
191    /**
192     * See ISO 18004:2006 Annex E
193     */
194    pub fn buildFunctionPattern(&self) -> Result<BitMatrix> {
195        if self.isRMQR() {
196            let size = Version::SymbolSize(self.versionNumber, Type::RectMicro);
197            let mut bitMatrix = BitMatrix::new(size.x as u32, size.y as u32)?;
198
199            // Set edge timing patterns
200            bitMatrix.setRegion(0, 0, size.x as u32, 1)?; // Top
201            bitMatrix.setRegion(0, (size.y - 1) as u32, size.x as u32, 1)?; // Bottom
202            bitMatrix.setRegion(0, 1, 1, (size.y - 2) as u32)?; // Left
203            bitMatrix.setRegion((size.x - 1) as u32, 1, 1, (size.y - 2) as u32)?; // Right
204
205            // Set vertical timing and alignment patterns
206            let max = self.alignmentPatternCenters.len(); // Same as vertical timing column
207            for x in 0..max {
208                // for (size_t x = 0; x < max; ++x) {
209                let cx = self.alignmentPatternCenters[x];
210                bitMatrix.setRegion(cx - 1, 1, 3, 2)?; // Top alignment pattern
211                bitMatrix.setRegion(cx - 1, (size.y - 3) as u32, 3, 2)?; // Bottom alignment pattern
212                bitMatrix.setRegion(cx, 3, 1, (size.y - 6) as u32)?; // Vertical timing pattern
213            }
214
215            // Top left finder pattern + separator
216            bitMatrix.setRegion(1, 1, 8 - 1, 8 - 1 - u32::from(size.y == 7))?; // R7 finder bottom flush with edge
217            // Top left format
218            bitMatrix.setRegion(8, 1, 3, 5)?;
219            bitMatrix.setRegion(11, 1, 1, 3)?;
220
221            // Bottom right finder subpattern
222            bitMatrix.setRegion((size.x - 5) as u32, (size.y - 5) as u32, 5 - 1, 5 - 1)?;
223            // Bottom right format
224            bitMatrix.setRegion((size.x - 8) as u32, (size.y - 6) as u32, 3, 5)?;
225            bitMatrix.setRegion((size.x - 5) as u32, (size.y - 6) as u32, 3, 1)?;
226
227            // Top right corner finder
228            bitMatrix.set((size.x - 2) as u32, 1);
229            if size.y > 9 {
230                // Bottom left corner finder
231                bitMatrix.set(1, (size.y - 2) as u32);
232            }
233
234            return Ok(bitMatrix);
235        }
236
237        let dimension = self.getDimensionForVersion();
238        let mut bitMatrix = BitMatrix::with_single_dimension(dimension)?;
239
240        // Top left finder pattern + separator + format
241        bitMatrix.setRegion(0, 0, 9, 9)?;
242
243        if self.qr_type != Type::Micro {
244            // Top right finder pattern + separator + format
245            bitMatrix.setRegion(dimension - 8, 0, 8, 9)?;
246            // Bottom left finder pattern + separator + format
247            bitMatrix.setRegion(0, dimension - 8, 9, 8)?;
248
249            // Alignment patterns
250            let max = self.alignmentPatternCenters.len();
251            for x in 0..max {
252                let i = self.alignmentPatternCenters[x] - 2;
253                for y in 0..max {
254                    if (x != 0 || (y != 0 && y != max - 1)) && (x != max - 1 || y != 0) {
255                        bitMatrix.setRegion(self.alignmentPatternCenters[y] - 2, i, 5, 5)?;
256                    }
257                    // else no o alignment patterns near the three finder patterns
258                }
259            }
260
261            // Vertical timing pattern
262            bitMatrix.setRegion(6, 9, 1, dimension - 17)?;
263            // Horizontal timing pattern
264            bitMatrix.setRegion(9, 6, dimension - 17, 1)?;
265
266            if self.versionNumber > 6 {
267                // Version info, top right
268                bitMatrix.setRegion(dimension - 11, 0, 3, 6)?;
269                // Version info, bottom left
270                bitMatrix.setRegion(0, dimension - 11, 6, 3)?;
271            }
272        } else {
273            // Vertical timing pattern
274            bitMatrix.setRegion(9, 0, dimension - 9, 1)?;
275
276            // Horizontal timing pattern
277            bitMatrix.setRegion(0, 9, 1, dimension - 9)?;
278        }
279
280        Ok(bitMatrix)
281    }
282}
283
284impl fmt::Display for Version {
285    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
286        write!(f, "{}", self.versionNumber)
287    }
288}
289
290/**
291 * <p>Encapsulates a set of error-correction blocks in one symbol version. Most versions will
292 * use blocks of differing sizes within one version, so, this encapsulates the parameters for
293 * each set of blocks. It also holds the number of error-correction codewords per block since it
294 * will be the same across all blocks within one version.</p>
295 */
296#[derive(Debug, Clone)]
297pub struct ECBlocks {
298    ecCodewordsPerBlock: u32,
299    ecBlocks: Box<[ECB]>,
300}
301
302impl ECBlocks {
303    pub const fn new(ecCodewordsPerBlock: u32, ecBlocks: Box<[ECB]>) -> Self {
304        Self {
305            ecCodewordsPerBlock,
306            ecBlocks,
307        }
308    }
309
310    pub const fn getECCodewordsPerBlock(&self) -> u32 {
311        self.ecCodewordsPerBlock
312    }
313
314    pub fn getNumBlocks(&self) -> u32 {
315        let mut total = 0;
316        for ecBlock in self.ecBlocks.iter() {
317            total += ecBlock.getCount();
318        }
319        total
320    }
321
322    pub fn getTotalECCodewords(&self) -> u32 {
323        self.ecCodewordsPerBlock * self.getNumBlocks()
324    }
325
326    pub fn getECBlocks(&self) -> &[ECB] {
327        &self.ecBlocks
328    }
329}
330
331/**
332 * <p>Encapsulates the parameters for one error-correction block in one symbol version.
333 * This includes the number of data codewords, and the number of times a block with these
334 * parameters is used consecutively in the QR code version's format.</p>
335 */
336#[derive(Debug, Clone, Copy)]
337pub struct ECB {
338    count: u32,
339    dataCodewords: u32,
340}
341
342impl ECB {
343    pub const fn new(count: u32, dataCodewords: u32) -> Self {
344        Self {
345            count,
346            dataCodewords,
347        }
348    }
349
350    pub const fn getCount(&self) -> u32 {
351        self.count
352    }
353
354    pub const fn getDataCodewords(&self) -> u32 {
355        self.dataCodewords
356    }
357}