#![deny(missing_docs)]
#![deny(rustdoc::broken_intra_doc_links)]
mod bch;
mod decode;
mod deflate;
mod deflate_encode;
mod encode;
mod galois;
mod mask;
mod matrix;
mod pbm;
mod png;
mod reed_solomon;
mod render;
mod sampler;
mod tables;
pub use bch::EcLevel;
pub use tables::Version;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Error {
DataTooLarge,
InvalidImage(&'static str),
Corrupted(&'static str),
Unsupported(&'static str),
}
impl core::fmt::Display for Error {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Error::DataTooLarge => f.write_str("data too large for QR (max version 40)"),
Error::InvalidImage(msg) => write!(f, "invalid image: {msg}"),
Error::Corrupted(msg) => write!(f, "corrupted QR code: {msg}"),
Error::Unsupported(msg) => write!(f, "unsupported feature: {msg}"),
}
}
}
impl std::error::Error for Error {}
pub struct Encoder<'a> {
data: &'a [u8],
ec_level: EcLevel,
}
impl<'a> Encoder<'a> {
pub fn new(data: &'a [u8]) -> Self {
Self {
data,
ec_level: EcLevel::M,
}
}
pub fn ec_level(mut self, level: EcLevel) -> Self {
self.ec_level = level;
self
}
pub fn build(self) -> Result<Code, Error> {
let (matrix, version, mask) =
encode::encode(self.data, self.ec_level).map_err(|_| Error::DataTooLarge)?;
Ok(Code {
matrix,
version,
ec_level: self.ec_level,
mask,
payload: None,
})
}
}
#[derive(Clone)]
pub struct Code {
matrix: matrix::Matrix,
version: Version,
ec_level: EcLevel,
mask: u8,
payload: Option<Vec<u8>>,
}
impl Code {
pub fn version(&self) -> Version {
self.version
}
pub fn ec_level(&self) -> EcLevel {
self.ec_level
}
pub fn mask(&self) -> u8 {
self.mask
}
pub fn size(&self) -> usize {
self.matrix.size
}
pub fn module(&self, row: usize, col: usize) -> bool {
self.matrix.get(row, col)
}
pub fn payload(&self) -> &[u8] {
self.payload.as_deref().unwrap_or(&[])
}
pub fn render(&self) -> RenderBuilder<'_> {
RenderBuilder {
code: self,
scale: 4,
quiet_zone: 4,
}
}
}
pub struct RenderBuilder<'a> {
code: &'a Code,
scale: usize,
quiet_zone: usize,
}
impl<'a> RenderBuilder<'a> {
pub fn scale(mut self, scale: usize) -> Self {
self.scale = scale.max(1);
self
}
pub fn quiet_zone(mut self, quiet: usize) -> Self {
self.quiet_zone = quiet;
self
}
pub fn build(self) -> Bitmap {
let bm = render::render_to_bitmap(&self.code.matrix, self.scale, self.quiet_zone);
Bitmap { inner: bm }
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Bitmap {
inner: pbm::Bitmap,
}
impl Bitmap {
pub fn width(&self) -> usize {
self.inner.width
}
pub fn height(&self) -> usize {
self.inner.height
}
pub fn pixel(&self, x: usize, y: usize) -> bool {
self.inner.get(x, y)
}
pub fn to_png(&self) -> Vec<u8> {
png::encode_grayscale(&self.inner)
}
pub fn to_pbm(&self) -> String {
pbm::write_p1(&self.inner)
}
}
pub struct Reader;
impl Reader {
pub fn from_png(data: &[u8]) -> Result<Code, Error> {
let bm = png::decode(data).map_err(Error::InvalidImage)?;
Self::from_bitmap_internal(bm)
}
pub fn from_pbm(data: &[u8]) -> Result<Code, Error> {
let bm = pbm::read(data).map_err(Error::InvalidImage)?;
Self::from_bitmap_internal(bm)
}
pub fn from_image_bytes(data: &[u8]) -> Result<Code, Error> {
if data.len() >= 8 && &data[..8] == b"\x89PNG\r\n\x1A\n" {
Self::from_png(data)
} else {
Self::from_pbm(data)
}
}
fn from_bitmap_internal(bm: pbm::Bitmap) -> Result<Code, Error> {
let matrix = sampler::matrix_from_bitmap(&bm).map_err(Error::Corrupted)?;
let version = decode::version_from_size(matrix.size).map_err(Error::Corrupted)?;
let (ec_level, mask) = decode::read_format_info(&matrix).map_err(Error::Corrupted)?;
let payload = decode::decode(&matrix).map_err(Error::Corrupted)?;
Ok(Code {
matrix,
version,
ec_level,
mask,
payload: Some(payload),
})
}
}