Skip to main content

sz_rust_core/
qr_code.rs

1//! 二维码生成模块 — 对齐 PHP `endroid/qr-code`
2//!
3//! 提供二维码生成功能,支持 PNG/SVG 输出和原始矩阵获取。
4//!
5//! ## PHP 对齐
6//!
7//! ### 核心 API 映射
8//!
9//! | PHP 类 | Rust 结构 | 说明 |
10//! |---------|-----------|------|
11//! | `Endroid\QrCode\QrCode` | [`QrCodeGenerator`] | 二维码生成器 |
12//! | `Endroid\QrCode\QrCode::setSize()` | [`QrCodeConfig::with_size`] | 设置尺寸 |
13//! | `Endroid\QrCode\QrCode::setMargin()` | [`QrCodeConfig::with_margin`] | 设置边距 |
14//! | `Endroid\QrCode\QrCode::setForegroundColor()` | [`QrCodeConfig::with_foreground_color`] | 前景色 |
15//! | `Endroid\QrCode\QrCode::setBackgroundColor()` | [`QrCodeConfig::with_background_color`] | 背景色 |
16//! | `Endroid\QrCode\QrCode::setErrorCorrectionLevel()` | [`QrCodeConfig::with_error_correction_level`] | 容错级别 |
17//! | `Endroid\QrCode\ErrorCorrectionLevel` | [`ErrorCorrectionLevel`] | 容错级别枚举 |
18//! | `Endroid\QrCode\QrCode::writeString()` (PNG) | [`QrCodeGenerator::generate_png`] | 生成 PNG |
19//! | `Endroid\QrCode\QrCode::writeString()` (SVG) | [`QrCodeGenerator::generate_svg`] | 生成 SVG |
20//!
21//! ### PHP 行为对齐
22//!
23//! - **容错级别**:PHP 支持 Low(7%)/Medium(15%)/Quartile(25%)/High(30%),Rust 通过 [`ErrorCorrectionLevel`] 表达。
24//! - **尺寸与边距**:PHP `setSize(size)` + `setMargin(margin)`,Rust 通过 [`QrCodeConfig`] 的 `size`/`margin` 字段表达。
25//! - **前景/背景色**:PHP 使用 RGBA 数组,Rust 简化为 RGB `[u8; 3]`。
26//! - **PNG 输出**:先从 `qrcode` crate 获取矩阵,再用 `image` crate 渲染为 PNG(像素级边距控制)。
27//! - **SVG 输出**:直接使用 `qrcode` crate 的 SVG 渲染器(`qrcode::render::svg::Color`)。
28//!
29//! ## Rust 用法
30//!
31//! ```rust,ignore
32//! use sz_rust_core::qr_code::{QrCodeGenerator, QrCodeConfig, ErrorCorrectionLevel};
33//!
34//! // 默认配置
35//! let generator = QrCodeGenerator::new();
36//! let png_bytes = generator.generate_png("https://example.com").unwrap();
37//! let svg_string = generator.generate_svg("https://example.com").unwrap();
38//!
39//! // 自定义配置
40//! let config = QrCodeConfig::new()
41//!     .with_size(300)
42//!     .with_margin(20)
43//!     .with_foreground_color([0, 0, 255])
44//!     .with_error_correction_level(ErrorCorrectionLevel::High);
45//! let generator = QrCodeGenerator::with_config(config);
46//! let png_bytes = generator.generate_png("Hello").unwrap();
47//! ```
48
49use image::{DynamicImage, ImageBuffer, Rgba, RgbaImage};
50use qrcode::render::svg;
51use qrcode::types::Color as QrColor;
52use qrcode::{EcLevel, QrCode};
53use thiserror::Error;
54
55// ============================================================================
56// 错误类型
57// ============================================================================
58
59/// 二维码生成错误 — 对齐 PHP `endroid\qr-code` 异常
60#[derive(Debug, Error)]
61pub enum QrCodeError {
62    /// 二维码生成失败(数据过长、版本不兼容等)
63    #[error("二维码生成失败: {0}")]
64    Generation(String),
65
66    /// 数据编码失败(空数据、无效字符等)
67    #[error("数据编码失败: {0}")]
68    Encoding(String),
69
70    /// IO 错误(PNG 编码写入失败等)
71    #[error("IO 错误: {0}")]
72    Io(String),
73}
74
75impl From<std::io::Error> for QrCodeError {
76    fn from(err: std::io::Error) -> Self {
77        QrCodeError::Io(err.to_string())
78    }
79}
80
81impl From<image::ImageError> for QrCodeError {
82    fn from(err: image::ImageError) -> Self {
83        QrCodeError::Io(err.to_string())
84    }
85}
86
87// ============================================================================
88// 容错级别枚举 — 对齐 PHP endroid\qr-code\ErrorCorrectionLevel
89// ============================================================================
90
91/// 二维码容错级别 — 对齐 PHP `Endroid\QrCode\ErrorCorrectionLevel`
92///
93/// 容错级别越高,二维码能容忍的损坏面积越大,但数据密度越低。
94#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
95pub enum ErrorCorrectionLevel {
96    /// 低容错 — 允许 7% 损坏
97    Low,
98    /// 中等容错(默认)— 允许 15% 损坏
99    #[default]
100    Medium,
101    /// 四分位容错 — 允许 25% 损坏
102    Quartile,
103    /// 高容错 — 允许 30% 损坏
104    High,
105}
106
107impl ErrorCorrectionLevel {
108    /// 转换为 `qrcode` crate 的 `EcLevel`
109    fn to_ec_level(self) -> EcLevel {
110        match self {
111            Self::Low => EcLevel::L,
112            Self::Medium => EcLevel::M,
113            Self::Quartile => EcLevel::Q,
114            Self::High => EcLevel::H,
115        }
116    }
117
118    /// 转换为字符串标识(对齐 PHP `ErrorCorrectionLevel` 类名)
119    pub fn as_str(self) -> &'static str {
120        match self {
121            Self::Low => "low",
122            Self::Medium => "medium",
123            Self::Quartile => "quartile",
124            Self::High => "high",
125        }
126    }
127}
128
129impl std::fmt::Display for ErrorCorrectionLevel {
130    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
131        f.write_str(self.as_str())
132    }
133}
134
135// ============================================================================
136// 二维码配置 — 对齐 PHP endroid\qr-code\QrCode
137// ============================================================================
138
139/// 二维码配置 — 对齐 PHP `Endroid\QrCode\QrCode`
140///
141/// 使用 Builder 模式构建配置,通过 [`QrCodeGenerator::with_config`] 创建生成器。
142#[derive(Debug, Clone)]
143pub struct QrCodeConfig {
144    /// 尺寸(像素,默认 200)
145    pub size: u32,
146    /// 边距(像素,默认 10)
147    pub margin: u32,
148    /// 前景色(RGB,默认黑色 `[0, 0, 0]`)
149    pub foreground_color: [u8; 3],
150    /// 背景色(RGB,默认白色 `[255, 255, 255]`)
151    pub background_color: [u8; 3],
152    /// 容错级别(默认 `Medium`)
153    pub error_correction_level: ErrorCorrectionLevel,
154}
155
156impl Default for QrCodeConfig {
157    fn default() -> Self {
158        Self {
159            size: 200,
160            margin: 10,
161            foreground_color: [0, 0, 0],
162            background_color: [255, 255, 255],
163            error_correction_level: ErrorCorrectionLevel::Medium,
164        }
165    }
166}
167
168impl QrCodeConfig {
169    /// 创建默认配置
170    ///
171    /// - 尺寸:200px
172    /// - 边距:10px
173    /// - 前景色:黑色 `[0, 0, 0]`
174    /// - 背景色:白色 `[255, 255, 255]`
175    /// - 容错级别:`Medium`
176    pub fn new() -> Self {
177        Self::default()
178    }
179
180    /// 设置尺寸(像素)
181    pub fn with_size(mut self, size: u32) -> Self {
182        self.size = size;
183        self
184    }
185
186    /// 设置边距(像素)
187    pub fn with_margin(mut self, margin: u32) -> Self {
188        self.margin = margin;
189        self
190    }
191
192    /// 设置前景色(RGB)
193    pub fn with_foreground_color(mut self, color: [u8; 3]) -> Self {
194        self.foreground_color = color;
195        self
196    }
197
198    /// 设置背景色(RGB)
199    pub fn with_background_color(mut self, color: [u8; 3]) -> Self {
200        self.background_color = color;
201        self
202    }
203
204    /// 设置容错级别
205    pub fn with_error_correction_level(mut self, level: ErrorCorrectionLevel) -> Self {
206        self.error_correction_level = level;
207        self
208    }
209
210    /// 将 RGB 颜色转换为 CSS hex 字符串(用于 SVG 渲染)
211    fn color_to_hex(color: [u8; 3]) -> String {
212        format!("#{:02x}{:02x}{:02x}", color[0], color[1], color[2])
213    }
214}
215
216// ============================================================================
217// 二维码生成器 — 对齐 PHP endroid\qr-code\QrCode
218// ============================================================================
219
220/// 二维码生成器 — 对齐 PHP `Endroid\QrCode\QrCode`
221///
222/// 持有 [`QrCodeConfig`] 配置,提供 PNG/SVG/矩阵三种输出方式。
223///
224/// # PHP 对齐
225///
226/// ```php
227/// // PHP endroid/qr-code
228/// $qr = new QrCode('Hello');
229/// $qr->setSize(200);
230/// $qr->setMargin(10);
231/// $qr->setErrorCorrectionLevel(new ErrorCorrectionLevel(ErrorCorrectionLevel::MEDIUM));
232/// $png = $qr->writeString(); // 默认 PNG
233/// ```
234///
235/// # Rust 用法
236///
237/// ```rust,ignore
238/// use sz_rust_core::qr_code::{QrCodeGenerator, QrCodeConfig, ErrorCorrectionLevel};
239///
240/// let config = QrCodeConfig::new()
241///     .with_size(200)
242///     .with_margin(10)
243///     .with_error_correction_level(ErrorCorrectionLevel::Medium);
244/// let generator = QrCodeGenerator::with_config(config);
245/// let png = generator.generate_png("Hello").unwrap();
246/// ```
247#[derive(Debug, Clone)]
248pub struct QrCodeGenerator {
249    /// 二维码配置
250    config: QrCodeConfig,
251}
252
253impl QrCodeGenerator {
254    /// 创建默认配置的生成器
255    pub fn new() -> Self {
256        Self {
257            config: QrCodeConfig::default(),
258        }
259    }
260
261    /// 使用指定配置创建生成器
262    pub fn with_config(config: QrCodeConfig) -> Self {
263        Self { config }
264    }
265
266    /// 获取配置引用
267    pub fn config(&self) -> &QrCodeConfig {
268        &self.config
269    }
270
271    /// 生成原始矩阵 — 返回 `bool` 二维数组(`true` = 深色模块)
272    ///
273    /// 矩阵尺寸为 `N × N`(正方形),不包含边距。
274    ///
275    /// # 参数
276    ///
277    /// - `data`: 待编码的数据(不能为空)
278    ///
279    /// # 返回
280    ///
281    /// 成功返回 `Vec<Vec<bool>>`,失败返回 [`QrCodeError`]。
282    pub fn generate_matrix(&self, data: &str) -> Result<Vec<Vec<bool>>, QrCodeError> {
283        if data.is_empty() {
284            return Err(QrCodeError::Encoding("数据不能为空".to_string()));
285        }
286
287        let code = QrCode::with_error_correction_level(
288            data.as_bytes(),
289            self.config.error_correction_level.to_ec_level(),
290        )
291        .map_err(|e| QrCodeError::Generation(format!("二维码编码失败: {e}")))?;
292
293        let width = code.width();
294        let matrix = (0..width)
295            .map(|y| (0..width).map(|x| code[(x, y)] == QrColor::Dark).collect())
296            .collect();
297
298        Ok(matrix)
299    }
300
301    /// 生成 PNG 二进制
302    ///
303    /// 先生成原始矩阵,再用 `image` crate 渲染为 PNG。
304    /// 像素级边距控制:`size` 为总尺寸(含边距),`margin` 为四周白边宽度。
305    ///
306    /// # 参数
307    ///
308    /// - `data`: 待编码的数据(不能为空)
309    ///
310    /// # 返回
311    ///
312    /// 成功返回 PNG 字节流 `Vec<u8>`,失败返回 [`QrCodeError`]。
313    pub fn generate_png(&self, data: &str) -> Result<Vec<u8>, QrCodeError> {
314        let matrix = self.generate_matrix(data)?;
315        let png_bytes = self.render_matrix_to_png(&matrix)?;
316        Ok(png_bytes)
317    }
318
319    /// 生成 SVG 字符串
320    ///
321    /// 使用 `qrcode` crate 内置的 SVG 渲染器(`qrcode::render::svg::Color`)。
322    /// 边距通过 quiet zone 控制(`margin > 0` 时启用)。
323    ///
324    /// # 参数
325    ///
326    /// - `data`: 待编码的数据(不能为空)
327    ///
328    /// # 返回
329    ///
330    /// 成功返回 SVG 字符串,失败返回 [`QrCodeError`]。
331    pub fn generate_svg(&self, data: &str) -> Result<String, QrCodeError> {
332        if data.is_empty() {
333            return Err(QrCodeError::Encoding("数据不能为空".to_string()));
334        }
335
336        let code = QrCode::with_error_correction_level(
337            data.as_bytes(),
338            self.config.error_correction_level.to_ec_level(),
339        )
340        .map_err(|e| QrCodeError::Generation(format!("二维码编码失败: {e}")))?;
341
342        let fg_hex = QrCodeConfig::color_to_hex(self.config.foreground_color);
343        let bg_hex = QrCodeConfig::color_to_hex(self.config.background_color);
344
345        let svg_string = code
346            .render::<svg::Color>()
347            .dark_color(svg::Color(&fg_hex))
348            .light_color(svg::Color(&bg_hex))
349            .quiet_zone(self.config.margin > 0)
350            .min_dimensions(self.config.size, self.config.size)
351            .build();
352
353        Ok(svg_string)
354    }
355
356    /// 将矩阵渲染为 PNG 字节流
357    ///
358    /// `size` = 总尺寸(含边距),`margin` = 四周白边宽度。
359    /// QR 码区域 = `size - 2 * margin`,按整数分块映射每个模块。
360    fn render_matrix_to_png(&self, matrix: &[Vec<bool>]) -> Result<Vec<u8>, QrCodeError> {
361        let matrix_width = matrix.len();
362        if matrix_width == 0 {
363            return Err(QrCodeError::Generation("矩阵为空".to_string()));
364        }
365
366        let total_size = self.config.size;
367        let margin = self.config.margin;
368
369        // 计算二维码区域尺寸(总尺寸减去两侧边距)
370        let qr_area = total_size
371            .checked_sub(margin.saturating_mul(2))
372            .filter(|&v| v > 0)
373            .ok_or_else(|| {
374                QrCodeError::Generation(format!(
375                    "尺寸不足以容纳边距: size={total_size}, margin={margin}"
376                ))
377            })?;
378
379        // 每个模块的像素大小(整数除法,至少 1px)
380        let module_size = (qr_area / matrix_width as u32).max(1);
381
382        let [fr, fg, fb] = self.config.foreground_color;
383        let [br, bg, bb] = self.config.background_color;
384        let fg_pixel = Rgba([fr, fg, fb, 255]);
385        let bg_pixel = Rgba([br, bg, bb, 255]);
386
387        // 创建背景色填充的画布
388        let mut img: RgbaImage = ImageBuffer::from_pixel(total_size, total_size, bg_pixel);
389
390        // 绘制深色模块
391        for (y, row) in matrix.iter().enumerate() {
392            for (x, &is_dark) in row.iter().enumerate() {
393                if is_dark {
394                    let start_x = margin + (x as u32) * module_size;
395                    let start_y = margin + (y as u32) * module_size;
396                    for dy in 0..module_size {
397                        for dx in 0..module_size {
398                            let px = start_x + dx;
399                            let py = start_y + dy;
400                            if px < total_size && py < total_size {
401                                img.put_pixel(px, py, fg_pixel);
402                            }
403                        }
404                    }
405                }
406            }
407        }
408
409        // 编码为 PNG
410        let dynamic = DynamicImage::ImageRgba8(img);
411        let mut bytes = Vec::new();
412        let mut cursor = std::io::Cursor::new(&mut bytes);
413        dynamic.write_to(&mut cursor, image::ImageFormat::Png)?;
414        Ok(bytes)
415    }
416}
417
418impl Default for QrCodeGenerator {
419    fn default() -> Self {
420        Self::new()
421    }
422}
423
424// ============================================================================
425// 单元测试
426// ============================================================================
427
428#[cfg(test)]
429mod tests {
430    use super::*;
431
432    // ------------------------------------------------------------------------
433    // QrCodeConfig 测试
434    // ------------------------------------------------------------------------
435
436    /// 测试 QrCodeConfig 默认值
437    #[test]
438    fn test_qr_code_config_default() {
439        let config = QrCodeConfig::default();
440        assert_eq!(config.size, 200);
441        assert_eq!(config.margin, 10);
442        assert_eq!(config.foreground_color, [0, 0, 0]);
443        assert_eq!(config.background_color, [255, 255, 255]);
444        assert_eq!(config.error_correction_level, ErrorCorrectionLevel::Medium);
445    }
446
447    /// 测试 QrCodeConfig builder 链式调用
448    #[test]
449    fn test_qr_code_config_builder() {
450        let config = QrCodeConfig::new()
451            .with_size(300)
452            .with_margin(20)
453            .with_foreground_color([255, 0, 0])
454            .with_background_color([0, 0, 255])
455            .with_error_correction_level(ErrorCorrectionLevel::High);
456
457        assert_eq!(config.size, 300);
458        assert_eq!(config.margin, 20);
459        assert_eq!(config.foreground_color, [255, 0, 0]);
460        assert_eq!(config.background_color, [0, 0, 255]);
461        assert_eq!(config.error_correction_level, ErrorCorrectionLevel::High);
462    }
463
464    // ------------------------------------------------------------------------
465    // ErrorCorrectionLevel 测试
466    // ------------------------------------------------------------------------
467
468    /// 测试 ErrorCorrectionLevel 默认值和转换
469    #[test]
470    fn test_error_correction_level() {
471        // 默认值
472        assert_eq!(
473            ErrorCorrectionLevel::default(),
474            ErrorCorrectionLevel::Medium
475        );
476
477        // as_str
478        assert_eq!(ErrorCorrectionLevel::Low.as_str(), "low");
479        assert_eq!(ErrorCorrectionLevel::Medium.as_str(), "medium");
480        assert_eq!(ErrorCorrectionLevel::Quartile.as_str(), "quartile");
481        assert_eq!(ErrorCorrectionLevel::High.as_str(), "high");
482
483        // Display
484        assert_eq!(format!("{}", ErrorCorrectionLevel::Low), "low");
485        assert_eq!(format!("{}", ErrorCorrectionLevel::High), "high");
486
487        // to_ec_level 映射
488        assert_eq!(ErrorCorrectionLevel::Low.to_ec_level(), EcLevel::L);
489        assert_eq!(ErrorCorrectionLevel::Medium.to_ec_level(), EcLevel::M);
490        assert_eq!(ErrorCorrectionLevel::Quartile.to_ec_level(), EcLevel::Q);
491        assert_eq!(ErrorCorrectionLevel::High.to_ec_level(), EcLevel::H);
492    }
493
494    // ------------------------------------------------------------------------
495    // QrCodeGenerator 测试
496    // ------------------------------------------------------------------------
497
498    /// 测试 QrCodeGenerator 默认配置
499    #[test]
500    fn test_qr_code_generator_default() {
501        let generator = QrCodeGenerator::new();
502        assert_eq!(generator.config().size, 200);
503        assert_eq!(generator.config().margin, 10);
504        assert_eq!(
505            generator.config().error_correction_level,
506            ErrorCorrectionLevel::Medium
507        );
508    }
509
510    /// 测试 QrCodeGenerator 自定义配置
511    #[test]
512    fn test_qr_code_generator_with_config() {
513        let config = QrCodeConfig::new()
514            .with_size(400)
515            .with_margin(15)
516            .with_error_correction_level(ErrorCorrectionLevel::Quartile);
517        let generator = QrCodeGenerator::with_config(config);
518        assert_eq!(generator.config().size, 400);
519        assert_eq!(generator.config().margin, 15);
520        assert_eq!(
521            generator.config().error_correction_level,
522            ErrorCorrectionLevel::Quartile
523        );
524    }
525
526    // ------------------------------------------------------------------------
527    // generate_matrix 测试
528    // ------------------------------------------------------------------------
529
530    /// 测试矩阵生成基本功能(非空且正方形)
531    #[test]
532    fn test_generate_matrix_basic() {
533        let generator = QrCodeGenerator::new();
534        let matrix = generator.generate_matrix("Hello, World!").unwrap();
535
536        assert!(!matrix.is_empty(), "矩阵不能为空");
537        let width = matrix.len();
538        for row in &matrix {
539            assert_eq!(row.len(), width, "矩阵必须是正方形");
540        }
541    }
542
543    /// 测试空数据返回错误
544    #[test]
545    fn test_generate_matrix_empty_data() {
546        let generator = QrCodeGenerator::new();
547        let result = generator.generate_matrix("");
548        assert!(result.is_err(), "空数据应返回错误");
549        match result {
550            Err(QrCodeError::Encoding(_)) => {}
551            other => panic!("期望 Encoding 错误,得到: {other:?}"),
552        }
553    }
554
555    // ------------------------------------------------------------------------
556    // generate_png 测试
557    // ------------------------------------------------------------------------
558
559    /// 测试 PNG 生成基本功能(验证 PNG 头部 magic bytes)
560    #[test]
561    fn test_generate_png_basic() {
562        let generator = QrCodeGenerator::new();
563        let png = generator.generate_png("https://example.com").unwrap();
564        assert!(!png.is_empty(), "PNG 字节流不能为空");
565
566        // PNG magic bytes: 89 50 4E 47 0D 0A 1A 0A
567        assert_eq!(png[0], 0x89, "PNG magic byte 0");
568        assert_eq!(png[1], 0x50, "PNG magic byte 1 ('P')");
569        assert_eq!(png[2], 0x4E, "PNG magic byte 2 ('N')");
570        assert_eq!(png[3], 0x47, "PNG magic byte 3 ('G')");
571        assert_eq!(png[4], 0x0D, "PNG magic byte 4 (CR)");
572        assert_eq!(png[5], 0x0A, "PNG magic byte 5 (LF)");
573        assert_eq!(png[6], 0x1A, "PNG magic byte 6");
574        assert_eq!(png[7], 0x0A, "PNG magic byte 7 (LF)");
575    }
576
577    /// 测试 PNG 输出有效头部
578    #[test]
579    fn test_generate_png_valid_output() {
580        let generator = QrCodeGenerator::new();
581        let png = generator.generate_png("test data 12345").unwrap();
582        // 验证 PNG 签名(8 字节)
583        let png_signature: [u8; 8] = [0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A];
584        assert_eq!(&png[..8], &png_signature, "PNG 头部签名必须匹配");
585        // 至少有 IHDR chunk
586        assert!(png.len() > 24, "PNG 数据长度应超过头部+IHDR");
587    }
588
589    // ------------------------------------------------------------------------
590    // generate_svg 测试
591    // ------------------------------------------------------------------------
592
593    /// 测试 SVG 生成基本功能(含 <svg> 标签)
594    #[test]
595    fn test_generate_svg_basic() {
596        let generator = QrCodeGenerator::new();
597        let svg = generator.generate_svg("Hello SVG").unwrap();
598        assert!(!svg.is_empty(), "SVG 字符串不能为空");
599        assert!(svg.contains("<svg"), "SVG 必须包含 <svg> 标签");
600        assert!(svg.contains("</svg>"), "SVG 必须包含 </svg> 闭合标签");
601    }
602
603    /// 测试 SVG 包含二维码数据(rect/path 元素)
604    #[test]
605    fn test_generate_svg_contains_data() {
606        let generator = QrCodeGenerator::new();
607        let svg = generator.generate_svg("Data content test 12345").unwrap();
608        // SVG 应包含路径或矩形元素来绘制二维码模块
609        assert!(
610            svg.contains("<rect") || svg.contains("<path"),
611            "SVG 必须包含 rect 或 path 元素来表示二维码模块"
612        );
613        // 验证 SVG 中包含前景色 hex(默认黑色 #000000)
614        assert!(svg.contains("#000000"), "SVG 应包含默认前景色 #000000");
615    }
616
617    // ------------------------------------------------------------------------
618    // 对比测试
619    // ------------------------------------------------------------------------
620
621    /// 测试不同数据生成不同矩阵
622    #[test]
623    fn test_generate_different_data_different_matrix() {
624        let generator = QrCodeGenerator::new();
625        let matrix1 = generator.generate_matrix("data one").unwrap();
626        let matrix2 = generator.generate_matrix("data two").unwrap();
627
628        // 两个矩阵不应完全相同
629        assert_ne!(matrix1, matrix2, "不同数据应生成不同的二维码矩阵");
630    }
631
632    /// 测试高容错级别生成
633    #[test]
634    fn test_generate_high_error_correction() {
635        let config = QrCodeConfig::new().with_error_correction_level(ErrorCorrectionLevel::High);
636        let generator = QrCodeGenerator::with_config(config);
637
638        // 高容错级别应能正常生成 PNG 和 SVG
639        let png = generator.generate_png("High EC test").unwrap();
640        assert!(!png.is_empty(), "高容错 PNG 不应为空");
641
642        let svg = generator.generate_svg("High EC test").unwrap();
643        assert!(svg.contains("<svg"), "高容错 SVG 应包含 <svg> 标签");
644
645        // 矩阵应能正常生成
646        let matrix = generator.generate_matrix("High EC test").unwrap();
647        assert!(!matrix.is_empty(), "高容错矩阵不应为空");
648
649        // 验证配置确实为 High
650        assert_eq!(
651            generator.config().error_correction_level,
652            ErrorCorrectionLevel::High
653        );
654    }
655}