#![cfg_attr(feature = "bench", feature(test))]
#![deny(dead_code)]
#![deny(missing_debug_implementations)]
#![deny(missing_docs)]
#![forbid(unsafe_code)]
#![warn(unreachable_pub)]
#![doc(
html_favicon_url = "https://kura.pro/qrc/favicon.ico",
html_logo_url = "https://kura.pro/qrc/images/logos/qrc.svg",
html_root_url = "https://docs.rs/mini-functions"
)]
#![crate_name = "qrc"]
#![crate_type = "lib"]
extern crate image;
extern crate qrcode;
use flate2::{write::ZlibEncoder, Compression};
use image::{ImageBuffer, Rgba, RgbaImage};
use qrcode::{render::svg, Color, QrCode};
use std::{collections::HashMap, io::Write};
pub mod macros;
#[non_exhaustive]
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct QRCode {
pub data: Vec<u8>,
encoding_format: String,
}
impl QRCode {
pub fn new(data: Vec<u8>) -> Self {
QRCode {
data,
encoding_format: "utf-8".to_string(),
}
}
pub fn from_string(data: String) -> Self {
QRCode {
data: data.into_bytes(),
encoding_format: "utf-8".to_string(),
}
}
pub fn from_bytes(data: Vec<u8>) -> Self {
QRCode {
data,
encoding_format: "utf-8".to_string(),
}
}
pub fn to_qrcode(&self) -> QrCode {
QrCode::new(&self.data).unwrap()
}
pub fn to_png(&self, width: u32) -> ImageBuffer<Rgba<u8>, Vec<u8>> {
let qrcode = self.to_qrcode();
let height = width;
let mut img = ImageBuffer::new(width, height);
for (x, y, pixel) in img.enumerate_pixels_mut() {
let x_index = (x as f32 / width as f32) * qrcode.width() as f32;
let y_index = (y as f32 / height as f32) * qrcode.width() as f32;
*pixel = match qrcode[(x_index as usize, y_index as usize)] {
qrcode::Color::Dark => Rgba([0, 0, 0, 0]),
qrcode::Color::Light => Rgba([255, 255, 255, 255]),
};
}
img
}
pub fn to_jpg(&self, width: u32) -> ImageBuffer<Rgba<u8>, Vec<u8>> {
let qrcode = self.to_qrcode();
let height = width;
let mut img = ImageBuffer::new(width, height);
for (x, y, pixel) in img.enumerate_pixels_mut() {
let x_index = (x as f32 / width as f32) * qrcode.width() as f32;
let y_index = (y as f32 / height as f32) * qrcode.width() as f32;
*pixel = match qrcode[(x_index as usize, y_index as usize)] {
qrcode::Color::Dark => Rgba([0, 0, 0, 0]),
qrcode::Color::Light => Rgba([255, 255, 255, 255]),
};
}
img
}
pub fn to_gif(&self, width: u32) -> ImageBuffer<Rgba<u8>, Vec<u8>> {
let qrcode = self.to_qrcode();
let height = width;
let mut img = ImageBuffer::new(width, height);
for (x, y, pixel) in img.enumerate_pixels_mut() {
let x_index = (x as f32 / width as f32) * qrcode.width() as f32;
let y_index = (y as f32 / height as f32) * qrcode.width() as f32;
*pixel = match qrcode[(x_index as usize, y_index as usize)] {
qrcode::Color::Dark => Rgba([0, 0, 0, 0]),
qrcode::Color::Light => Rgba([255, 255, 255, 255]),
};
}
img
}
pub fn to_svg(&self, width: u32) -> String {
let qrcode = self.to_qrcode();
let svg_string = qrcode
.render::<svg::Color>()
.min_dimensions(width, width)
.dark_color(svg::Color("#000000"))
.light_color(svg::Color("#FFFFFF"))
.build();
svg_string
}
pub fn colorize(&self, color: Rgba<u8>) -> RgbaImage {
let qrcode = self.to_qrcode();
let mut img: RgbaImage = ImageBuffer::new(qrcode.width() as u32, qrcode.width() as u32);
for (x, y, pixel) in img.enumerate_pixels_mut() {
let c = if qrcode[(x as usize, y as usize)] == qrcode::Color::Dark {
color
} else {
Rgba([255, 255, 255, 255])
};
*pixel = c;
}
img
}
pub fn resize(&self, width: u32, height: u32) -> RgbaImage {
let qrcode = self.to_qrcode();
let mut img: RgbaImage = ImageBuffer::new(width, height);
for y in 0..height {
for x in 0..width {
let x_index = (x as f32 / width as f32) * qrcode.width() as f32;
let y_index = (y as f32 / height as f32) * qrcode.width() as f32;
let c = match qrcode[(x_index as usize, y_index as usize)] {
qrcode::Color::Dark => Rgba([0, 0, 0, 0]),
qrcode::Color::Light => Rgba([255, 255, 255, 255]),
};
img.put_pixel(x, y, c);
}
}
img
}
pub fn add_image_watermark(img: &mut RgbaImage, watermark: &RgbaImage) {
let (width, height) = img.dimensions();
let (watermark_width, watermark_height) = watermark.dimensions();
let x = width - watermark_width;
let y = height - watermark_height;
for (dx, dy, watermark_pixel) in watermark.enumerate_pixels() {
let x = x + dx;
let y = y + dy;
let qr_pixel = img.get_pixel(x, y);
let alpha = (watermark_pixel[3] as f32) / 255.0;
let new_r = (1.0 - alpha) * (qr_pixel[0] as f32) + alpha * (watermark_pixel[0] as f32);
let new_g = (1.0 - alpha) * (qr_pixel[1] as f32) + alpha * (watermark_pixel[1] as f32);
let new_b = (1.0 - alpha) * (qr_pixel[2] as f32) + alpha * (watermark_pixel[2] as f32);
let new_a = (qr_pixel[3] as f32) + alpha * (255.0 - qr_pixel[3] as f32);
let new_pixel = [new_r as u8, new_g as u8, new_b as u8, new_a as u8];
img.put_pixel(x, y, image::Rgba(new_pixel));
}
}
pub fn create_multilanguage(data_map: HashMap<String, String>) -> Self {
let user_language = "en";
let mut selected_data = "";
if let Some(language_data) = data_map.get(user_language) {
selected_data = language_data;
}
QRCode::from_string(selected_data.to_string())
}
pub fn create_dynamic(initial_data: &str) -> Self {
let dynamic_data_format = "url";
let dynamic_url = match dynamic_data_format {
"url" => {
format!(
"https://your-api-endpoint.com/update?qrcode={}",
initial_data
)
}
_ => return QRCode::from_string(initial_data.to_string()), };
QRCode::from_string(dynamic_url)
}
pub fn combine_qr_codes(codes: Vec<QRCode>) -> Result<Self, &'static str> {
if codes.is_empty() {
return Err("No QR codes to combine");
}
let total_width = codes
.iter()
.map(|code| code.to_qrcode().width() as u32)
.sum();
let mut combined_qrcode = QRCode::from_bytes(Vec::new());
combined_qrcode.resize(total_width, total_width);
let mut combined_image = combined_qrcode.to_png(total_width);
let mut x_offset = 0;
for code in codes {
let qrcode = code.to_qrcode();
let width = qrcode.width() as u32;
let height = qrcode.width() as u32;
for x in 0..width {
for y in 0..height {
let pixel = qrcode[(x as usize, y as usize)];
let combined_x = x + x_offset;
let combined_y = y;
match pixel {
qrcode::Color::Dark => {
combined_image.put_pixel(combined_x, combined_y, Rgba([0, 0, 0, 0]));
}
qrcode::Color::Light => {
combined_image.put_pixel(
combined_x,
combined_y,
Rgba([255, 255, 255, 255]),
);
}
}
}
}
x_offset += width;
}
combined_qrcode.data = combined_image.into_raw();
Ok(combined_qrcode)
}
pub fn compress_data(data: &str) -> Vec<u8> {
let input_bytes = data.as_bytes();
let mut compressed_data = Vec::new();
let mut encoder = ZlibEncoder::new(&mut compressed_data, Compression::default());
if encoder.write_all(input_bytes).is_err() {
return input_bytes.to_vec();
}
if encoder.finish().is_err() {
return input_bytes.to_vec();
}
compressed_data
}
pub fn batch_generate_qr_codes(data: Vec<String>) -> Vec<QRCode> {
let mut qr_codes = Vec::new();
for item in data {
let qr_code = QRCode::from_string(item);
qr_codes.push(qr_code);
}
qr_codes
}
pub fn overlay_image(&self, overlay: &RgbaImage) -> RgbaImage {
let qrcode = self.to_qrcode();
let mut combined_image = ImageBuffer::new(qrcode.width() as u32, qrcode.width() as u32);
for x in 0..qrcode.width() {
for y in 0..qrcode.width() {
let pixel = qrcode[(x, y)];
let combined_x = x as u32; let combined_y = y as u32;
match pixel {
Color::Dark => {
combined_image.put_pixel(combined_x, combined_y, Rgba([0, 0, 0, 0]));
}
Color::Light => {
combined_image.put_pixel(
combined_x,
combined_y,
Rgba([255, 255, 255, 255]),
);
}
}
}
}
for x in 0..overlay.width() {
for y in 0..overlay.height() {
let pixel = overlay.get_pixel(x, y);
let combined_x = x; let combined_y = y;
combined_image.put_pixel(combined_x, combined_y, *pixel);
}
}
combined_image
}
pub fn set_encoding_format(&self, format: &str) -> Result<Self, &'static str> {
if format != "utf-8" {
return Err("Unsupported encoding format");
}
Ok(Self {
data: self.data.clone(),
encoding_format: format.to_string(), })
}
pub fn get_encoding_format(&self) -> &str {
&self.encoding_format
}
}