use std::collections::HashMap;
use crate::{
common::{BitMatrix, Result},
BarcodeFormat, EncodeHintType, EncodeHintValue, Exceptions, Writer,
};
use super::{
decoder::ErrorCorrectionLevel,
encoder::{qrcode_encoder, QRCode},
};
const QUIET_ZONE_SIZE: i32 = 4;
#[derive(Default)]
pub struct QRCodeWriter;
impl Writer for QRCodeWriter {
fn encode(
&self,
contents: &str,
format: &crate::BarcodeFormat,
width: i32,
height: i32,
) -> Result<crate::common::BitMatrix> {
self.encode_with_hints(contents, format, width, height, &HashMap::new())
}
fn encode_with_hints(
&self,
contents: &str,
format: &crate::BarcodeFormat,
width: i32,
height: i32,
hints: &crate::EncodingHintDictionary,
) -> Result<crate::common::BitMatrix> {
if contents.is_empty() {
return Err(Exceptions::illegal_argument_with("found empty contents"));
}
if format != &BarcodeFormat::QR_CODE {
return Err(Exceptions::illegal_argument_with(format!(
"can only encode QR_CODE, but got {format:?}"
)));
}
if width < 0 || height < 0 {
return Err(Exceptions::illegal_argument_with(format!(
"requested dimensions are too small: {width}x{height}"
)));
}
let errorCorrectionLevel = if let Some(EncodeHintValue::ErrorCorrection(ec_level)) =
hints.get(&EncodeHintType::ERROR_CORRECTION)
{
ec_level.parse()?
} else {
ErrorCorrectionLevel::L
};
let quietZone =
if let Some(EncodeHintValue::Margin(margin)) = hints.get(&EncodeHintType::MARGIN) {
margin
.parse::<i32>()
.map_err(|e| Exceptions::parse_with(format!("could not parse {margin}: {e}")))?
} else {
QUIET_ZONE_SIZE
};
let code = qrcode_encoder::encode_with_hints(contents, errorCorrectionLevel, hints)?;
Self::renderRXingResult(&code, width, height, quietZone)
}
}
impl QRCodeWriter {
fn renderRXingResult(
code: &QRCode,
width: i32,
height: i32,
quietZone: i32,
) -> Result<BitMatrix> {
let input = code.getMatrix();
if input.is_none() {
return Err(Exceptions::illegal_state_with("matrix is empty"));
}
let input = input.as_ref().ok_or(Exceptions::ILLEGAL_STATE)?;
let inputWidth = input.getWidth() as i32;
let inputHeight = input.getHeight() as i32;
let qrWidth = inputWidth + (quietZone * 2);
let qrHeight = inputHeight + (quietZone * 2);
let outputWidth = width.max(qrWidth);
let outputHeight = height.max(qrHeight);
let multiple = (outputWidth / qrWidth).min(outputHeight / qrHeight);
let leftPadding = (outputWidth - (inputWidth * multiple)) / 2;
let topPadding = (outputHeight - (inputHeight * multiple)) / 2;
let mut output = BitMatrix::new(outputWidth as u32, outputHeight as u32)?;
let mut inputY = 0;
let mut outputY = topPadding;
while inputY < inputHeight {
let mut inputX = 0;
let mut outputX = leftPadding;
while inputX < inputWidth {
if input.get(inputX as u32, inputY as u32) == 1 {
output.setRegion(
outputX as u32,
outputY as u32,
multiple as u32,
multiple as u32,
)?;
}
inputX += 1;
outputX += multiple;
}
inputY += 1;
outputY += multiple;
}
Ok(output)
}
}