Skip to main content

qrcode_rs/
lib.rs

1//! QRCode encoder
2//!
3//! This crate provides a QR code and Micro QR code encoder for binary data.
4//!
5#![cfg_attr(feature = "image", doc = "```rust")]
6#![cfg_attr(not(feature = "image"), doc = "```ignore")]
7//! use qrcode_rs::QrCode;
8//! use image::Luma;
9//!
10//! // Encode some data into bits.
11//! let code = QrCode::new(b"01234567").unwrap();
12//!
13//! // Render the bits into an image.
14//! let image = code.render::<Luma<u8>>().build();
15//!
16//! // Save the image.
17//! # if cfg!(unix) {
18//! image.save("/tmp/qrcode.png").unwrap();
19//! # }
20//!
21//! // You can also render it into a string.
22//! let string = code.render()
23//!     .light_color(' ')
24//!     .dark_color('#')
25//!     .build();
26//! println!("{}", string);
27//! ```
28
29#![cfg_attr(docsrs, feature(doc_cfg))]
30#![deny(clippy::uninlined_format_args, clippy::manual_range_contains, clippy::semicolon_if_nothing_returned)]
31#![allow(
32    clippy::must_use_candidate, // This is just annoying.
33)]
34
35pub mod bits;
36pub mod canvas;
37mod cast;
38pub mod ec;
39pub mod optimize;
40pub mod render;
41pub mod types;
42pub use crate::types::{Color, EcLevel, Mode, QrError, QrResult, Version};
43
44use crate::cast::As;
45use crate::render::{Pixel, Renderer};
46use std::iter::FusedIterator;
47use std::ops::Index;
48
49/// The encoded QR code symbol.
50#[derive(Clone)]
51pub struct QrCode {
52    content: Vec<Color>,
53    version: Version,
54    ec_level: EcLevel,
55    width: usize,
56}
57
58impl QrCode {
59    /// Constructs a new QR code which automatically encodes the given data.
60    ///
61    /// This method uses the "medium" error correction level and automatically
62    /// chooses the smallest QR code.
63    ///
64    ///     use qrcode_rs::QrCode;
65    ///
66    ///     let code = QrCode::new(b"Some data").unwrap();
67    ///
68    /// # Errors
69    ///
70    /// Returns error if the QR code cannot be constructed, e.g. when the data
71    /// is too long.
72    pub fn new<D: AsRef<[u8]>>(data: D) -> QrResult<Self> {
73        Self::with_error_correction_level(data, EcLevel::M)
74    }
75
76    /// Constructs a new QR code which automatically encodes the given data at a
77    /// specific error correction level.
78    ///
79    /// This method automatically chooses the smallest QR code.
80    ///
81    ///     use qrcode_rs::{QrCode, EcLevel};
82    ///
83    ///     let code = QrCode::with_error_correction_level(b"Some data", EcLevel::H).unwrap();
84    ///
85    /// # Errors
86    ///
87    /// Returns error if the QR code cannot be constructed, e.g. when the data
88    /// is too long.
89    pub fn with_error_correction_level<D: AsRef<[u8]>>(data: D, ec_level: EcLevel) -> QrResult<Self> {
90        let bits = bits::encode_auto(data.as_ref(), ec_level)?;
91        Self::with_bits(bits, ec_level)
92    }
93
94    /// Constructs a new Micro QR code which automatically encodes the given
95    /// data.
96    ///
97    /// This method uses the "medium" error correction level and automatically
98    /// chooses the smallest Micro QR code.
99    ///
100    ///     use qrcode_rs::QrCode;
101    ///
102    ///     let code = QrCode::new_micro(b"123").unwrap();
103    ///
104    /// # Errors
105    ///
106    /// Returns error if the data cannot be encoded as a Micro QR code, e.g.
107    /// when the data is too long.
108    pub fn new_micro<D: AsRef<[u8]>>(data: D) -> QrResult<Self> {
109        Self::micro_with_error_correction_level(data, EcLevel::M)
110    }
111
112    /// Constructs a new Micro QR code which automatically encodes the given
113    /// data at a specific error correction level.
114    ///
115    /// This method automatically chooses the smallest Micro QR code.
116    ///
117    ///     use qrcode_rs::{QrCode, EcLevel};
118    ///
119    ///     let code = QrCode::micro_with_error_correction_level(b"123", EcLevel::L).unwrap();
120    ///
121    /// # Errors
122    ///
123    /// Returns error if the data cannot be encoded as a Micro QR code, e.g.
124    /// when the data is too long, or when the error correction level is not
125    /// supported by any Micro QR version.
126    pub fn micro_with_error_correction_level<D: AsRef<[u8]>>(data: D, ec_level: EcLevel) -> QrResult<Self> {
127        let bits = bits::encode_auto_micro(data.as_ref(), ec_level)?;
128        Self::with_bits(bits, ec_level)
129    }
130
131    /// Constructs a new QR code for the given version and error correction
132    /// level.
133    ///
134    ///     use qrcode_rs::{QrCode, Version, EcLevel};
135    ///
136    ///     let code = QrCode::with_version(b"Some data", Version::Normal(5), EcLevel::M).unwrap();
137    ///
138    /// This method can also be used to generate Micro QR code.
139    ///
140    ///     use qrcode_rs::{QrCode, Version, EcLevel};
141    ///
142    ///     let micro_code = QrCode::with_version(b"123", Version::Micro(1), EcLevel::L).unwrap();
143    ///
144    /// # Errors
145    ///
146    /// Returns error if the QR code cannot be constructed, e.g. when the data
147    /// is too long, or when the version and error correction level are
148    /// incompatible.
149    pub fn with_version<D: AsRef<[u8]>>(data: D, version: Version, ec_level: EcLevel) -> QrResult<Self> {
150        let mut bits = bits::Bits::new(version);
151        bits.push_optimal_data(data.as_ref())?;
152        bits.push_terminator(ec_level)?;
153        Self::with_bits(bits, ec_level)
154    }
155
156    /// Constructs a new QR code with encoded bits.
157    ///
158    /// Use this method only if there are very special need to manipulate the
159    /// raw bits before encoding. Some examples are:
160    ///
161    /// * Encode data using specific character set with ECI
162    /// * Use the FNC1 modes
163    /// * Avoid the optimal segmentation algorithm
164    ///
165    /// See the `Bits` structure for detail.
166    ///
167    ///     #![allow(unused_must_use)]
168    ///
169    ///     use qrcode_rs::{QrCode, Version, EcLevel};
170    ///     use qrcode_rs::bits::Bits;
171    ///
172    ///     let mut bits = Bits::new(Version::Normal(1));
173    ///     bits.push_eci_designator(9);
174    ///     bits.push_byte_data(b"\xca\xfe\xe4\xe9\xea\xe1\xf2 QR");
175    ///     bits.push_terminator(EcLevel::L);
176    ///     let qrcode = QrCode::with_bits(bits, EcLevel::L);
177    ///
178    /// # Errors
179    ///
180    /// Returns error if the QR code cannot be constructed, e.g. when the bits
181    /// are too long, or when the version and error correction level are
182    /// incompatible.
183    pub fn with_bits(bits: bits::Bits, ec_level: EcLevel) -> QrResult<Self> {
184        let version = bits.version();
185        let data = bits.into_bytes();
186        let (encoded_data, ec_data) = ec::construct_codewords(&data, version, ec_level)?;
187        let mut canvas = canvas::Canvas::new(version, ec_level);
188        canvas.draw_all_functional_patterns();
189        canvas.draw_data(&encoded_data, &ec_data);
190        let canvas = canvas.apply_best_mask();
191        Ok(Self { content: canvas.into_colors(), version, ec_level, width: version.width().as_usize() })
192    }
193
194    /// Gets the version of this QR code.
195    pub const fn version(&self) -> Version {
196        self.version
197    }
198
199    /// Gets the error correction level of this QR code.
200    pub const fn error_correction_level(&self) -> EcLevel {
201        self.ec_level
202    }
203
204    /// Gets the number of modules per side, i.e. the width of this QR code.
205    ///
206    /// The width here does not contain the quiet zone paddings.
207    pub const fn width(&self) -> usize {
208        self.width
209    }
210
211    /// Gets the maximum number of allowed erratic modules can be introduced
212    /// before the data becomes corrupted. Note that errors should not be
213    /// introduced to functional modules.
214    pub fn max_allowed_errors(&self) -> usize {
215        ec::max_allowed_errors(self.version, self.ec_level).expect("invalid version or ec_level")
216    }
217
218    /// Returns metadata about this QR code (version, error-correction level,
219    /// dimensions, module count, error tolerance, and data capacity).
220    ///
221    /// # Examples
222    ///
223    /// ```rust
224    /// use qrcode_rs::QrCode;
225    ///
226    /// let code = QrCode::new(b"hello").unwrap();
227    /// let info = code.info();
228    /// assert_eq!(info.width(), code.width());
229    /// assert_eq!(info.module_count(), code.width() * code.width());
230    /// assert!(info.data_capacity_bytes() > 0);
231    /// ```
232    #[must_use]
233    pub fn info(&self) -> Info {
234        Info {
235            version: self.version,
236            ec_level: self.ec_level,
237            width: self.width,
238            module_count: self.width * self.width,
239            max_allowed_errors: self.max_allowed_errors(),
240            data_capacity_bytes: bits::data_capacity_bits(self.version, self.ec_level).map(|b| b / 8).unwrap_or(0),
241        }
242    }
243
244    /// Checks whether a module at coordinate (x, y) is a functional module or
245    /// not.
246    pub fn is_functional(&self, x: usize, y: usize) -> bool {
247        let x = x.try_into().expect("coordinate is too large for QR code");
248        let y = y.try_into().expect("coordinate is too large for QR code");
249        canvas::is_functional(self.version, self.version.width(), x, y)
250    }
251
252    /// Converts the QR code into a human-readable string. This is mainly for
253    /// debugging only.
254    pub fn to_debug_str(&self, on_char: char, off_char: char) -> String {
255        self.render().quiet_zone(false).dark_color(on_char).light_color(off_char).build()
256    }
257
258    /// Converts the QR code to a vector of colors.
259    pub fn to_colors(&self) -> Vec<Color> {
260        self.content.clone()
261    }
262
263    /// Converts the QR code to a vector of colors.
264    pub fn into_colors(self) -> Vec<Color> {
265        self.content
266    }
267
268    /// Renders the QR code into an image. The result is an image builder, which
269    /// you may do some additional configuration before copying it into a
270    /// concrete image.
271    ///  Note: the`image` crate itself also provides method to rotate the image,
272    /// or overlay a logo on top of the QR code.
273    /// # Examples
274    ///
275    #[cfg_attr(feature = "image", doc = " ```rust")]
276    #[cfg_attr(not(feature = "image"), doc = " ```ignore")]
277    /// # use qrcode_rs::QrCode;
278    /// # use image::Rgb;
279    ///
280    /// let image = QrCode::new(b"hello").unwrap()
281    ///                     .render()
282    ///                     .dark_color(Rgb([0, 0, 128]))
283    ///                     .light_color(Rgb([224, 224, 224])) // adjust colors
284    ///                     .quiet_zone(false)          // disable quiet zone (white border)
285    ///                     .min_dimensions(300, 300)   // sets minimum image size
286    ///                     .build();
287    /// ```
288    ///
289    pub fn render<P: Pixel>(&self) -> Renderer<'_, P> {
290        let quiet_zone = if self.version.is_micro() { 2 } else { 4 };
291        Renderer::new(&self.content, self.width, quiet_zone)
292    }
293}
294
295impl QrCode {
296    /// Creates a [`QrCodeBuilder`] for configuring and constructing a QR code.
297    ///
298    /// This is an ergonomic alternative to the `with_*` constructors. The
299    /// builder uses the same encoding paths, so its output is identical to the
300    /// equivalent constructor.
301    ///
302    /// # Examples
303    ///
304    /// ```rust
305    /// use qrcode_rs::{QrCode, EcLevel};
306    ///
307    /// let code = QrCode::builder(b"https://example.com")
308    ///     .ec_level(EcLevel::H)
309    ///     .build()
310    ///     .unwrap();
311    /// # let _ = code;
312    /// ```
313    pub fn builder<D: AsRef<[u8]>>(data: D) -> QrCodeBuilder<D> {
314        QrCodeBuilder::new(data)
315    }
316
317    /// Returns an iterator yielding one [`Row`] of modules at a time.
318    ///
319    /// Each row iterates over the module [`Color`]s from left to right. The
320    /// quiet zone is *not* included.
321    ///
322    /// # Examples
323    ///
324    /// ```rust
325    /// use qrcode_rs::QrCode;
326    ///
327    /// let code = QrCode::new(b"hi").unwrap();
328    /// for row in code.rows() {
329    ///     for color in row {
330    ///         # let _ = color;
331    ///     }
332    /// }
333    /// ```
334    pub fn rows(&self) -> Rows<'_> {
335        Rows { code: self, y: 0 }
336    }
337
338    /// Returns an iterator over the `(x, y)` coordinates of every dark module,
339    /// convenient for custom rendering. The quiet zone is *not* included.
340    ///
341    /// # Examples
342    ///
343    /// ```rust
344    /// use qrcode_rs::QrCode;
345    ///
346    /// let code = QrCode::new(b"hi").unwrap();
347    /// let dark_count = code.dark_modules().count();
348    /// # let _ = dark_count;
349    /// ```
350    pub fn dark_modules(&self) -> DarkModules<'_> {
351        DarkModules { code: self, idx: 0 }
352    }
353
354    /// Encodes a URL, using high error correction (robust to print damage).
355    ///
356    /// # Errors
357    ///
358    /// Returns an error only if the URL is too long to encode.
359    pub fn for_url<D: AsRef<[u8]>>(url: D) -> QrResult<Self> {
360        Self::with_error_correction_level(url, EcLevel::H)
361    }
362
363    /// Encodes plain text at the default (medium) error correction level.
364    ///
365    /// # Errors
366    ///
367    /// Returns an error only if the text is too long to encode.
368    pub fn for_text<D: AsRef<[u8]>>(text: D) -> QrResult<Self> {
369        Self::new(text)
370    }
371
372    /// Encodes a WiFi configuration that most phone cameras will offer to join.
373    ///
374    /// `auth` is one of `WPA`, `WEP` or `nopass`. Special characters in the
375    /// SSID/password are backslash-escaped per the WiFi QR specification.
376    ///
377    /// # Errors
378    ///
379    /// Returns an error if the resulting payload is too long to encode.
380    ///
381    /// # Examples
382    ///
383    /// ```rust
384    /// use qrcode_rs::QrCode;
385    ///
386    /// let code = QrCode::for_wifi("MyNetwork", "p\\a;ss", "WPA").unwrap();
387    /// # let _ = code;
388    /// ```
389    pub fn for_wifi(ssid: &str, password: &str, auth: &str) -> QrResult<Self> {
390        let mut payload = String::from("WIFI:T:");
391        payload.push_str(auth);
392        payload.push_str(";S:");
393        push_escaped_wifi(&mut payload, ssid);
394        payload.push_str(";P:");
395        push_escaped_wifi(&mut payload, password);
396        payload.push_str(";;");
397        Self::new(payload)
398    }
399
400    /// Encodes a minimal vCard 3.0 contact card.
401    ///
402    /// # Errors
403    ///
404    /// Returns an error if the resulting payload is too long to encode.
405    ///
406    /// # Examples
407    ///
408    /// ```rust
409    /// use qrcode_rs::QrCode;
410    ///
411    /// let code = QrCode::for_vcard("John Doe", "+1234567890", "john@example.com").unwrap();
412    /// # let _ = code;
413    /// ```
414    pub fn for_vcard(name: &str, phone: &str, email: &str) -> QrResult<Self> {
415        let vcard = format!("BEGIN:VCARD\r\nVERSION:3.0\r\nFN:{name}\r\nTEL:{phone}\r\nEMAIL:{email}\r\nEND:VCARD\r\n");
416        Self::new(vcard)
417    }
418
419    /// Encodes a GS1 data carrier (FNC1 in first position), e.g. a GTIN /
420    /// application-identifier payload such as
421    /// `"010491234512345915970331301234561842"`. Uses medium error correction
422    /// and the smallest fitting version.
423    ///
424    /// # Errors
425    ///
426    /// Returns an error if the data is too long to encode.
427    ///
428    /// # Examples
429    ///
430    /// ```rust
431    /// use qrcode_rs::QrCode;
432    ///
433    /// let code = QrCode::for_gs1("010491234512345915970331301234561842").unwrap();
434    /// # let _ = code;
435    /// ```
436    pub fn for_gs1<D: AsRef<[u8]>>(data: D) -> QrResult<Self> {
437        let data = data.as_ref();
438        for v in 1..=40 {
439            let version = Version::Normal(v);
440            let mut bits = bits::Bits::new(version);
441            if bits.push_fnc1_first_position().is_err()
442                || bits.push_optimal_data(data).is_err()
443                || bits.push_terminator(EcLevel::M).is_err()
444            {
445                continue;
446            }
447            return Self::with_bits(bits, EcLevel::M);
448        }
449        Err(QrError::DataTooLong)
450    }
451
452    /// Generates accessible alt text describing a QR code that encodes `data`.
453    ///
454    /// URLs are described as "linking to …"; other payloads as "containing: …".
455    /// Use the result as the `alt` of an `<img>` or the `aria-label` of an inline
456    /// SVG so assistive technology can describe the code without decoding it.
457    ///
458    /// This is an associated function (it does not require a constructed
459    /// [`QrCode`]), so the input data does not need to be retained on the code.
460    ///
461    /// # Examples
462    ///
463    /// ```rust
464    /// use qrcode_rs::QrCode;
465    ///
466    /// assert_eq!(QrCode::alt_text("https://example.com"), "QR code linking to https://example.com");
467    /// assert_eq!(QrCode::alt_text("hello"), "QR code containing: hello");
468    /// ```
469    #[must_use]
470    pub fn alt_text<D: AsRef<[u8]>>(data: D) -> String {
471        let text = String::from_utf8_lossy(data.as_ref());
472        if text.starts_with("http://") || text.starts_with("https://") {
473            format!("QR code linking to {text}")
474        } else {
475            format!("QR code containing: {text}")
476        }
477    }
478
479    /// Generates alt text with a custom formatter that receives the raw bytes.
480    ///
481    /// # Examples
482    ///
483    /// ```rust
484    /// use qrcode_rs::QrCode;
485    ///
486    /// let alt = QrCode::alt_text_custom("hello", |data| {
487    ///     format!("A QR code with {} bytes", data.len())
488    /// });
489    /// assert_eq!(alt, "A QR code with 5 bytes");
490    /// ```
491    #[must_use]
492    pub fn alt_text_custom<D: AsRef<[u8]>, F: FnOnce(&[u8]) -> String>(data: D, f: F) -> String {
493        f(data.as_ref())
494    }
495
496    /// Encodes `data` forced into a single `mode` at a pinned version. Used by
497    /// [`QrCodeBuilder::build`] when both a version and an encoding-mode hint
498    /// are set.
499    fn with_mode<D: AsRef<[u8]>>(data: D, version: Version, ec_level: EcLevel, mode: Mode) -> QrResult<Self> {
500        let mut bits = bits::Bits::new(version);
501        match mode {
502            Mode::Numeric => bits.push_numeric_data(data.as_ref())?,
503            Mode::Alphanumeric => bits.push_alphanumeric_data(data.as_ref())?,
504            Mode::Byte => bits.push_byte_data(data.as_ref())?,
505            Mode::Kanji => bits.push_kanji_data(data.as_ref())?,
506        }
507        bits.push_terminator(ec_level)?;
508        Self::with_bits(bits, ec_level)
509    }
510
511    /// Encodes `data` forced into a single `mode`, auto-selecting the smallest
512    /// fitting version. Used by [`QrCodeBuilder::build`] when an encoding-mode
513    /// hint is set without a pinned version. Returns the underlying error
514    /// (e.g. [`QrError::InvalidCharacter`]) if the data is incompatible with the
515    /// forced mode.
516    fn with_mode_auto<D: AsRef<[u8]>>(data: D, ec_level: EcLevel, mode: Mode) -> QrResult<Self> {
517        let data = data.as_ref();
518        let mut last_err = QrError::DataTooLong;
519        for v in 1..=40 {
520            let version = Version::Normal(v);
521            let mut bits = bits::Bits::new(version);
522            let pushed = match mode {
523                Mode::Numeric => bits.push_numeric_data(data),
524                Mode::Alphanumeric => bits.push_alphanumeric_data(data),
525                Mode::Byte => bits.push_byte_data(data),
526                Mode::Kanji => bits.push_kanji_data(data),
527            };
528            if let Err(e) = pushed {
529                last_err = e;
530                continue;
531            }
532            if let Err(e) = bits.push_terminator(ec_level) {
533                last_err = e;
534                continue;
535            }
536            return Self::with_bits(bits, ec_level);
537        }
538        Err(last_err)
539    }
540}
541
542/// Backslash-escapes the characters that are special in a WiFi QR payload.
543fn push_escaped_wifi(out: &mut String, s: &str) {
544    for c in s.chars() {
545        if matches!(c, ';' | ',' | '"' | '\\' | ':') {
546            out.push('\\');
547        }
548        out.push(c);
549    }
550}
551
552impl Index<(usize, usize)> for QrCode {
553    type Output = Color;
554
555    fn index(&self, (x, y): (usize, usize)) -> &Color {
556        let index = y * self.width + x;
557        &self.content[index]
558    }
559}
560
561//------------------------------------------------------------------------------
562//{{{ QrCodeBuilder
563
564/// A builder for [`QrCode`], offering ergonomic, chainable configuration.
565///
566/// Construct one with [`QrCode::builder`]. The builder delegates to the
567/// existing constructors, so its output is identical to calling them directly.
568#[derive(Clone, Debug)]
569pub struct QrCodeBuilder<D: AsRef<[u8]>> {
570    data: D,
571    ec_level: EcLevel,
572    version: Option<Version>,
573    micro: bool,
574    mode_hint: Option<Mode>,
575}
576
577impl<D: AsRef<[u8]>> QrCodeBuilder<D> {
578    fn new(data: D) -> Self {
579        Self { data, ec_level: EcLevel::M, version: None, micro: false, mode_hint: None }
580    }
581
582    /// Sets the error correction level (default [`EcLevel::M`]).
583    #[must_use]
584    pub fn ec_level(mut self, ec_level: EcLevel) -> Self {
585        self.ec_level = ec_level;
586        self
587    }
588
589    /// Pins a specific QR [`Version`]. When set, `build()` behaves like
590    /// [`QrCode::with_version`]. If [`micro`](Self::micro) is also set, the
591    /// explicit version takes precedence.
592    #[must_use]
593    pub fn version(mut self, version: Version) -> Self {
594        self.version = Some(version);
595        self
596    }
597
598    /// Requests a Micro QR code (the smallest fitting Micro version), behaving
599    /// like [`QrCode::micro_with_error_correction_level`] when no explicit
600    /// [`version`](Self::version) is set.
601    #[must_use]
602    pub fn micro(mut self, yes: bool) -> Self {
603        self.micro = yes;
604        self
605    }
606
607    /// Hints the encoding [`Mode`] (e.g. [`Mode::Byte`]), bypassing automatic
608    /// mode optimization. When a [`version`](Self::version) is also set it is
609    /// used directly; otherwise the smallest fitting version for that mode is
610    /// auto-selected.
611    ///
612    /// The data must be encodable in the chosen mode: [`Mode::Kanji`] validates
613    /// its Shift-JIS pairs and [`Mode::Byte`] accepts anything, but
614    /// [`Mode::Numeric`] / [`Mode::Alphanumeric`] assume their input already
615    /// matches (as automatic optimization would never select them otherwise).
616    #[must_use]
617    pub fn encoding_mode(mut self, mode: Mode) -> Self {
618        self.mode_hint = Some(mode);
619        self
620    }
621
622    /// Forces a specific encoding [`Mode`], bypassing automatic optimization.
623    /// This is an alias for [`encoding_mode`](Self::encoding_mode), provided for
624    /// familiarity with the QR-code vocabulary.
625    #[must_use]
626    pub fn force_mode(self, mode: Mode) -> Self {
627        self.encoding_mode(mode)
628    }
629
630    /// Builds the [`QrCode`].
631    ///
632    /// # Errors
633    ///
634    /// Propagates any [`QrError`](crate::QrError) from the underlying encoder
635    /// (e.g. data too long, or an incompatible version / error-correction
636    /// combination).
637    pub fn build(self) -> QrResult<QrCode> {
638        if let Some(version) = self.version {
639            if let Some(mode) = self.mode_hint {
640                return QrCode::with_mode(self.data, version, self.ec_level, mode);
641            }
642            return QrCode::with_version(self.data, version, self.ec_level);
643        }
644        if let Some(mode) = self.mode_hint {
645            return QrCode::with_mode_auto(self.data, self.ec_level, mode);
646        }
647        if self.micro {
648            return QrCode::micro_with_error_correction_level(self.data, self.ec_level);
649        }
650        QrCode::with_error_correction_level(self.data, self.ec_level)
651    }
652}
653
654//}}}
655//------------------------------------------------------------------------------
656//{{{ Info
657
658/// Metadata about a constructed [`QrCode`], returned by [`QrCode::info`].
659///
660/// Fields that require retaining the input data or the chosen mask (e.g.
661/// `encoding_modes`, `mask_pattern`, `remaining_capacity`) are intentionally
662/// omitted to keep `QrCode` zero-overhead; they may be added in a later version.
663#[derive(Clone, Copy, Debug, PartialEq, Eq)]
664pub struct Info {
665    version: Version,
666    ec_level: EcLevel,
667    width: usize,
668    module_count: usize,
669    max_allowed_errors: usize,
670    data_capacity_bytes: usize,
671}
672
673impl Info {
674    /// The QR [`Version`].
675    #[must_use]
676    pub const fn version(&self) -> Version {
677        self.version
678    }
679
680    /// The error correction level.
681    #[must_use]
682    pub const fn ec_level(&self) -> EcLevel {
683        self.ec_level
684    }
685
686    /// Modules per side (excluding the quiet zone).
687    #[must_use]
688    pub const fn width(&self) -> usize {
689        self.width
690    }
691
692    /// Total number of modules (`width * width`).
693    #[must_use]
694    pub const fn module_count(&self) -> usize {
695        self.module_count
696    }
697
698    /// Maximum number of erroneous modules that can still be recovered.
699    #[must_use]
700    pub const fn max_allowed_errors(&self) -> usize {
701        self.max_allowed_errors
702    }
703
704    /// Data capacity of this symbol in bytes.
705    #[must_use]
706    pub const fn data_capacity_bytes(&self) -> usize {
707        self.data_capacity_bytes
708    }
709}
710
711//}}}
712//------------------------------------------------------------------------------
713//{{{ Module iterators
714
715/// Iterator over the rows of a [`QrCode`], created by [`QrCode::rows`].
716pub struct Rows<'a> {
717    code: &'a QrCode,
718    y: usize,
719}
720
721impl<'a> Iterator for Rows<'a> {
722    type Item = Row<'a>;
723
724    fn next(&mut self) -> Option<Self::Item> {
725        let w = self.code.width;
726        if self.y < w {
727            let row = Row { code: self.code, y: self.y, x: 0 };
728            self.y += 1;
729            Some(row)
730        } else {
731            None
732        }
733    }
734
735    fn size_hint(&self) -> (usize, Option<usize>) {
736        let rem = self.code.width - self.y;
737        (rem, Some(rem))
738    }
739}
740
741impl<'a> ExactSizeIterator for Rows<'a> {
742    fn len(&self) -> usize {
743        self.code.width - self.y
744    }
745}
746
747impl<'a> FusedIterator for Rows<'a> {}
748
749/// A single row of modules, yielded by [`Rows`]. Iterates over [`Color`]s from
750/// left to right (quiet zone excluded).
751pub struct Row<'a> {
752    code: &'a QrCode,
753    y: usize,
754    x: usize,
755}
756
757impl<'a> Row<'a> {
758    /// The number of modules in this row.
759    #[must_use]
760    pub fn len(&self) -> usize {
761        self.code.width
762    }
763
764    /// Whether the row is empty (always `false` for a valid QR code).
765    #[must_use]
766    pub fn is_empty(&self) -> bool {
767        self.code.width == 0
768    }
769}
770
771impl<'a> Iterator for Row<'a> {
772    type Item = Color;
773
774    fn next(&mut self) -> Option<Color> {
775        let w = self.code.width;
776        if self.x < w {
777            let color = self.code.content[self.y * w + self.x];
778            self.x += 1;
779            Some(color)
780        } else {
781            None
782        }
783    }
784
785    fn size_hint(&self) -> (usize, Option<usize>) {
786        let rem = self.code.width - self.x;
787        (rem, Some(rem))
788    }
789}
790
791impl<'a> ExactSizeIterator for Row<'a> {
792    fn len(&self) -> usize {
793        self.code.width - self.x
794    }
795}
796
797impl<'a> FusedIterator for Row<'a> {}
798
799/// Iterator over the `(x, y)` coordinates of every dark module in a [`QrCode`],
800/// created by [`QrCode::dark_modules`].
801pub struct DarkModules<'a> {
802    code: &'a QrCode,
803    idx: usize,
804}
805
806impl<'a> Iterator for DarkModules<'a> {
807    type Item = (usize, usize);
808
809    fn next(&mut self) -> Option<(usize, usize)> {
810        let w = self.code.width;
811        let content = &self.code.content;
812        while self.idx < content.len() {
813            let i = self.idx;
814            self.idx += 1;
815            if content[i] == Color::Dark {
816                return Some((i % w, i / w));
817            }
818        }
819        None
820    }
821}
822
823impl<'a> FusedIterator for DarkModules<'a> {}
824
825//}}}
826
827#[cfg(test)]
828mod tests {
829    use crate::{EcLevel, QrCode, Version};
830
831    #[test]
832    fn test_annex_i_qr() {
833        // This uses the ISO Annex I as test vector.
834        let code = QrCode::with_version(b"01234567", Version::Normal(1), EcLevel::M).unwrap();
835        assert_eq!(
836            &*code.to_debug_str('#', '.'),
837            "\
838             #######..#.##.#######\n\
839             #.....#..####.#.....#\n\
840             #.###.#.#.....#.###.#\n\
841             #.###.#.##....#.###.#\n\
842             #.###.#.#.###.#.###.#\n\
843             #.....#.#...#.#.....#\n\
844             #######.#.#.#.#######\n\
845             ........#..##........\n\
846             #.#####..#..#.#####..\n\
847             ...#.#.##.#.#..#.##..\n\
848             ..#...##.#.#.#..#####\n\
849             ....#....#.....####..\n\
850             ...######..#.#..#....\n\
851             ........#.#####..##..\n\
852             #######..##.#.##.....\n\
853             #.....#.#.#####...#.#\n\
854             #.###.#.#...#..#.##..\n\
855             #.###.#.##..#..#.....\n\
856             #.###.#.#.##.#..#.#..\n\
857             #.....#........##.##.\n\
858             #######.####.#..#.#.."
859        );
860    }
861
862    #[test]
863    fn test_annex_i_micro_qr() {
864        let code = QrCode::with_version(b"01234567", Version::Micro(2), EcLevel::L).unwrap();
865        assert_eq!(
866            &*code.to_debug_str('#', '.'),
867            "\
868             #######.#.#.#\n\
869             #.....#.###.#\n\
870             #.###.#..##.#\n\
871             #.###.#..####\n\
872             #.###.#.###..\n\
873             #.....#.#...#\n\
874             #######..####\n\
875             .........##..\n\
876             ##.#....#...#\n\
877             .##.#.#.#.#.#\n\
878             ###..#######.\n\
879             ...#.#....##.\n\
880             ###.#..##.###"
881        );
882    }
883}
884
885#[cfg(test)]
886mod api_tests {
887    use crate::{Color, EcLevel, Mode, QrCode, Version};
888
889    fn colors(code: &QrCode) -> Vec<Color> {
890        code.to_colors()
891    }
892
893    #[test]
894    fn builder_matches_with_error_correction_level() {
895        let direct = QrCode::with_error_correction_level(b"Some data", EcLevel::H).unwrap();
896        let built = QrCode::builder(b"Some data").ec_level(EcLevel::H).build().unwrap();
897        assert_eq!(colors(&direct), colors(&built));
898        assert_eq!(direct.version(), built.version());
899        assert_eq!(direct.error_correction_level(), built.error_correction_level());
900    }
901
902    #[test]
903    fn builder_matches_with_version() {
904        let direct = QrCode::with_version(b"Some data", Version::Normal(1), EcLevel::M).unwrap();
905        let built = QrCode::builder(b"Some data").version(Version::Normal(1)).build().unwrap();
906        assert_eq!(colors(&direct), colors(&built));
907    }
908
909    #[test]
910    fn builder_micro_matches() {
911        let direct = QrCode::micro_with_error_correction_level(b"123", EcLevel::L).unwrap();
912        let built = QrCode::builder(b"123").ec_level(EcLevel::L).micro(true).build().unwrap();
913        assert_eq!(colors(&direct), colors(&built));
914        assert!(built.version().is_micro());
915    }
916
917    #[test]
918    fn builder_version_wins_over_micro() {
919        let built = QrCode::builder(b"01234567").version(Version::Micro(2)).micro(true).build().unwrap();
920        assert_eq!(built.version(), Version::Micro(2));
921    }
922
923    #[test]
924    fn builder_forces_byte_mode() {
925        // Forcing Byte mode on digits must differ from the optimal (Numeric) mode.
926        let optimal = QrCode::builder(b"01234567").version(Version::Normal(2)).build().unwrap();
927        let byte = QrCode::builder(b"01234567").version(Version::Normal(2)).encoding_mode(Mode::Byte).build().unwrap();
928        assert_ne!(colors(&optimal), colors(&byte));
929    }
930
931    #[test]
932    fn rows_iterate_full_grid() {
933        let code = QrCode::new(b"hello").unwrap();
934        let w = code.width();
935        let rows: Vec<Vec<Color>> = code.rows().map(|r| r.collect()).collect();
936        assert_eq!(rows.len(), w);
937        assert!(rows.iter().all(|r| r.len() == w));
938        for y in 0..w {
939            for x in 0..w {
940                assert_eq!(rows[y][x], code[(x, y)]);
941            }
942        }
943    }
944
945    #[test]
946    fn rows_exact_size() {
947        let code = QrCode::new(b"hello").unwrap();
948        let mut rows = code.rows();
949        let total = rows.len();
950        let mut counted = 0;
951        while rows.next().is_some() {
952            counted += 1;
953            assert_eq!(rows.len(), total - counted);
954        }
955    }
956
957    #[test]
958    fn dark_modules_match_indexed_dark_cells() {
959        let code = QrCode::new(b"hello").unwrap();
960        let w = code.width();
961        let expected: Vec<(usize, usize)> =
962            (0..w).flat_map(|y| (0..w).map(move |x| (x, y))).filter(|&(x, y)| code[(x, y)] == Color::Dark).collect();
963        let actual: Vec<(usize, usize)> = code.dark_modules().collect();
964        // dark_modules scans in row-major order, matching the construction above.
965        assert_eq!(expected, actual);
966    }
967
968    #[test]
969    fn for_url_uses_high_ec() {
970        let code = QrCode::for_url(b"https://example.com").unwrap();
971        assert_eq!(code.error_correction_level(), EcLevel::H);
972    }
973
974    #[test]
975    fn wifi_escape_helper() {
976        let mut out = String::new();
977        super::push_escaped_wifi(&mut out, "a;b,c\"d\\e:f");
978        assert_eq!(out, "a\\;b\\,c\\\"d\\\\e\\:f");
979    }
980
981    #[test]
982    fn for_wifi_encodes_with_special_chars() {
983        let code = QrCode::for_wifi("My;Net", "a,b", "WPA").unwrap();
984        assert!(code.width() > 0);
985    }
986
987    #[test]
988    fn for_vcard_encodes() {
989        let code = QrCode::for_vcard("John Doe", "+1234567890", "john@example.com").unwrap();
990        assert!(code.width() > 0);
991    }
992
993    #[test]
994    fn for_gs1_encodes() {
995        let code = QrCode::for_gs1("010491234512345915970331301234561842").unwrap();
996        assert!(code.width() > 0);
997        // GS1 uses FNC1 first position; smallest fitting version, medium EC.
998        assert!(!code.version().is_micro());
999        assert_eq!(code.error_correction_level(), crate::EcLevel::M);
1000    }
1001
1002    #[test]
1003    fn info_reports_metadata() {
1004        let code = QrCode::with_version(b"01234567", Version::Normal(1), crate::EcLevel::M).unwrap();
1005        let info = code.info();
1006        assert_eq!(info.version(), Version::Normal(1));
1007        assert_eq!(info.ec_level(), crate::EcLevel::M);
1008        assert_eq!(info.width(), code.width());
1009        assert_eq!(info.module_count(), code.width() * code.width());
1010        assert!(info.data_capacity_bytes() > 0);
1011        // higher EC level => fewer data bytes for the same version
1012        let code_h = QrCode::with_version(b"01234567", Version::Normal(1), crate::EcLevel::H).unwrap();
1013        assert!(info.data_capacity_bytes() > code_h.info().data_capacity_bytes());
1014    }
1015
1016    #[test]
1017    fn force_mode_without_version_auto_selects() {
1018        // Forcing Byte on digits must differ from auto (Numeric) without pinning a version.
1019        let auto = QrCode::new(b"0123456789").unwrap();
1020        let forced_byte = QrCode::builder(b"0123456789").force_mode(Mode::Byte).build().unwrap();
1021        assert_ne!(colors(&auto), colors(&forced_byte));
1022        // Forcing Numeric on digits matches auto (which also picks Numeric).
1023        let forced_num = QrCode::builder(b"0123456789").force_mode(Mode::Numeric).build().unwrap();
1024        assert_eq!(colors(&auto), colors(&forced_num));
1025        // Odd-length Kanji input surfaces InvalidCharacter via the length check.
1026        let err = QrCode::builder(b"\x93").force_mode(Mode::Kanji).build();
1027        assert!(matches!(err, Err(crate::QrError::InvalidCharacter { .. })));
1028    }
1029}
1030
1031#[cfg(all(test, feature = "image"))]
1032mod image_tests {
1033    use crate::{EcLevel, QrCode, Version};
1034    use image::{Luma, Rgb, load_from_memory};
1035
1036    #[test]
1037    fn test_annex_i_qr_as_image() {
1038        let code = QrCode::new(b"01234567").unwrap();
1039        let image = code.render::<Luma<u8>>().build();
1040        let expected =
1041            load_from_memory(include_bytes!("../docs/images/test_annex_i_qr_as_image.png")).unwrap().to_luma8();
1042        assert_eq!(image.dimensions(), expected.dimensions());
1043        assert_eq!(image.into_raw(), expected.into_raw());
1044    }
1045
1046    #[test]
1047    fn test_annex_i_micro_qr_as_image() {
1048        let code = QrCode::with_version(b"01234567", Version::Micro(2), EcLevel::L).unwrap();
1049        let image = code
1050            .render()
1051            .min_dimensions(200, 200)
1052            .dark_color(Rgb([128, 0, 0]))
1053            .light_color(Rgb([255, 255, 128]))
1054            .build();
1055        let expected =
1056            load_from_memory(include_bytes!("../docs/images/test_annex_i_micro_qr_as_image.png")).unwrap().to_rgb8();
1057        assert_eq!(image.dimensions(), expected.dimensions());
1058        assert_eq!(image.into_raw(), expected.into_raw());
1059    }
1060}
1061
1062#[cfg(all(test, feature = "svg"))]
1063mod svg_tests {
1064    use crate::render::svg::Color as SvgColor;
1065    use crate::{EcLevel, QrCode, Version};
1066
1067    #[test]
1068    fn test_annex_i_qr_as_svg() {
1069        let code = QrCode::new(b"01234567").unwrap();
1070        let image = code.render::<SvgColor>().build();
1071        let expected = include_str!("../docs/images/test_annex_i_qr_as_svg.svg");
1072        assert_eq!(&image, expected);
1073    }
1074
1075    #[test]
1076    fn test_annex_i_micro_qr_as_svg() {
1077        let code = QrCode::with_version(b"01234567", Version::Micro(2), EcLevel::L).unwrap();
1078        let image = code
1079            .render()
1080            .min_dimensions(200, 200)
1081            .dark_color(SvgColor("#800000"))
1082            .light_color(SvgColor("#ffff80"))
1083            .build();
1084        let expected = include_str!("../docs/images/test_annex_i_micro_qr_as_svg.svg");
1085        assert_eq!(&image, expected);
1086    }
1087}
1088
1089#[cfg(all(test, feature = "eps"))]
1090mod eps_tests {
1091    use crate::render::eps::Color as EpsColor;
1092    use crate::{EcLevel, QrCode, Version};
1093
1094    #[test]
1095    fn test_annex_i_qr_as_eps() {
1096        let code = QrCode::new(b"01234567").unwrap();
1097        let image = code.render::<EpsColor>().build();
1098        let expected = include_str!("../docs/images/test_annex_i_qr_as_eps.eps");
1099        assert_eq!(&image, expected);
1100    }
1101
1102    #[test]
1103    fn test_annex_i_micro_qr_as_eps() {
1104        let code = QrCode::with_version(b"01234567", Version::Micro(2), EcLevel::L).unwrap();
1105        let image = code
1106            .render()
1107            .min_dimensions(200, 200)
1108            .dark_color(EpsColor([0.5, 0.0, 0.0]))
1109            .light_color(EpsColor([1.0, 1.0, 0.5]))
1110            .build();
1111        let expected = include_str!("../docs/images/test_annex_i_micro_qr_as_eps.eps");
1112        assert_eq!(&image, expected);
1113    }
1114}
1115
1116#[cfg(all(test, feature = "pic"))]
1117mod pic_tests {
1118    use crate::render::pic::Color as PicColor;
1119    use crate::{EcLevel, QrCode, Version};
1120
1121    #[test]
1122    fn test_annex_i_qr_as_pic() {
1123        let code = QrCode::new(b"01234567").unwrap();
1124        let image = code.render::<PicColor>().build();
1125        let expected = include_str!("../docs/images/test_annex_i_qr_as_pic.pic");
1126        assert_eq!(&image, expected);
1127    }
1128
1129    #[test]
1130    fn test_annex_i_micro_qr_as_pic() {
1131        let code = QrCode::with_version(b"01234567", Version::Micro(2), EcLevel::L).unwrap();
1132        let image = code.render::<PicColor>().min_dimensions(1, 1).build();
1133        let expected = include_str!("../docs/images/test_annex_i_micro_qr_as_pic.pic");
1134        assert_eq!(&image, expected);
1135    }
1136}