#![allow(deprecated)]
use std::collections::HashMap;
use crate::{
common::{BitMatrix, CharacterSet, Result},
qrcode::encoder::ByteMatrix,
BarcodeFormat, EncodeHintType, EncodeHintValue, Exceptions, Writer,
};
use super::encoder::{
high_level_encoder, minimal_encoder, DefaultPlacement, SymbolInfo, SymbolInfoLookup,
SymbolShapeHint,
};
use super::encoder::error_correction;
#[derive(Default)]
pub struct DataMatrixWriter;
impl Writer for DataMatrixWriter {
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::DATA_MATRIX {
return Err(Exceptions::illegal_argument_with(format!(
"Can only encode DATA_MATRIX, but got {format:?}"
)));
}
if width < 0 || height < 0 {
return Err(Exceptions::illegal_argument_with(format!(
"Requested dimensions can't be negative: {width}x{height}"
)));
}
let mut shape = &SymbolShapeHint::FORCE_NONE;
let mut minSize = None;
let mut maxSize = None;
if !hints.is_empty() {
if let Some(EncodeHintValue::DataMatrixShape(rq)) =
hints.get(&EncodeHintType::DATA_MATRIX_SHAPE)
{
shape = rq;
}
let requestedMinSize = hints.get(&EncodeHintType::MIN_SIZE);
if let Some(EncodeHintValue::MinSize(rq)) = requestedMinSize {
minSize = Some(*rq);
}
let requestedMaxSize = hints.get(&EncodeHintType::MAX_SIZE);
if let Some(EncodeHintValue::MinSize(rq)) = requestedMaxSize {
maxSize = Some(*rq);
}
}
let encoded;
let hasCompactionHint = if let Some(EncodeHintValue::DataMatrixCompact(res)) =
hints.get(&EncodeHintType::DATA_MATRIX_COMPACT)
{
*res
} else {
false
};
if hasCompactionHint {
let hasGS1FormatHint = if let Some(EncodeHintValue::Gs1Format(res)) =
hints.get(&EncodeHintType::GS1_FORMAT)
{
*res
} else {
false
};
let mut charset: Option<CharacterSet> = None;
let hasEncodingHint = hints.contains_key(&EncodeHintType::CHARACTER_SET);
if hasEncodingHint {
let Some(EncodeHintValue::CharacterSet(char_set_name)) =
hints.get(&EncodeHintType::CHARACTER_SET)
else {
return Err(Exceptions::illegal_argument_with("charset does not exist"));
};
charset = CharacterSet::get_character_set_by_name(char_set_name);
}
encoded = minimal_encoder::encodeHighLevelWithDetails(
contents,
charset,
if hasGS1FormatHint {
Some(0x1D as char)
} else {
None
},
*shape,
)?;
} else {
let hasForceC40Hint = if let Some(EncodeHintValue::ForceC40(hint)) =
hints.get(&EncodeHintType::FORCE_C40)
{
*hint
} else {
false
};
encoded = high_level_encoder::encodeHighLevelWithDimensionForceC40(
contents,
*shape,
minSize,
maxSize,
hasForceC40Hint,
)?;
}
let symbol_lookup = SymbolInfoLookup::new();
let Some(symbolInfo) = symbol_lookup.lookup_with_codewords_shape_size_fail(
encoded.chars().count() as u32,
*shape,
&minSize,
&maxSize,
true,
)?
else {
return Err(Exceptions::not_found_with("symbol info is bad"));
};
let codewords = error_correction::encodeECC200(&encoded, symbolInfo)?;
let mut placement = DefaultPlacement::new(
codewords,
symbolInfo.getSymbolDataWidth()? as usize,
symbolInfo.getSymbolDataHeight()? as usize,
);
placement.place()?;
Self::encodeLowLevel(&placement, symbolInfo, width as u32, height as u32)
}
}
impl DataMatrixWriter {
fn encodeLowLevel(
placement: &DefaultPlacement,
symbolInfo: &SymbolInfo,
width: u32,
height: u32,
) -> Result<BitMatrix> {
let symbolWidth = symbolInfo.getSymbolDataWidth()?;
let symbolHeight = symbolInfo.getSymbolDataHeight()?;
let mut matrix =
ByteMatrix::new(symbolInfo.getSymbolWidth()?, symbolInfo.getSymbolHeight()?);
let mut matrixY = 0;
for y in 0..symbolHeight {
let mut matrixX;
if (y % symbolInfo.matrixHeight) == 0 {
matrixX = 0;
for x in 0..symbolInfo.getSymbolWidth()? {
matrix.set_bool(matrixX, matrixY, (x % 2) == 0);
matrixX += 1;
}
matrixY += 1;
}
matrixX = 0;
for x in 0..symbolWidth {
if (x % symbolInfo.matrixWidth) == 0 {
matrix.set_bool(matrixX, matrixY, true);
matrixX += 1;
}
matrix.set_bool(matrixX, matrixY, placement.getBit(x as usize, y as usize));
matrixX += 1;
if (x % symbolInfo.matrixWidth) == symbolInfo.matrixWidth - 1 {
matrix.set_bool(matrixX, matrixY, (y % 2) == 0);
matrixX += 1;
}
}
matrixY += 1;
if (y % symbolInfo.matrixHeight) == symbolInfo.matrixHeight - 1 {
matrixX = 0;
for _x in 0..symbolInfo.getSymbolWidth()? {
matrix.set_bool(matrixX, matrixY, true);
matrixX += 1;
}
matrixY += 1;
}
}
Self::convertByteMatrixToBitMatrix(&matrix, width, height)
}
fn convertByteMatrixToBitMatrix(
matrix: &ByteMatrix,
reqWidth: u32,
reqHeight: u32,
) -> Result<BitMatrix> {
let matrixWidth = matrix.getWidth();
let matrixHeight = matrix.getHeight();
let outputWidth = reqWidth.max(matrixWidth);
let outputHeight = reqHeight.max(matrixHeight);
let multiple = (outputWidth / matrixWidth).min(outputHeight / matrixHeight);
let mut leftPadding = (outputWidth - (matrixWidth * multiple)) / 2;
let mut topPadding = (outputHeight - (matrixHeight * multiple)) / 2;
let mut output;
if reqHeight < matrixHeight || reqWidth < matrixWidth {
leftPadding = 0;
topPadding = 0;
output = BitMatrix::new(matrixWidth, matrixHeight)?;
} else {
output = BitMatrix::new(reqWidth, reqHeight)?;
}
output.clear();
let mut inputY = 0;
let mut outputY = topPadding;
while inputY < matrixHeight {
let mut inputX = 0;
let mut outputX = leftPadding;
while inputX < matrixWidth {
if matrix.get(inputX, inputY) == 1 {
output.setRegion(outputX, outputY, multiple, multiple)?;
}
inputX += 1;
outputX += multiple
}
inputY += 1;
outputY += multiple
}
Ok(output)
}
}
#[cfg(test)]
mod tests {
use std::collections::HashMap;
use crate::{
datamatrix::{encoder::SymbolShapeHint, DataMatrixWriter},
BarcodeFormat, EncodeHintType, EncodeHintValue, Writer,
};
#[test]
fn testDataMatrixImageWriter() {
let mut hints = HashMap::new();
hints.insert(
EncodeHintType::DATA_MATRIX_SHAPE,
EncodeHintValue::DataMatrixShape(SymbolShapeHint::FORCE_SQUARE),
);
let bigEnough = 64;
let writer = DataMatrixWriter {};
let matrix = writer
.encode_with_hints(
"Hello Google",
&BarcodeFormat::DATA_MATRIX,
bigEnough,
bigEnough,
&hints,
)
.expect("must encode");
assert!(bigEnough >= matrix.getWidth() as i32);
assert!(bigEnough >= matrix.getHeight() as i32);
}
#[test]
fn testDataMatrixWriter() {
let mut hints = HashMap::new();
hints.insert(
EncodeHintType::DATA_MATRIX_SHAPE,
EncodeHintValue::DataMatrixShape(SymbolShapeHint::FORCE_SQUARE),
);
let bigEnough = 14;
let writer = DataMatrixWriter {};
let matrix = writer
.encode_with_hints(
"Hello Me",
&BarcodeFormat::DATA_MATRIX,
bigEnough,
bigEnough,
&hints,
)
.expect("must encode");
assert_eq!(bigEnough, matrix.getWidth() as i32);
assert_eq!(bigEnough, matrix.getHeight() as i32);
}
#[test]
fn testDataMatrixTooSmall() {
let tooSmall = 8;
let writer = DataMatrixWriter {};
let matrix = writer
.encode_with_hints(
"http://www.google.com/",
&BarcodeFormat::DATA_MATRIX,
tooSmall,
tooSmall,
&HashMap::new(),
)
.expect("must encode");
assert!(tooSmall < matrix.getWidth() as i32);
assert!(tooSmall < matrix.getHeight() as i32);
}
}