Skip to main content

qrcode_generator_evcxr/
lib.rs

1pub use qrcode_generator::QrCodeEcc;
2
3/// Trait for types that can be converted into a QR code matrix.
4pub trait QrCodeable {
5    fn to_matrix(&self, ec_level: QrCodeEcc) -> Vec<Vec<bool>>;
6}
7
8impl QrCodeable for str {
9    fn to_matrix(&self, ec_level: QrCodeEcc) -> Vec<Vec<bool>> {
10        qrcode_generator::to_matrix_from_str(self, ec_level)
11            .expect("Failed to generate QR code matrix")
12    }
13}
14
15impl QrCodeable for String {
16    fn to_matrix(&self, ec_level: QrCodeEcc) -> Vec<Vec<bool>> {
17        qrcode_generator::to_matrix_from_str(self, ec_level)
18            .expect("Failed to generate QR code matrix")
19    }
20}
21
22impl QrCodeable for Vec<Vec<bool>> {
23    fn to_matrix(&self, _ec_level: QrCodeEcc) -> Vec<Vec<bool>> {
24        self.clone()
25    }
26}
27
28/// Blanket impl so that `&str`, `&String`, `&&str`, etc. all work.
29impl<T: QrCodeable + ?Sized> QrCodeable for &T {
30    fn to_matrix(&self, ec_level: QrCodeEcc) -> Vec<Vec<bool>> {
31        (**self).to_matrix(ec_level)
32    }
33}
34
35/// Configuration for QR code rendering.
36pub struct QrConfig {
37    /// Error correction level.
38    pub ec_level: QrCodeEcc,
39    /// Total SVG size in pixels (width and height).
40    pub size: usize,
41    /// Quiet zone margin in modules.
42    pub margin: usize,
43}
44
45impl Default for QrConfig {
46    fn default() -> Self {
47        QrConfig {
48            ec_level: QrCodeEcc::Medium,
49            size: 256,
50            margin: 4,
51        }
52    }
53}
54
55/// Render a QR code bool matrix as an SVG string.
56///
57/// - `matrix`: 2D bool grid where `true` = black module
58/// - `size`: total SVG width/height in pixels
59/// - `margin`: quiet zone in modules around the QR data
60pub fn render_svg(matrix: &[Vec<bool>], size: usize, margin: usize) -> String {
61    let data_modules = matrix.len();
62    let total_modules = data_modules + 2 * margin;
63
64    if total_modules == 0 {
65        return format!(
66            "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"{size}\" height=\"{size}\"></svg>"
67        );
68    }
69
70    let cell_size = size as f64 / total_modules as f64;
71
72    let mut svg = format!(
73        "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"{size}\" height=\"{size}\" \
74         viewBox=\"0 0 {size} {size}\">\
75         <rect width=\"{size}\" height=\"{size}\" fill=\"white\"/>"
76    );
77
78    for (row_idx, row) in matrix.iter().enumerate() {
79        for (col_idx, &cell) in row.iter().enumerate() {
80            if cell {
81                let x = ((col_idx + margin) as f64 * cell_size).round() as usize;
82                let y = ((row_idx + margin) as f64 * cell_size).round() as usize;
83                let w = (((col_idx + margin + 1) as f64 * cell_size).round() as usize) - x;
84                let h = (((row_idx + margin + 1) as f64 * cell_size).round() as usize) - y;
85                svg.push_str(&format!(
86                    "<rect x=\"{x}\" y=\"{y}\" width=\"{w}\" height=\"{h}\" fill=\"black\"/>"
87                ));
88            }
89        }
90    }
91
92    svg.push_str("</svg>");
93    svg
94}
95
96/// Display a QR code in Jupyter using default settings.
97///
98/// Accepts any type implementing `QrCodeable`: `&str`, `String`, or `Vec<Vec<bool>>`.
99pub fn draw_qrcode(input: impl QrCodeable) {
100    draw_qrcode_with_config(input, QrConfig::default());
101}
102
103/// Display a QR code in Jupyter with custom configuration.
104///
105/// Accepts any type implementing `QrCodeable`: `&str`, `String`, or `Vec<Vec<bool>>`.
106pub fn draw_qrcode_with_config(input: impl QrCodeable, config: QrConfig) {
107    let matrix = input.to_matrix(config.ec_level);
108    let svg = render_svg(&matrix, config.size, config.margin);
109    println!("EVCXR_BEGIN_CONTENT image/svg+xml");
110    println!("{}", svg);
111    println!("EVCXR_END_CONTENT");
112}