pub extern crate qrcodegen;
mod qr_code_error;
use core::{mem::size_of, str::from_utf8};
use std::{
fs::{self, File},
io::Write,
path::Path,
};
#[cfg(feature = "image")]
use image::codecs::png::{CompressionType, FilterType, PngEncoder};
#[cfg(feature = "image")]
use image::{ColorType, ImageBuffer, ImageEncoder, Luma};
pub use qr_code_error::*;
use qrcodegen::QrCode;
pub use qrcodegen::{QrCodeEcc, QrSegment};
#[inline]
fn generate_qrcode<D: AsRef<[u8]>>(data: D, ecc: QrCodeEcc) -> Result<QrCode, QRCodeError> {
match from_utf8(data.as_ref()) {
Ok(text) => generate_qrcode_from_str(text, ecc),
Err(_) => {
let qr = match QrCode::encode_binary(data.as_ref(), ecc) {
Ok(qr) => qr,
Err(_) => return Err(QRCodeError::DataTooLong),
};
Ok(qr)
},
}
}
#[inline]
fn generate_qrcode_from_str<S: AsRef<str>>(text: S, ecc: QrCodeEcc) -> Result<QrCode, QRCodeError> {
let qr = match QrCode::encode_text(text.as_ref(), ecc) {
Ok(qr) => qr,
Err(_) => return Err(QRCodeError::DataTooLong),
};
Ok(qr)
}
#[inline]
fn generate_qrcode_from_segments(
segments: &[QrSegment],
ecc: QrCodeEcc,
) -> Result<QrCode, QRCodeError> {
let qr = match QrCode::encode_segments(segments, ecc) {
Ok(qr) => qr,
Err(_) => return Err(QRCodeError::DataTooLong),
};
Ok(qr)
}
#[inline]
fn to_matrix_inner(qr: QrCode) -> Vec<Vec<bool>> {
let size = qr.size();
let size_u = size as usize;
let mut rows = Vec::with_capacity(size_u);
for y in 0..size {
let mut row = Vec::with_capacity(size_u);
for x in 0..size {
row.push(qr.get_module(x, y));
}
rows.push(row);
}
rows
}
#[inline]
fn to_svg_inner<S: AsRef<str>, W: Write>(
qr: QrCode,
size: usize,
description: Option<S>,
mut writer: W,
) -> Result<(), QRCodeError> {
let margin_size = 1;
let s = qr.size();
let data_length = s as usize;
let data_length_with_margin = data_length + 2 * margin_size;
let point_size = size / data_length_with_margin;
if point_size == 0 {
return Err(QRCodeError::ImageSizeTooSmall);
}
let margin = (size - (point_size * data_length)) / 2;
writer.write_fmt(format_args!("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<svg width=\"{size}\" height=\"{size}\" shape-rendering=\"crispEdges\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\">\n"))?;
match description {
Some(description) => {
let description = description.as_ref();
if !description.is_empty() {
writer.write_all(b"\t<desc>")?;
html_escape::encode_safe_to_writer(description, &mut writer)?;
writer.write_all(b"</desc>\n")?;
}
},
None => {
writer.write_fmt(format_args!(
"\t<desc>{name} {version} by magiclen.org</desc>\n",
name = env!("CARGO_PKG_NAME"),
version = env!("CARGO_PKG_VERSION")
))?;
},
}
writer.write_fmt(format_args!(
"\t<rect width=\"{size}\" height=\"{size}\" fill=\"#FFF\"/>\n\t<path d=\""
))?;
for i in 0..s {
for j in 0..s {
if qr.get_module(j, i) {
let x = j as usize * point_size + margin;
let y = i as usize * point_size + margin;
writer.write_fmt(format_args!("M{x} {y}h{point_size}v{point_size}H{x}V{y}"))?;
}
}
}
writer.write_all(b"\"/>\n</svg>")?;
writer.flush()?;
Ok(())
}
#[inline]
fn to_svg_to_vec_inner<S: AsRef<str>>(
qr: QrCode,
size: usize,
description: Option<S>,
) -> Result<Vec<u8>, QRCodeError> {
let mut svg = Vec::with_capacity(32768);
let margin_size = 1;
let s = qr.size();
let data_length = s as usize;
let data_length_with_margin = data_length + 2 * margin_size;
let point_size = size / data_length_with_margin;
if point_size == 0 {
return Err(QRCodeError::ImageSizeTooSmall);
}
let margin = (size - (point_size * data_length)) / 2;
svg.write_fmt(format_args!("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<svg width=\"{size}\" height=\"{size}\" shape-rendering=\"crispEdges\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\">\n"))?;
match description {
Some(description) => {
let description = description.as_ref();
if !description.is_empty() {
svg.extend_from_slice(b"\t<desc>");
html_escape::encode_safe_to_writer(description, &mut svg)?;
svg.extend_from_slice(b"</desc>\n");
}
},
None => {
svg.write_fmt(format_args!(
"\t<desc>{name} {version} by magiclen.org</desc>\n",
name = env!("CARGO_PKG_NAME"),
version = env!("CARGO_PKG_VERSION")
))?;
},
}
svg.write_fmt(format_args!(
"\t<rect width=\"{size}\" height=\"{size}\" fill=\"#FFF\"/>\n\t<path d=\""
))?;
for i in 0..s {
for j in 0..s {
if qr.get_module(j, i) {
let x = j as usize * point_size + margin;
let y = i as usize * point_size + margin;
svg.write_fmt(format_args!("M{x} {y}h{point_size}v{point_size}H{x}V{y}"))?;
}
}
}
svg.write_all(b"\"/>\n</svg>")?;
Ok(svg)
}
#[inline]
fn to_svg_to_string_inner<S: AsRef<str>>(
qr: QrCode,
size: usize,
description: Option<S>,
) -> Result<String, QRCodeError> {
let svg = to_svg_to_vec_inner(qr, size, description)?;
Ok(unsafe { String::from_utf8_unchecked(svg) })
}
#[inline]
fn to_svg_to_file_inner<S: AsRef<str>, P: AsRef<Path>>(
qr: QrCode,
size: usize,
description: Option<S>,
path: P,
) -> Result<(), QRCodeError> {
let path = path.as_ref();
let file = File::create(path)?;
to_svg_inner(qr, size, description, file).map_err(|err| {
if fs::remove_file(path).is_err() {
}
err
})
}
fn to_image_inner(qr: QrCode, size: usize) -> Result<Vec<u8>, QRCodeError> {
if size >= 2usize.pow((size_of::<usize>() * 4) as u32) {
return Err(QRCodeError::ImageSizeTooLarge);
}
let margin_size = 1;
let s = qr.size();
let data_length = s as usize;
let data_length_with_margin = data_length + 2 * margin_size;
let point_size = size / data_length_with_margin;
if point_size == 0 {
return Err(QRCodeError::ImageSizeTooSmall);
}
let margin = (size - (point_size * data_length)) / 2;
let length = size * size;
let mut img_raw: Vec<u8> = vec![255u8; length];
for i in 0..s {
for j in 0..s {
if qr.get_module(i, j) {
let x = i as usize * point_size + margin;
let y = j as usize * point_size + margin;
for j in y..(y + point_size) {
let offset = j * size;
for i in x..(x + point_size) {
img_raw[offset + i] = 0;
}
}
}
}
}
Ok(img_raw)
}
#[cfg(feature = "image")]
#[inline]
fn to_png_inner<W: Write>(qr: QrCode, size: usize, writer: W) -> Result<(), QRCodeError> {
let img_raw = to_image_inner(qr, size)?;
let encoder = PngEncoder::new_with_quality(writer, CompressionType::Best, FilterType::NoFilter);
Ok(encoder.write_image(&img_raw, size as u32, size as u32, ColorType::L8)?)
}
#[cfg(feature = "image")]
#[inline]
fn to_png_to_vec_inner(qr: QrCode, size: usize) -> Result<Vec<u8>, QRCodeError> {
let mut png = Vec::with_capacity(4096);
to_png_inner(qr, size, &mut png)?;
Ok(png)
}
#[cfg(feature = "image")]
#[inline]
fn to_png_to_file_inner<P: AsRef<Path>>(
qr: QrCode,
size: usize,
path: P,
) -> Result<(), QRCodeError> {
let path = path.as_ref();
let file = File::create(path)?;
to_png_inner(qr, size, file).map_err(|err| {
if fs::remove_file(path).is_err() {
}
err
})
}
#[cfg(feature = "image")]
#[inline]
fn to_image_buffer_inner(
qr: QrCode,
size: usize,
) -> Result<ImageBuffer<Luma<u8>, Vec<u8>>, QRCodeError> {
let img_raw = to_image_inner(qr, size)?;
let img: ImageBuffer<Luma<u8>, Vec<u8>> =
ImageBuffer::from_vec(size as u32, size as u32, img_raw).unwrap();
Ok(img)
}
#[inline]
pub fn to_matrix<D: AsRef<[u8]>>(data: D, ecc: QrCodeEcc) -> Result<Vec<Vec<bool>>, QRCodeError> {
Ok(to_matrix_inner(generate_qrcode(data, ecc)?))
}
#[inline]
pub fn to_matrix_from_str<S: AsRef<str>>(
text: S,
ecc: QrCodeEcc,
) -> Result<Vec<Vec<bool>>, QRCodeError> {
Ok(to_matrix_inner(generate_qrcode_from_str(text, ecc)?))
}
#[inline]
pub fn to_matrix_from_segments(
segments: &[QrSegment],
ecc: QrCodeEcc,
) -> Result<Vec<Vec<bool>>, QRCodeError> {
Ok(to_matrix_inner(generate_qrcode_from_segments(segments, ecc)?))
}
pub fn to_image<D: AsRef<[u8]>>(
data: D,
ecc: QrCodeEcc,
size: usize,
) -> Result<Vec<u8>, QRCodeError> {
to_image_inner(generate_qrcode(data, ecc)?, size)
}
pub fn to_image_from_str<S: AsRef<str>>(
text: S,
ecc: QrCodeEcc,
size: usize,
) -> Result<Vec<u8>, QRCodeError> {
to_image_inner(generate_qrcode_from_str(text, ecc)?, size)
}
pub fn to_image_from_segments(
segments: &[QrSegment],
ecc: QrCodeEcc,
size: usize,
) -> Result<Vec<u8>, QRCodeError> {
to_image_inner(generate_qrcode_from_segments(segments, ecc)?, size)
}
#[inline]
pub fn to_svg_to_string<D: AsRef<[u8]>, DESC: AsRef<str>>(
data: D,
ecc: QrCodeEcc,
size: usize,
description: Option<DESC>,
) -> Result<String, QRCodeError> {
to_svg_to_string_inner(generate_qrcode(data, ecc)?, size, description)
}
#[inline]
pub fn to_svg_to_string_from_str<S: AsRef<str>, DESC: AsRef<str>>(
text: S,
ecc: QrCodeEcc,
size: usize,
description: Option<DESC>,
) -> Result<String, QRCodeError> {
to_svg_to_string_inner(generate_qrcode_from_str(text, ecc)?, size, description)
}
#[inline]
pub fn to_svg_to_string_from_segments<DESC: AsRef<str>>(
segments: &[QrSegment],
ecc: QrCodeEcc,
size: usize,
description: Option<DESC>,
) -> Result<String, QRCodeError> {
to_svg_to_string_inner(generate_qrcode_from_segments(segments, ecc)?, size, description)
}
#[inline]
pub fn to_svg_to_file<D: AsRef<[u8]>, DESC: AsRef<str>, P: AsRef<Path>>(
data: D,
ecc: QrCodeEcc,
size: usize,
description: Option<DESC>,
path: P,
) -> Result<(), QRCodeError> {
to_svg_to_file_inner(generate_qrcode(data, ecc)?, size, description, path)
}
#[inline]
pub fn to_svg_to_file_from_str<S: AsRef<str>, DESC: AsRef<str>, P: AsRef<Path>>(
text: S,
ecc: QrCodeEcc,
size: usize,
description: Option<DESC>,
path: P,
) -> Result<(), QRCodeError> {
to_svg_to_file_inner(generate_qrcode_from_str(text, ecc)?, size, description, path)
}
#[inline]
pub fn to_svg_to_file_from_segments<DESC: AsRef<str>, P: AsRef<Path>>(
segments: &[QrSegment],
ecc: QrCodeEcc,
size: usize,
description: Option<DESC>,
path: P,
) -> Result<(), QRCodeError> {
to_svg_to_file_inner(generate_qrcode_from_segments(segments, ecc)?, size, description, path)
}
#[inline]
pub fn to_svg_to_writer<D: AsRef<[u8]>, DESC: AsRef<str>, W: Write>(
data: D,
ecc: QrCodeEcc,
size: usize,
description: Option<DESC>,
writer: &mut W,
) -> Result<(), QRCodeError> {
to_svg_inner(generate_qrcode(data, ecc)?, size, description, writer)
}
#[inline]
pub fn to_svg_to_writer_from_str<S: AsRef<str>, DESC: AsRef<str>, W: Write>(
text: S,
ecc: QrCodeEcc,
size: usize,
description: Option<DESC>,
writer: &mut W,
) -> Result<(), QRCodeError> {
to_svg_inner(generate_qrcode_from_str(text, ecc)?, size, description, writer)
}
#[inline]
pub fn to_svg_to_writer_from_segments<DESC: AsRef<str>, W: Write>(
segments: &[QrSegment],
ecc: QrCodeEcc,
size: usize,
description: Option<DESC>,
writer: &mut W,
) -> Result<(), QRCodeError> {
to_svg_inner(generate_qrcode_from_segments(segments, ecc)?, size, description, writer)
}
#[cfg(feature = "image")]
#[inline]
pub fn to_png_to_vec<D: AsRef<[u8]>>(
data: D,
ecc: QrCodeEcc,
size: usize,
) -> Result<Vec<u8>, QRCodeError> {
to_png_to_vec_inner(generate_qrcode(data, ecc)?, size)
}
#[cfg(feature = "image")]
#[inline]
pub fn to_png_to_vec_from_str<S: AsRef<str>>(
text: S,
ecc: QrCodeEcc,
size: usize,
) -> Result<Vec<u8>, QRCodeError> {
to_png_to_vec_inner(generate_qrcode_from_str(text, ecc)?, size)
}
#[cfg(feature = "image")]
#[inline]
pub fn to_png_to_vec_from_segments(
segments: &[QrSegment],
ecc: QrCodeEcc,
size: usize,
) -> Result<Vec<u8>, QRCodeError> {
to_png_to_vec_inner(generate_qrcode_from_segments(segments, ecc)?, size)
}
#[cfg(feature = "image")]
#[inline]
pub fn to_png_to_file<D: AsRef<[u8]>, P: AsRef<Path>>(
data: D,
ecc: QrCodeEcc,
size: usize,
path: P,
) -> Result<(), QRCodeError> {
to_png_to_file_inner(generate_qrcode(data, ecc)?, size, path)
}
#[cfg(feature = "image")]
#[inline]
pub fn to_png_to_file_from_str<S: AsRef<str>, P: AsRef<Path>>(
text: S,
ecc: QrCodeEcc,
size: usize,
path: P,
) -> Result<(), QRCodeError> {
to_png_to_file_inner(generate_qrcode_from_str(text, ecc)?, size, path)
}
#[cfg(feature = "image")]
#[inline]
pub fn to_png_to_file_from_segments<P: AsRef<Path>>(
segments: &[QrSegment],
ecc: QrCodeEcc,
size: usize,
path: P,
) -> Result<(), QRCodeError> {
to_png_to_file_inner(generate_qrcode_from_segments(segments, ecc)?, size, path)
}
#[cfg(feature = "image")]
#[inline]
pub fn to_png_to_writer<D: AsRef<[u8]>, W: Write>(
data: D,
ecc: QrCodeEcc,
size: usize,
writer: &mut W,
) -> Result<(), QRCodeError> {
to_png_inner(generate_qrcode(data, ecc)?, size, writer)
}
#[cfg(feature = "image")]
#[inline]
pub fn to_png_to_writer_from_str<S: AsRef<str>, W: Write>(
text: S,
ecc: QrCodeEcc,
size: usize,
writer: &mut W,
) -> Result<(), QRCodeError> {
to_png_inner(generate_qrcode_from_str(text, ecc)?, size, writer)
}
#[cfg(feature = "image")]
#[inline]
pub fn to_png_to_writer_from_segments<W: Write>(
segments: &[QrSegment],
ecc: QrCodeEcc,
size: usize,
writer: &mut W,
) -> Result<(), QRCodeError> {
to_png_inner(generate_qrcode_from_segments(segments, ecc)?, size, writer)
}
#[cfg(feature = "image")]
pub fn to_image_buffer<D: AsRef<[u8]>>(
data: D,
ecc: QrCodeEcc,
size: usize,
) -> Result<ImageBuffer<Luma<u8>, Vec<u8>>, QRCodeError> {
to_image_buffer_inner(generate_qrcode(data, ecc)?, size)
}
#[cfg(feature = "image")]
pub fn to_image_buffer_from_str<S: AsRef<str>>(
text: S,
ecc: QrCodeEcc,
size: usize,
) -> Result<ImageBuffer<Luma<u8>, Vec<u8>>, QRCodeError> {
to_image_buffer_inner(generate_qrcode_from_str(text, ecc)?, size)
}
#[cfg(feature = "image")]
pub fn to_image_buffer_from_segments<S: AsRef<str>>(
segments: &[QrSegment],
ecc: QrCodeEcc,
size: usize,
) -> Result<ImageBuffer<Luma<u8>, Vec<u8>>, QRCodeError> {
to_image_buffer_inner(generate_qrcode_from_segments(segments, ecc)?, size)
}