#![doc(html_root_url = "https://docs.rs/qrcode2/0.18.0/")]
#![cfg_attr(not(feature = "std"), no_std)]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![deny(missing_docs)]
extern crate alloc;
#[cfg(feature = "std")]
extern crate std;
pub mod bits;
pub mod canvas;
mod cast;
pub mod ec;
pub mod error;
pub mod optimize;
pub mod render;
pub mod types;
use alloc::{string::String, vec::Vec};
use core::ops::Index;
#[cfg(feature = "svg")]
pub use csscolorparser;
#[cfg(feature = "image")]
pub use image;
use crate::{
bits::{Bits, RectMicroStrategy},
canvas::Canvas,
cast::As,
render::{Pixel, Renderer},
};
pub use crate::{
error::{Error, Result},
types::{Color, EcLevel, Version},
};
#[derive(Clone, Debug)]
pub struct QrCode {
content: Vec<Color>,
version: Version,
ec_level: EcLevel,
width: usize,
height: usize,
}
impl QrCode {
pub fn new(data: impl AsRef<[u8]>) -> Result<Self> {
Self::with_error_correction_level(data, EcLevel::default())
}
pub fn new_micro(data: impl AsRef<[u8]>) -> Result<Self> {
Self::micro_with_error_correction_level(data, EcLevel::default())
}
pub fn new_rect_micro(data: impl AsRef<[u8]>) -> Result<Self> {
Self::rect_micro_with_error_correction_level(data, EcLevel::default())
}
pub fn with_error_correction_level(data: impl AsRef<[u8]>, ec_level: EcLevel) -> Result<Self> {
let bits = bits::encode_auto(data.as_ref(), ec_level)?;
Self::with_bits(bits, ec_level)
}
pub fn micro_with_error_correction_level(
data: impl AsRef<[u8]>,
ec_level: EcLevel,
) -> Result<Self> {
let bits = bits::encode_auto_micro(data.as_ref(), ec_level)?;
Self::with_bits(bits, ec_level)
}
pub fn rect_micro_with_error_correction_level(
data: impl AsRef<[u8]>,
ec_level: EcLevel,
) -> Result<Self> {
let bits = bits::encode_auto_rect_micro(data.as_ref(), ec_level, RectMicroStrategy::Area)?;
Self::with_bits(bits, ec_level)
}
pub fn with_version(
data: impl AsRef<[u8]>,
version: Version,
ec_level: EcLevel,
) -> Result<Self> {
let mut bits = Bits::new(version);
bits.push_optimal_data(data.as_ref())?;
bits.push_terminator(ec_level)?;
Self::with_bits(bits, ec_level)
}
pub fn with_bits(bits: Bits, ec_level: EcLevel) -> Result<Self> {
let version = bits.version();
let data = bits.into_bytes();
let (encoded_data, ec_data) = ec::construct_codewords(&data, version, ec_level)?;
let mut canvas = Canvas::new(version, ec_level);
canvas.draw_all_functional_patterns();
canvas.draw_data(&encoded_data, &ec_data);
let content = canvas.apply_best_mask().into_colors();
let (width, height) = (version.width().as_usize(), version.height().as_usize());
Ok(Self {
content,
version,
ec_level,
width,
height,
})
}
#[must_use]
pub const fn version(&self) -> Version {
self.version
}
#[must_use]
pub const fn error_correction_level(&self) -> EcLevel {
self.ec_level
}
#[must_use]
pub const fn width(&self) -> usize {
self.width
}
#[must_use]
pub const fn height(&self) -> usize {
self.height
}
#[expect(clippy::missing_panics_doc)]
#[must_use]
pub fn max_allowed_errors(&self) -> usize {
ec::max_allowed_errors(self.version, self.ec_level).unwrap()
}
#[must_use]
pub fn is_functional(&self, x: usize, y: usize) -> bool {
let x = x.try_into().unwrap();
let y = y.try_into().unwrap();
canvas::is_functional(self.version, self.version.width(), x, y)
}
#[must_use]
pub fn to_debug_str(&self, on_char: char, off_char: char) -> String {
self.render()
.has_quiet_zone(false)
.dark_color(on_char)
.light_color(off_char)
.build()
}
#[must_use]
pub fn to_colors(&self) -> Vec<Color> {
self.content.clone()
}
#[must_use]
pub fn into_colors(self) -> Vec<Color> {
self.content
}
#[must_use]
pub fn render<P: Pixel>(&self) -> Renderer<'_, P> {
let quiet_zone = if self.version.is_normal() { 4 } else { 2 };
Renderer::new(&self.content, self.width, self.height, quiet_zone)
}
}
impl Index<(usize, usize)> for QrCode {
type Output = Color;
fn index(&self, (x, y): (usize, usize)) -> &Self::Output {
let index = y * self.width + x;
&self.content[index]
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_annex_i_qr() {
let code = QrCode::with_version(b"01234567", Version::Normal(1), EcLevel::M).unwrap();
assert_eq!(
code.to_debug_str('#', '.'),
concat!(
"#######..#.##.#######\n",
"#.....#..####.#.....#\n",
"#.###.#.#.....#.###.#\n",
"#.###.#.##....#.###.#\n",
"#.###.#.#.###.#.###.#\n",
"#.....#.#...#.#.....#\n",
"#######.#.#.#.#######\n",
"........#..##........\n",
"#.#####..#..#.#####..\n",
"...#.#.##.#.#..#.##..\n",
"..#...##.#.#.#..#####\n",
"....#....#.....####..\n",
"...######..#.#..#....\n",
"........#.#####..##..\n",
"#######..##.#.##.....\n",
"#.....#.#.#####...#.#\n",
"#.###.#.#...#..#.##..\n",
"#.###.#.##..#..#.....\n",
"#.###.#.#.##.#..#.#..\n",
"#.....#........##.##.\n",
"#######.####.#..#.#.."
)
);
}
#[test]
fn test_annex_i_micro_qr() {
let code = QrCode::with_version(b"01234567", Version::Micro(2), EcLevel::L).unwrap();
assert_eq!(
code.to_debug_str('#', '.'),
concat!(
"#######.#.#.#\n",
"#.....#.###.#\n",
"#.###.#..##.#\n",
"#.###.#..####\n",
"#.###.#.###..\n",
"#.....#.#...#\n",
"#######..####\n",
".........##..\n",
"##.#....#...#\n",
".##.#.#.#.#.#\n",
"###..#######.\n",
"...#.#....##.\n",
"###.#..##.###"
)
);
}
#[test]
fn test_annex_i_rmqr() {
let code =
QrCode::with_version(b"0123456", Version::RectMicro(11, 27), EcLevel::H).unwrap();
assert_eq!(
code.to_debug_str('#', '.'),
concat!(
"#######.#.#.#.#.#.#.#.#.###\n",
"#.....#..##.#....###.#..#.#\n",
"#.###.#....#..####.#..#####\n",
"#.###.#.####.##.####...##..\n",
"#.###.#..#.###.######..#.##\n",
"#.....#.###....#..#####..#.\n",
"#######.#########...#.#####\n",
"........#####.#.##.#..#...#\n",
"####......#..#.#..#####.#.#\n",
"#.#.#.#..##..#.#..###.#...#\n",
"###.#.#.#.#.#.#.#.#.#.#####"
)
);
}
}