1use 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
36pub 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#[derive(Debug)]
54pub struct Version {
55 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 for ecb in ecbArray {
73 total += ecb.getCount() * (ecb.getDataCodewords() + ecCodewords);
75 }
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 }
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 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 if targetVersion == versionBits {
172 return Self::getVersionForNumber(i + 7);
173 }
174 let bitsDifference = FormatInformation::numBitsDiffering(versionBits, targetVersion);
177 if bitsDifference < bestDifference {
178 bestVersion = i + 7;
179 bestDifference = bitsDifference;
180 }
181 }
182 if bestDifference <= 3 {
185 return Self::getVersionForNumber(bestVersion);
186 }
187 Err(Exceptions::NOT_FOUND)
189 }
190
191 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 bitMatrix.setRegion(0, 0, size.x as u32, 1)?; bitMatrix.setRegion(0, (size.y - 1) as u32, size.x as u32, 1)?; bitMatrix.setRegion(0, 1, 1, (size.y - 2) as u32)?; bitMatrix.setRegion((size.x - 1) as u32, 1, 1, (size.y - 2) as u32)?; let max = self.alignmentPatternCenters.len(); for x in 0..max {
208 let cx = self.alignmentPatternCenters[x];
210 bitMatrix.setRegion(cx - 1, 1, 3, 2)?; bitMatrix.setRegion(cx - 1, (size.y - 3) as u32, 3, 2)?; bitMatrix.setRegion(cx, 3, 1, (size.y - 6) as u32)?; }
214
215 bitMatrix.setRegion(1, 1, 8 - 1, 8 - 1 - u32::from(size.y == 7))?; bitMatrix.setRegion(8, 1, 3, 5)?;
219 bitMatrix.setRegion(11, 1, 1, 3)?;
220
221 bitMatrix.setRegion((size.x - 5) as u32, (size.y - 5) as u32, 5 - 1, 5 - 1)?;
223 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 bitMatrix.set((size.x - 2) as u32, 1);
229 if size.y > 9 {
230 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 bitMatrix.setRegion(0, 0, 9, 9)?;
242
243 if self.qr_type != Type::Micro {
244 bitMatrix.setRegion(dimension - 8, 0, 8, 9)?;
246 bitMatrix.setRegion(0, dimension - 8, 9, 8)?;
248
249 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 }
259 }
260
261 bitMatrix.setRegion(6, 9, 1, dimension - 17)?;
263 bitMatrix.setRegion(9, 6, dimension - 17, 1)?;
265
266 if self.versionNumber > 6 {
267 bitMatrix.setRegion(dimension - 11, 0, 3, 6)?;
269 bitMatrix.setRegion(0, dimension - 11, 6, 3)?;
271 }
272 } else {
273 bitMatrix.setRegion(9, 0, dimension - 9, 1)?;
275
276 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#[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#[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}