use crate::encoder::{best_mode, estimated_bit_length, EncodeBits, Encoder};
use crate::error_correction::{
ecc_codewords_per_block, interleave, split_into_blocks, total_data_codewords, ECCLevel,
};
use crate::matrix::{Module, QRMatrix};
use crate::render;
#[derive(Debug, thiserror::Error)]
pub enum QRGenError {
#[error("input too long for ECC level {ecc}: needs more than the largest QR can hold")]
InputTooLong { ecc: ECCLevel },
#[error("input too long for version {version} with ECC level {ecc}: needs {needed_bits} bits, capacity is {capacity_bits}")]
InputTooLongForVersion {
version: u8,
ecc: ECCLevel,
needed_bits: usize,
capacity_bits: usize,
},
#[error("invalid version {version}: must be 1-40")]
InvalidVersion { version: u8 },
#[error("IO error: {0}")]
Io(#[from] std::io::Error),
}
#[derive(Debug)]
pub struct QRCode {
version: u8,
matrix: QRMatrix,
mask_id: u8,
ecc: ECCLevel,
}
fn interleaved_codewords(input: &str, ecc: ECCLevel, version: u8) -> Result<Vec<u8>, QRGenError> {
let total_codewords = total_data_codewords(version, ecc);
let capacity_bits = total_codewords * 8;
let mut bits = Encoder::encode(input, version)?;
let needed_bits = bits.len();
if needed_bits > capacity_bits {
return Err(QRGenError::InputTooLongForVersion {
version,
ecc,
needed_bits,
capacity_bits,
});
}
let terminator = (capacity_bits - bits.len()).min(4);
for _ in 0..terminator {
bits.push(false);
}
while bits.len() % 8 != 0 {
bits.push(false);
}
let mut data_bytes = pack_bits_msb_first(&bits);
let padding = [0xECu8, 0x11];
let mut idx = 0;
while data_bytes.len() < total_codewords {
data_bytes.push(padding[idx % 2]);
idx += 1;
}
debug_assert_eq!(data_bytes.len(), total_codewords);
let _ = ecc_codewords_per_block(version, ecc);
let blocks = split_into_blocks(&data_bytes, version, ecc);
Ok(interleave(&blocks))
}
fn matrix_before_mask(input: &str, ecc: ECCLevel, version: u8) -> Result<QRMatrix, QRGenError> {
let interleaved = interleaved_codewords(input, ecc, version)?;
let mut matrix = QRMatrix::new(version);
matrix.place_function_patterns();
let bit_stream = bytes_to_bits_msb_first(&interleaved);
crate::matrix::data_placement::place_data(&mut matrix, &bit_stream);
Ok(matrix)
}
impl QRCode {
pub fn new(input: &str, ecc: Option<ECCLevel>, version: Option<u8>) -> Result<Self, QRGenError> {
let ecc = ecc.unwrap_or(ECCLevel::M);
let version = match version {
Some(v) => {
if !(1..=40).contains(&v) {
return Err(QRGenError::InvalidVersion { version: v });
}
v
}
None => smallest_version(input, ecc).ok_or(QRGenError::InputTooLong { ecc })?,
};
let mut matrix = matrix_before_mask(input, ecc, version)?;
let mask_id = crate::matrix::masking::find_best_mask(&mut matrix, ecc);
if version >= 7 {
crate::matrix::version_info::place_version_info(&mut matrix, version);
}
Ok(Self { version, matrix, mask_id, ecc })
}
pub fn to_svg(&self, quiet_zone: bool) -> String {
render::render_svg(&self.matrix, quiet_zone)
}
pub fn to_png(&self, size_px: u32, quiet_zone: bool) -> image::ImageBuffer<image::Rgba<u8>, Vec<u8>> {
render::render_png(&self.matrix, size_px, quiet_zone)
}
#[inline]
pub fn width(&self) -> usize {
self.matrix.size
}
#[inline]
pub fn module_is_dark(&self, col: usize, row: usize) -> bool {
self.matrix.get(col, row).is_dark()
}
#[inline]
pub fn mask_id(&self) -> u8 {
self.mask_id
}
#[inline]
pub fn version(&self) -> u8 {
self.version
}
#[inline]
pub fn ecc(&self) -> ECCLevel {
self.ecc
}
#[cfg(debug_assertions)]
pub fn debug_matrix(&self) -> String {
let mut s = format!("size: {}\n", self.matrix.size);
for j in 0..self.matrix.size {
for i in 0..self.matrix.size {
let v = self.matrix.data_at(i, j);
s.push(match v {
Some(true) => '#',
Some(false) => '.',
None => '?',
});
}
s.push('\n');
}
s
}
#[cfg(debug_assertions)]
pub fn get_module(&self, i: usize, j: usize) -> Module {
self.matrix.get(i, j)
}
#[cfg(debug_assertions)]
pub fn size(&self) -> usize {
self.matrix.size
}
#[cfg(debug_assertions)]
pub fn debug_full_matrix(&self) -> String {
use std::collections::HashMap;
let mut counts: HashMap<&str, usize> = HashMap::new();
for j in 0..self.matrix.size {
for i in 0..self.matrix.size {
let name = match self.matrix.get(i, j) {
Module::Data(false) => "Data(false)",
Module::Data(true) => "Data(true)",
Module::Finder(_) => "Finder",
Module::Separator => "Separator",
Module::Alignment(_) => "Alignment",
Module::Timing(_) => "Timing",
Module::FormatInfo(_) => "FormatInfo",
Module::VersionInfo(_) => "VersionInfo",
};
*counts.entry(name).or_insert(0) += 1;
}
}
let mut s = format!("size: {}\n", self.matrix.size);
s.push_str("Module counts:\n");
for (k, v) in &counts {
s.push_str(&format!(" {}: {}\n", k, v));
}
s.push_str("\nMatrix:\n");
for j in 0..self.matrix.size {
for i in 0..self.matrix.size {
let ch = match self.matrix.get(i, j) {
Module::Data(false) => '.',
Module::Data(true) => '#',
Module::Finder(_) => 'F',
Module::Separator => 's',
Module::Alignment(_) => 'A',
Module::Timing(_) => 'T',
Module::FormatInfo(_) => 'f',
Module::VersionInfo(_) => 'v',
};
s.push(ch);
}
s.push('\n');
}
s
}
}
fn pack_bits_msb_first(bits: &EncodeBits) -> Vec<u8> {
let mut out = Vec::with_capacity((bits.len() + 7) / 8);
let mut byte = 0u8;
let mut filled = 0u8;
for bit in bits.iter() {
byte = (byte << 1) | if *bit { 1 } else { 0 };
filled += 1;
if filled == 8 {
out.push(byte);
byte = 0;
filled = 0;
}
}
if filled != 0 {
byte <<= 8 - filled;
out.push(byte);
}
out
}
fn bytes_to_bits_msb_first(bytes: &[u8]) -> Vec<bool> {
let mut out = Vec::with_capacity(bytes.len() * 8);
for &b in bytes {
for i in (0..8).rev() {
out.push(((b >> i) & 1) != 0);
}
}
out
}
fn smallest_version(input: &str, ecc: ECCLevel) -> Option<u8> {
for v in 1u8..=40 {
let cap_bits = total_data_codewords(v, ecc) * 8;
let mode = best_mode(input, v);
let n = estimated_bit_length(input, mode, v);
if n > cap_bits {
continue;
}
let t = (cap_bits - n).min(4);
let after_term = n + t;
let pad_to_byte = (8 - (after_term % 8)) % 8;
if after_term + pad_to_byte <= cap_bits {
return Some(v);
}
}
None
}
#[cfg(test)]
mod mask_score_debug {
use super::*;
#[test]
fn interleaved_matches_qrcode_for_v1_digit_1() {
use qrcode::bits::Bits;
use qrcode::ec;
use qrcode::types::{EcLevel, Version};
let ours = interleaved_codewords("1", ECCLevel::M, 1).expect("ours");
let mut bits = Bits::new(Version::Normal(1));
bits.push_optimal_data(b"1").unwrap();
bits.push_terminator(EcLevel::M).unwrap();
let raw = bits.into_bytes();
let (data, eccv) = ec::construct_codewords(&raw, Version::Normal(1), EcLevel::M).unwrap();
let mut ref_interleaved = Vec::new();
ref_interleaved.extend_from_slice(&data);
ref_interleaved.extend_from_slice(&eccv);
assert_eq!(ours, ref_interleaved, "interleaved codewords differ from qrcode");
}
}