Skip to main content

qrcode_rust/
qr_code.rs

1//! QR Code implementation
2
3use core::fmt::Write;
4
5use qrcode_rust_shared::{
6    qr_8bit_byte::QR8bitByte,
7    qr_bit_buffer::BitBuffer,
8    qr_code_model::{get_type_number, QRErrorCorrectLevel, QRMode, PATTERN_POSITION_TABLE},
9    qr_polynomial::Polynomial,
10    qr_rs_block::get_rs_blocks,
11    qr_util::{get_bch_digit, get_length_in_bits},
12};
13
14/// QRCode 选项
15#[derive(Clone)]
16pub struct QRCodeOptions {
17    pub width: i32,
18    pub height: i32,
19    pub color_dark: String,
20    pub color_light: String,
21    pub correct_level: QRErrorCorrectLevel,
22}
23
24impl Default for QRCodeOptions {
25    fn default() -> Self {
26        QRCodeOptions {
27            width: 256,
28            height: 256,
29            color_dark: String::from("#000000"),
30            color_light: String::from("#ffffff"),
31            correct_level: QRErrorCorrectLevel::H,
32        }
33    }
34}
35
36/// QRCode 结构
37pub struct QRCode {
38    pub options: QRCodeOptions,
39    pub type_number: i32,
40    pub module_count: i32,
41    pub modules: Vec<Vec<Option<bool>>>,
42    pub data_cache: Option<Vec<i32>>,
43    pub data_list: Vec<QR8bitByte>,
44}
45
46impl QRCode {
47    pub fn new() -> Self {
48        QRCode {
49            options: QRCodeOptions::default(),
50            type_number: 0,
51            module_count: 0,
52            modules: Vec::new(),
53            data_cache: None,
54            data_list: Vec::new(),
55        }
56    }
57
58    pub fn with_options(options: QRCodeOptions) -> Self {
59        QRCode {
60            options,
61            type_number: 0,
62            module_count: 0,
63            modules: Vec::new(),
64            data_cache: None,
65            data_list: Vec::new(),
66        }
67    }
68
69    pub fn add_data(&mut self, data: &str) {
70        self.data_list.push(QR8bitByte::new(data));
71        self.data_cache = None;
72    }
73
74    pub fn is_dark(&self, row: i32, col: i32) -> bool {
75        if row < 0 || self.module_count <= row || col < 0 || self.module_count <= col {
76            panic!("isDark: out of range");
77        }
78        self.modules[row as usize][col as usize].unwrap_or(false)
79    }
80
81    pub fn get_module_count(&self) -> i32 {
82        self.module_count
83    }
84
85    pub fn get_modules(&self) -> Option<&Vec<Vec<Option<bool>>>> {
86        if self.modules.is_empty() {
87            None
88        } else {
89            Some(&self.modules)
90        }
91    }
92
93    pub fn make_code(&mut self, text: &str) {
94        self.data_list.clear();
95        self.add_data(text);
96        self.make();
97    }
98
99    fn make(&mut self) {
100        self.make_impl(false);
101    }
102
103    fn make_impl(&mut self, test: bool) {
104        // 计算类型号
105        if self.type_number == 0 {
106            let mut type_num = 1;
107            for data in &self.data_list {
108                type_num = type_num.max(get_type_number(&data.data, self.options.correct_level));
109            }
110            self.type_number = type_num;
111        }
112
113        self.module_count = self.type_number * 4 + 17;
114        self.modules = vec![vec![None; self.module_count as usize]; self.module_count as usize];
115
116        self.setup_position_probe_pattern(0, 0);
117        self.setup_position_probe_pattern(self.module_count - 7, 0);
118        self.setup_position_probe_pattern(0, self.module_count - 7);
119        self.setup_position_adjust_pattern();
120        self.setup_timing_pattern();
121        self.setup_type_info(test);
122
123        if self.type_number >= 7 {
124            self.setup_type_number(test);
125        }
126
127        if self.data_cache.is_none() {
128            self.data_cache = Some(self.create_data());
129        }
130
131        let data = self.data_cache.as_ref().unwrap().clone();
132        self.map_data(&data);
133    }
134
135    fn setup_position_probe_pattern(&mut self, row: i32, col: i32) {
136        for r in -1..=7 {
137            if row + r <= -1 || self.module_count <= row + r {
138                continue;
139            }
140            for c in -1..=7 {
141                if col + c <= -1 || self.module_count <= col + c {
142                    continue;
143                }
144                let is_dark = ((0..=6).contains(&r) && (c == 0 || c == 6))
145                    || ((0..=6).contains(&c) && (r == 0 || r == 6))
146                    || ((2..=4).contains(&r) && (2..=4).contains(&c));
147                self.modules[(row + r) as usize][(col + c) as usize] = Some(is_dark);
148            }
149        }
150    }
151
152    fn setup_position_adjust_pattern(&mut self) {
153        let pos = PATTERN_POSITION_TABLE[(self.type_number - 1) as usize];
154        for i in 0..pos.len() {
155            for j in 0..pos.len() {
156                let row = pos[i];
157                let col = pos[j];
158
159                // 跳过无效位置(0 表示没有位置调整图案)
160                if row == 0 || col == 0 {
161                    continue;
162                }
163
164                // 跳过与位置探测图案重叠的位置
165                // 位置探测图案在:(0,0), (module_count-7, 0), (0, module_count-7)
166                // 每个位置探测图案占 7x7,加上 1 格分隔符是 9x9
167                if row <= 8 && col <= 8 {
168                    continue; // 左上角区域
169                }
170                if row <= 8 && col >= self.module_count - 8 {
171                    continue; // 右上角区域
172                }
173                if row >= self.module_count - 8 && col <= 8 {
174                    continue; // 左下角区域
175                }
176
177                // 跳过已经设置的位置
178                if self.modules[row as usize][col as usize].is_some() {
179                    continue;
180                }
181
182                for r in -2..=2 {
183                    for c in -2..=2 {
184                        let new_row = row + r;
185                        let new_col = col + c;
186                        // 确保不越界
187                        if new_row < 0
188                            || new_row >= self.module_count
189                            || new_col < 0
190                            || new_col >= self.module_count
191                        {
192                            continue;
193                        }
194                        let is_dark = r == -2 || r == 2 || c == -2 || c == 2 || (r == 0 && c == 0);
195                        self.modules[new_row as usize][new_col as usize] = Some(is_dark);
196                    }
197                }
198            }
199        }
200    }
201
202    fn setup_timing_pattern(&mut self) {
203        for r in 8..self.module_count - 8 {
204            if self.modules[r as usize][6].is_none() {
205                self.modules[r as usize][6] = Some(r % 2 == 0);
206            }
207        }
208        for c in 8..self.module_count - 8 {
209            if self.modules[6][c as usize].is_none() {
210                self.modules[6][c as usize] = Some(c % 2 == 0);
211            }
212        }
213    }
214
215    fn setup_type_info(&mut self, test: bool) {
216        let g15 = (1 << 10) | (1 << 8) | (1 << 5) | (1 << 4) | (1 << 2) | (1 << 1) | (1 << 0);
217        let g15_mask = (1 << 14) | (1 << 12) | (1 << 10) | (1 << 4) | (1 << 1);
218
219        let mask_pattern = 0; // kennytm 选择的最优 mask pattern
220        let correct_level = match self.options.correct_level {
221            QRErrorCorrectLevel::L => 1,
222            QRErrorCorrectLevel::M => 0,
223            QRErrorCorrectLevel::Q => 3,
224            QRErrorCorrectLevel::H => 2,
225        };
226
227        let mut data = (correct_level << 3) | mask_pattern;
228        let mut d = data << 10;
229
230        while get_bch_digit(d) - get_bch_digit(g15) >= 0 {
231            d ^= g15 << (get_bch_digit(d) - get_bch_digit(g15));
232        }
233
234        data = ((data << 10) | d) ^ g15_mask;
235
236        // 垂直类型信息(第 8 列)- 从上到下
237        for i in 0..15 {
238            // 在 test 模式下,格式信息位设置为 false(白色)
239            let bit = !test && ((data >> i) & 1) == 1;
240
241            if i < 6 {
242                // 行 0-5
243                self.modules[i as usize][8] = Some(bit);
244            } else if i < 8 {
245                // 行 7-8(跳过第6行,因为它是定时图案)
246                self.modules[(i + 1) as usize][8] = Some(bit);
247            } else {
248                // 行 module_count-7 到 module_count-1
249                self.modules[(self.module_count - 15 + i) as usize][8] = Some(bit);
250            }
251        }
252
253        // 水平类型信息(第 8 行)- 从右到左
254        for i in 0..15 {
255            let bit = !test && ((data >> i) & 1) == 1;
256
257            if i < 8 {
258                // 列 module_count-1 到 module_count-8
259                self.modules[8][(self.module_count - 1 - i) as usize] = Some(bit);
260            } else if i < 9 {
261                // 列 7
262                self.modules[8][(15 - i - 1 + 1) as usize] = Some(bit);
263            } else {
264                // 列 5 到 0
265                self.modules[8][(15 - i - 1) as usize] = Some(bit);
266            }
267        }
268
269        // 固定暗模块 (module_count-8, 8)
270        self.modules[(self.module_count - 8) as usize][8] = Some(!test);
271    }
272
273    fn setup_type_number(&mut self, _test: bool) {
274        // 简化版本
275    }
276
277    fn map_data(&mut self, data: &[i32]) {
278        let mut inc = -1;
279        let mut row = self.module_count - 1;
280        let mut bit_index = 7;
281        let mut byte_index = 0;
282
283        let mut col = self.module_count - 1;
284        while col > 0 {
285            // Skip column 6 (timing pattern column)
286            if col == 6 {
287                col -= 1;
288            }
289
290            loop {
291                for c in 0..2 {
292                    let col_idx = col - c;
293                    if col_idx < 0 || col_idx >= self.module_count {
294                        continue;
295                    }
296                    if self.modules[row as usize][col_idx as usize].is_none() {
297                        let mut dark = false;
298                        if byte_index < data.len() {
299                            dark = ((data[byte_index] >> bit_index) & 1) == 1;
300                        }
301
302                        // Mask pattern 0: (row + col) % 2 == 0
303                        let mask = (row + col_idx) % 2 == 0;
304                        if mask {
305                            dark = !dark;
306                        }
307
308                        self.modules[row as usize][col_idx as usize] = Some(dark);
309
310                        if bit_index == 0 {
311                            bit_index = 7;
312                            byte_index += 1;
313                        } else {
314                            bit_index -= 1;
315                        }
316                    }
317                }
318
319                row += inc;
320
321                if row < 0 || self.module_count <= row {
322                    row -= inc;
323                    inc = -inc;
324                    break;
325                }
326            }
327
328            col -= 2;
329        }
330    }
331
332    fn create_data(&self) -> Vec<i32> {
333        let rs_blocks = get_rs_blocks(self.type_number, self.options.correct_level);
334
335        let mut buffer = BitBuffer::new();
336
337        for data in &self.data_list {
338            buffer.put(QRMode::MODE_8BIT_BYTE, 4);
339            buffer.put(
340                data.get_length() as i32,
341                get_length_in_bits(QRMode::MODE_8BIT_BYTE, self.type_number),
342            );
343            data.write(&mut buffer);
344        }
345
346        let mut total_data_count = 0;
347        for block in &rs_blocks {
348            total_data_count += block.data_count;
349        }
350
351        if buffer.length + 4 <= total_data_count as usize * 8 {
352            buffer.put(0, 4);
353        }
354
355        while !buffer.length.is_multiple_of(8) {
356            buffer.put_bit(false);
357        }
358
359        loop {
360            if buffer.length >= total_data_count as usize * 8 {
361                break;
362            }
363            buffer.put(0xEC, 8);
364            if buffer.length >= total_data_count as usize * 8 {
365                break;
366            }
367            buffer.put(0x11, 8);
368        }
369
370        #[cfg(test)]
371        eprintln!(
372            "DEBUG: buffer.length={}, buffer.buffer.len={}, total_data_count*8={}",
373            buffer.length,
374            buffer.buffer.len(),
375            total_data_count as usize * 8
376        );
377
378        let data = buffer.buffer;
379
380        let mut offset = 0;
381        let max_dc_count = rs_blocks.iter().map(|b| b.data_count).max().unwrap_or(0);
382        let max_ec_count = rs_blocks
383            .iter()
384            .map(|b| b.total_count - b.data_count)
385            .max()
386            .unwrap_or(0);
387
388        let mut dcdata: Vec<Vec<i32>> = Vec::new();
389        let mut ecdata: Vec<Vec<i32>> = Vec::new();
390
391        for block in &rs_blocks {
392            let dc_count = block.data_count;
393            let ec_count = block.total_count - dc_count;
394
395            dcdata.push(data[offset as usize..(offset + dc_count) as usize].to_vec());
396            offset += dc_count;
397
398            // 生成 Reed-Solomon 纠错码
399            // 数据多项式: d(x) = d_0*x^(n-1) + d_1*x^(n-2) + ... + d_{n-1}
400            // 系数按降序排列(JS 匹配)
401            let rs_poly = Polynomial::generate_rs_poly(ec_count);
402
403            // 创建数据多项式并扩展 ec_count 个零
404            // 在降序表示中,在末尾添加零相当于乘以 x^ec_count
405            let dc = dcdata.last().unwrap();
406            let mut raw_coeff = dc.clone();
407            raw_coeff.extend(std::iter::repeat_n(0, ec_count as usize));
408            let raw_poly = Polynomial::new(raw_coeff, 0);
409
410            // 计算纠错码: (数据多项式 * x^{ec_count}) mod 生成多项式
411            let mod_poly = raw_poly.r#mod(&rs_poly);
412
413            // 提取纠错码:取 mod_poly 的最后 ec_count 个系数(最低次项)
414            // 降序排列中,最低次项在数组末尾
415            // 匹配 JS 实现: const modIndex = i + modPoly.length - ecCount;
416            let mut ec: Vec<i32> = Vec::with_capacity(ec_count as usize);
417            for i in 0..ec_count {
418                let mod_index = i + mod_poly.len() as i32 - ec_count;
419                let val = if mod_index >= 0 {
420                    mod_poly.get(mod_index as usize)
421                } else {
422                    0
423                };
424                #[cfg(test)]
425                if i < 5 {
426                    eprintln!("DEBUG: ec[{}] = {} (mod_index={})", i, val, mod_index);
427                }
428                ec.push(val);
429            }
430            ecdata.push(ec);
431        }
432
433        let mut result: Vec<i32> = Vec::new();
434
435        for i in 0..max_dc_count {
436            for (_r, item) in dcdata.iter().enumerate().take(rs_blocks.len()) {
437                if i < item.len() as i32 {
438                    result.push(item[i as usize]);
439                }
440            }
441        }
442
443        for i in 0..max_ec_count {
444            for (_r, item) in ecdata.iter().enumerate().take(rs_blocks.len()) {
445                if i < item.len() as i32 {
446                    result.push(item[i as usize]);
447                }
448            }
449        }
450
451        result
452    }
453}
454
455impl QRCode {
456    /// 生成 SVG 字符串(高性能版本 - 使用 Path 合并 + write! 优化)
457    pub fn get_svg(&self) -> String {
458        let count = self.module_count;
459        if count == 0 {
460            return String::new();
461        }
462
463        let size = 256;
464        let cell_size = size / count;
465        let actual_size = cell_size * count;
466        let offset = (size - actual_size) / 2;
467
468        // 估算容量:约 50% 模块为深色,每个模块约 25 字节
469        let estimated_dark = (count * count) as usize / 2;
470        let mut svg = String::with_capacity(350 + estimated_dark * 25);
471
472        // SVG 头部 - 使用 write! 避免临时字符串
473        write!(
474            svg,
475            r#"<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 {0} {0}" width="{0}" height="{0}"><path d="M0 0h{0}v{0}H0z" fill="{1}"/><path fill="{2}" d=""#,
476            size, self.options.color_light, self.options.color_dark
477        ).unwrap();
478
479        // 使用单个 Path 绘制所有深色模块
480        // 展平二维访问为一维,减少边界检查开销
481        let modules_flat: Vec<bool> = self
482            .modules
483            .iter()
484            .flat_map(|row| row.iter().map(|&m| m.unwrap_or(false)))
485            .collect();
486
487        let count_i32 = count;
488        for (idx, is_dark) in modules_flat.iter().enumerate() {
489            if *is_dark {
490                let row = (idx as i32) / count_i32;
491                let col = (idx as i32) % count_i32;
492                let x = col * cell_size + offset;
493                let y = row * cell_size + offset;
494                write!(svg, "M{x} {y}h{cell_size}v{cell_size}h-{cell_size}z").unwrap();
495            }
496        }
497
498        svg.push_str(r#""/></svg>"#);
499        svg
500    }
501}
502
503impl Default for QRCode {
504    fn default() -> Self {
505        Self::new()
506    }
507}