mirror of
https://github.com/starovoid/rxing.git
synced 2026-07-26 04:12:34 +00:00
port writer and tests, but does not pass
This commit is contained in:
@@ -89,12 +89,14 @@ impl Reader for AztecReader {
|
||||
// }
|
||||
// }
|
||||
|
||||
if let Some(rpcb) = hints.get(&DecodeHintType::NEED_RESULT_POINT_CALLBACK) {
|
||||
if let DecodeHintValue::NeedResultPointCallback(cb) = rpcb {
|
||||
for point in points {
|
||||
cb(point);
|
||||
}
|
||||
if let Some(DecodeHintValue::NeedResultPointCallback(cb)) =
|
||||
hints.get(&DecodeHintType::NEED_RESULT_POINT_CALLBACK)
|
||||
{
|
||||
// if let DecodeHintValue::NeedResultPointCallback(cb) = rpcb {
|
||||
for point in points {
|
||||
cb(point);
|
||||
}
|
||||
// }
|
||||
}
|
||||
|
||||
let mut result = RXingResult::new_complex(
|
||||
|
||||
@@ -16,14 +16,18 @@
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::{Reader, Exceptions, common::{BitMatrix, DetectorRXingResult}, RXingResultPoint, DecodeHintType, RXingResult, BarcodeFormat, RXingResultMetadataType, RXingResultMetadataValue};
|
||||
use crate::{
|
||||
common::{BitMatrix, DetectorRXingResult},
|
||||
BarcodeFormat, DecodeHintType, Exceptions, RXingResult, RXingResultMetadataType,
|
||||
RXingResultMetadataValue, RXingResultPoint, Reader,
|
||||
};
|
||||
|
||||
use super::{detector::Detector, decoder::Decoder};
|
||||
use super::{decoder::Decoder, detector::Detector};
|
||||
|
||||
use lazy_static::lazy_static;
|
||||
use lazy_static::lazy_static;
|
||||
|
||||
lazy_static!{
|
||||
static ref decoder : Decoder = Decoder::new();
|
||||
lazy_static! {
|
||||
static ref decoder: Decoder = Decoder::new();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -33,61 +37,79 @@ lazy_static!{
|
||||
*/
|
||||
pub struct DataMatrixReader;
|
||||
|
||||
// private static final RXingResultPoint[] NO_POINTS = new RXingResultPoint[0];
|
||||
// private static final RXingResultPoint[] NO_POINTS = new RXingResultPoint[0];
|
||||
|
||||
// private final Decoder decoder = new Decoder();
|
||||
|
||||
impl Reader for DataMatrixReader {
|
||||
// private final Decoder decoder = new Decoder();
|
||||
|
||||
impl Reader for DataMatrixReader {
|
||||
/**
|
||||
* Locates and decodes a Data Matrix code in an image.
|
||||
*
|
||||
* @return a String representing the content encoded by the Data Matrix code
|
||||
* @throws NotFoundException if a Data Matrix code cannot be found
|
||||
* @throws FormatException if a Data Matrix code cannot be decoded
|
||||
* @throws ChecksumException if error correction fails
|
||||
*/
|
||||
fn decode(&mut self, image: &crate::BinaryBitmap) -> Result<crate::RXingResult, crate::Exceptions> {
|
||||
self.decode_with_hints(image,&HashMap::new())
|
||||
* Locates and decodes a Data Matrix code in an image.
|
||||
*
|
||||
* @return a String representing the content encoded by the Data Matrix code
|
||||
* @throws NotFoundException if a Data Matrix code cannot be found
|
||||
* @throws FormatException if a Data Matrix code cannot be decoded
|
||||
* @throws ChecksumException if error correction fails
|
||||
*/
|
||||
fn decode(
|
||||
&mut self,
|
||||
image: &crate::BinaryBitmap,
|
||||
) -> Result<crate::RXingResult, crate::Exceptions> {
|
||||
self.decode_with_hints(image, &HashMap::new())
|
||||
}
|
||||
|
||||
/**
|
||||
* Locates and decodes a Data Matrix code in an image.
|
||||
*
|
||||
* @return a String representing the content encoded by the Data Matrix code
|
||||
* @throws NotFoundException if a Data Matrix code cannot be found
|
||||
* @throws FormatException if a Data Matrix code cannot be decoded
|
||||
* @throws ChecksumException if error correction fails
|
||||
*/
|
||||
* Locates and decodes a Data Matrix code in an image.
|
||||
*
|
||||
* @return a String representing the content encoded by the Data Matrix code
|
||||
* @throws NotFoundException if a Data Matrix code cannot be found
|
||||
* @throws FormatException if a Data Matrix code cannot be decoded
|
||||
* @throws ChecksumException if error correction fails
|
||||
*/
|
||||
fn decode_with_hints(
|
||||
&mut self,
|
||||
image: &crate::BinaryBitmap,
|
||||
hints: &crate::DecodingHintDictionary,
|
||||
) -> Result<crate::RXingResult, crate::Exceptions> {
|
||||
let decoderRXingResult;
|
||||
let mut points = Vec::new();
|
||||
if hints.contains_key(&DecodeHintType::PURE_BARCODE) {
|
||||
let bits = self.extractPureBits(image.getBlackMatrix())?;
|
||||
decoderRXingResult = decoder.decode(&bits)?;
|
||||
points.clear();
|
||||
} else {
|
||||
let detectorRXingResult = Detector::new(image.getBlackMatrix().clone())?.detect()?;
|
||||
decoderRXingResult = decoder.decode(detectorRXingResult.getBits())?;
|
||||
points = detectorRXingResult.getPoints().clone();
|
||||
}
|
||||
let mut result = RXingResult::new(decoderRXingResult.getText().clone(), decoderRXingResult.getRawBytes().clone(), points.clone(),
|
||||
BarcodeFormat::DATA_MATRIX);
|
||||
let byteSegments = decoderRXingResult.getByteSegments();
|
||||
if !byteSegments.is_empty() {
|
||||
result.putMetadata(RXingResultMetadataType::BYTE_SEGMENTS, RXingResultMetadataValue::ByteSegments(byteSegments.clone()));
|
||||
}
|
||||
let ecLevel = decoderRXingResult.getECLevel();
|
||||
if !ecLevel.is_empty() {
|
||||
result.putMetadata(RXingResultMetadataType::ERROR_CORRECTION_LEVEL, RXingResultMetadataValue::ErrorCorrectionLevel(ecLevel.to_string()));
|
||||
}
|
||||
result.putMetadata(RXingResultMetadataType::SYMBOLOGY_IDENTIFIER, RXingResultMetadataValue::SymbologyIdentifier(format!("]d{}" , decoderRXingResult.getSymbologyModifier())));
|
||||
|
||||
Ok(result)
|
||||
let decoderRXingResult;
|
||||
let mut points = Vec::new();
|
||||
if hints.contains_key(&DecodeHintType::PURE_BARCODE) {
|
||||
let bits = self.extractPureBits(image.getBlackMatrix())?;
|
||||
decoderRXingResult = decoder.decode(&bits)?;
|
||||
points.clear();
|
||||
} else {
|
||||
let detectorRXingResult = Detector::new(image.getBlackMatrix().clone())?.detect()?;
|
||||
decoderRXingResult = decoder.decode(detectorRXingResult.getBits())?;
|
||||
points = detectorRXingResult.getPoints().clone();
|
||||
}
|
||||
let mut result = RXingResult::new(
|
||||
decoderRXingResult.getText().clone(),
|
||||
decoderRXingResult.getRawBytes().clone(),
|
||||
points.clone(),
|
||||
BarcodeFormat::DATA_MATRIX,
|
||||
);
|
||||
let byteSegments = decoderRXingResult.getByteSegments();
|
||||
if !byteSegments.is_empty() {
|
||||
result.putMetadata(
|
||||
RXingResultMetadataType::BYTE_SEGMENTS,
|
||||
RXingResultMetadataValue::ByteSegments(byteSegments.clone()),
|
||||
);
|
||||
}
|
||||
let ecLevel = decoderRXingResult.getECLevel();
|
||||
if !ecLevel.is_empty() {
|
||||
result.putMetadata(
|
||||
RXingResultMetadataType::ERROR_CORRECTION_LEVEL,
|
||||
RXingResultMetadataValue::ErrorCorrectionLevel(ecLevel.to_string()),
|
||||
);
|
||||
}
|
||||
result.putMetadata(
|
||||
RXingResultMetadataType::SYMBOLOGY_IDENTIFIER,
|
||||
RXingResultMetadataValue::SymbologyIdentifier(format!(
|
||||
"]d{}",
|
||||
decoderRXingResult.getSymbologyModifier()
|
||||
)),
|
||||
);
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn reset(&mut self) {
|
||||
@@ -95,79 +117,73 @@ pub struct DataMatrixReader;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
impl DataMatrixReader {
|
||||
|
||||
/**
|
||||
* This method detects a code in a "pure" image -- that is, pure monochrome image
|
||||
* which contains only an unrotated, unskewed, image of a code, with some white border
|
||||
* around it. This is a specialized method that works exceptionally fast in this special
|
||||
* case.
|
||||
*/
|
||||
fn extractPureBits(&self, image:&BitMatrix) -> Result<BitMatrix,Exceptions> {
|
||||
|
||||
let Some(leftTopBlack) = image.getTopLeftOnBit() else {
|
||||
impl DataMatrixReader {
|
||||
/**
|
||||
* This method detects a code in a "pure" image -- that is, pure monochrome image
|
||||
* which contains only an unrotated, unskewed, image of a code, with some white border
|
||||
* around it. This is a specialized method that works exceptionally fast in this special
|
||||
* case.
|
||||
*/
|
||||
fn extractPureBits(&self, image: &BitMatrix) -> Result<BitMatrix, Exceptions> {
|
||||
let Some(leftTopBlack) = image.getTopLeftOnBit() else {
|
||||
return Err(Exceptions::NotFoundException("".to_owned()))
|
||||
};
|
||||
let Some(rightBottomBlack) = image.getBottomRightOnBit()else {
|
||||
let Some(rightBottomBlack) = image.getBottomRightOnBit()else {
|
||||
return Err(Exceptions::NotFoundException("".to_owned()))
|
||||
};
|
||||
|
||||
let moduleSize = Self::moduleSize(&leftTopBlack, image)?;
|
||||
let moduleSize = Self::moduleSize(&leftTopBlack, image)?;
|
||||
|
||||
let mut top = leftTopBlack[1];
|
||||
let bottom = rightBottomBlack[1];
|
||||
let mut left = leftTopBlack[0];
|
||||
let right = rightBottomBlack[0];
|
||||
let mut top = leftTopBlack[1];
|
||||
let bottom = rightBottomBlack[1];
|
||||
let mut left = leftTopBlack[0];
|
||||
let right = rightBottomBlack[0];
|
||||
|
||||
let matrixWidth = (right - left + 1) / moduleSize;
|
||||
let matrixHeight = (bottom - top + 1) / moduleSize;
|
||||
if matrixWidth <= 0 || matrixHeight <= 0 {
|
||||
return Err(Exceptions::NotFoundException("".to_owned()))
|
||||
// throw NotFoundException.getNotFoundInstance();
|
||||
}
|
||||
|
||||
// Push in the "border" by half the module width so that we start
|
||||
// sampling in the middle of the module. Just in case the image is a
|
||||
// little off, this will help recover.
|
||||
let nudge = moduleSize / 2;
|
||||
top += nudge;
|
||||
left += nudge;
|
||||
|
||||
// Now just read off the bits
|
||||
let mut bits = BitMatrix::new(matrixWidth, matrixHeight)?;
|
||||
for y in 0..matrixHeight {
|
||||
// for (int y = 0; y < matrixHeight; y++) {
|
||||
let iOffset = top + y * moduleSize;
|
||||
for x in 0..matrixWidth {
|
||||
// for (int x = 0; x < matrixWidth; x++) {
|
||||
if image.get(left + x * moduleSize, iOffset) {
|
||||
bits.set(x, y);
|
||||
let matrixWidth = (right - left + 1) / moduleSize;
|
||||
let matrixHeight = (bottom - top + 1) / moduleSize;
|
||||
if matrixWidth <= 0 || matrixHeight <= 0 {
|
||||
return Err(Exceptions::NotFoundException("".to_owned()));
|
||||
// throw NotFoundException.getNotFoundInstance();
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok( bits)
|
||||
}
|
||||
|
||||
fn moduleSize( leftTopBlack:&[u32], image:&BitMatrix) -> Result<u32,Exceptions> {
|
||||
let width = image.getWidth();
|
||||
let mut x = leftTopBlack[0];
|
||||
let y = leftTopBlack[1];
|
||||
while x < width && image.get(x, y) {
|
||||
x+=1;
|
||||
}
|
||||
if x == width {
|
||||
return Err(Exceptions::NotFoundException("".to_owned()))
|
||||
// Push in the "border" by half the module width so that we start
|
||||
// sampling in the middle of the module. Just in case the image is a
|
||||
// little off, this will help recover.
|
||||
let nudge = moduleSize / 2;
|
||||
top += nudge;
|
||||
left += nudge;
|
||||
|
||||
// Now just read off the bits
|
||||
let mut bits = BitMatrix::new(matrixWidth, matrixHeight)?;
|
||||
for y in 0..matrixHeight {
|
||||
// for (int y = 0; y < matrixHeight; y++) {
|
||||
let iOffset = top + y * moduleSize;
|
||||
for x in 0..matrixWidth {
|
||||
// for (int x = 0; x < matrixWidth; x++) {
|
||||
if image.get(left + x * moduleSize, iOffset) {
|
||||
bits.set(x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(bits)
|
||||
}
|
||||
|
||||
let moduleSize = x - leftTopBlack[0];
|
||||
if moduleSize == 0 {
|
||||
return Err(Exceptions::NotFoundException("".to_owned()))
|
||||
}
|
||||
|
||||
Ok(moduleSize)
|
||||
}
|
||||
fn moduleSize(leftTopBlack: &[u32], image: &BitMatrix) -> Result<u32, Exceptions> {
|
||||
let width = image.getWidth();
|
||||
let mut x = leftTopBlack[0];
|
||||
let y = leftTopBlack[1];
|
||||
while x < width && image.get(x, y) {
|
||||
x += 1;
|
||||
}
|
||||
if x == width {
|
||||
return Err(Exceptions::NotFoundException("".to_owned()));
|
||||
}
|
||||
|
||||
let moduleSize = x - leftTopBlack[0];
|
||||
if moduleSize == 0 {
|
||||
return Err(Exceptions::NotFoundException("".to_owned()));
|
||||
}
|
||||
|
||||
Ok(moduleSize)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,23 +14,21 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing.datamatrix;
|
||||
use std::collections::HashMap;
|
||||
|
||||
import com.google.zxing.BarcodeFormat;
|
||||
import com.google.zxing.EncodeHintType;
|
||||
import com.google.zxing.Writer;
|
||||
import com.google.zxing.common.BitMatrix;
|
||||
import com.google.zxing.datamatrix.encoder.DefaultPlacement;
|
||||
import com.google.zxing.Dimension;
|
||||
import com.google.zxing.datamatrix.encoder.ErrorCorrection;
|
||||
import com.google.zxing.datamatrix.encoder.HighLevelEncoder;
|
||||
import com.google.zxing.datamatrix.encoder.MinimalEncoder;
|
||||
import com.google.zxing.datamatrix.encoder.SymbolInfo;
|
||||
import com.google.zxing.datamatrix.encoder.SymbolShapeHint;
|
||||
import com.google.zxing.qrcode.encoder.ByteMatrix;
|
||||
use encoding::EncodingRef;
|
||||
|
||||
import java.util.Map;
|
||||
import java.nio.charset.Charset;
|
||||
use crate::{
|
||||
common::BitMatrix, 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;
|
||||
|
||||
/**
|
||||
* This object renders a Data Matrix code as a BitMatrix 2D array of greyscale values.
|
||||
@@ -38,183 +36,319 @@ import java.nio.charset.Charset;
|
||||
* @author dswitkin@google.com (Daniel Switkin)
|
||||
* @author Guillaume Le Biller Added to zxing lib.
|
||||
*/
|
||||
public final class DataMatrixWriter implements Writer {
|
||||
pub struct DataMatrixWriter;
|
||||
|
||||
@Override
|
||||
public BitMatrix encode(String contents, BarcodeFormat format, int width, int height) {
|
||||
return encode(contents, format, width, height, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BitMatrix encode(String contents, BarcodeFormat format, int width, int height, Map<EncodeHintType,?> hints) {
|
||||
|
||||
if (contents.isEmpty()) {
|
||||
throw new IllegalArgumentException("Found empty contents");
|
||||
impl Writer for DataMatrixWriter {
|
||||
fn encode(
|
||||
&self,
|
||||
contents: &str,
|
||||
format: &crate::BarcodeFormat,
|
||||
width: i32,
|
||||
height: i32,
|
||||
) -> Result<crate::common::BitMatrix, crate::Exceptions> {
|
||||
self.encode_with_hints(contents, format, width, height, &HashMap::new())
|
||||
}
|
||||
|
||||
if (format != BarcodeFormat.DATA_MATRIX) {
|
||||
throw new IllegalArgumentException("Can only encode DATA_MATRIX, but got " + format);
|
||||
}
|
||||
|
||||
if (width < 0 || height < 0) {
|
||||
throw new IllegalArgumentException("Requested dimensions can't be negative: " + width + 'x' + height);
|
||||
}
|
||||
|
||||
// Try to get force shape & min / max size
|
||||
SymbolShapeHint shape = SymbolShapeHint.FORCE_NONE;
|
||||
Dimension minSize = null;
|
||||
Dimension maxSize = null;
|
||||
if (hints != null) {
|
||||
SymbolShapeHint requestedShape = (SymbolShapeHint) hints.get(EncodeHintType.DATA_MATRIX_SHAPE);
|
||||
if (requestedShape != null) {
|
||||
shape = requestedShape;
|
||||
}
|
||||
@SuppressWarnings("deprecation")
|
||||
Dimension requestedMinSize = (Dimension) hints.get(EncodeHintType.MIN_SIZE);
|
||||
if (requestedMinSize != null) {
|
||||
minSize = requestedMinSize;
|
||||
}
|
||||
@SuppressWarnings("deprecation")
|
||||
Dimension requestedMaxSize = (Dimension) hints.get(EncodeHintType.MAX_SIZE);
|
||||
if (requestedMaxSize != null) {
|
||||
maxSize = requestedMaxSize;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//1. step: Data encodation
|
||||
String encoded;
|
||||
|
||||
boolean hasCompactionHint = hints != null && hints.containsKey(EncodeHintType.DATA_MATRIX_COMPACT) &&
|
||||
Boolean.parseBoolean(hints.get(EncodeHintType.DATA_MATRIX_COMPACT).toString());
|
||||
if (hasCompactionHint) {
|
||||
|
||||
boolean hasGS1FormatHint = hints.containsKey(EncodeHintType.GS1_FORMAT) &&
|
||||
Boolean.parseBoolean(hints.get(EncodeHintType.GS1_FORMAT).toString());
|
||||
|
||||
Charset charset = null;
|
||||
boolean hasEncodingHint = hints.containsKey(EncodeHintType.CHARACTER_SET);
|
||||
if (hasEncodingHint) {
|
||||
charset = Charset.forName(hints.get(EncodeHintType.CHARACTER_SET).toString());
|
||||
}
|
||||
encoded = MinimalEncoder.encodeHighLevel(contents, charset, hasGS1FormatHint ? 0x1D : -1, shape);
|
||||
} else {
|
||||
boolean hasForceC40Hint = hints != null && hints.containsKey(EncodeHintType.FORCE_C40) &&
|
||||
Boolean.parseBoolean(hints.get(EncodeHintType.FORCE_C40).toString());
|
||||
encoded = HighLevelEncoder.encodeHighLevel(contents, shape, minSize, maxSize, hasForceC40Hint);
|
||||
}
|
||||
|
||||
SymbolInfo symbolInfo = SymbolInfo.lookup(encoded.length(), shape, minSize, maxSize, true);
|
||||
|
||||
//2. step: ECC generation
|
||||
String codewords = ErrorCorrection.encodeECC200(encoded, symbolInfo);
|
||||
|
||||
//3. step: Module placement in Matrix
|
||||
DefaultPlacement placement =
|
||||
new DefaultPlacement(codewords, symbolInfo.getSymbolDataWidth(), symbolInfo.getSymbolDataHeight());
|
||||
placement.place();
|
||||
|
||||
//4. step: low-level encoding
|
||||
return encodeLowLevel(placement, symbolInfo, width, height);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode the given symbol info to a bit matrix.
|
||||
*
|
||||
* @param placement The DataMatrix placement.
|
||||
* @param symbolInfo The symbol info to encode.
|
||||
* @return The bit matrix generated.
|
||||
*/
|
||||
private static BitMatrix encodeLowLevel(DefaultPlacement placement, SymbolInfo symbolInfo, int width, int height) {
|
||||
int symbolWidth = symbolInfo.getSymbolDataWidth();
|
||||
int symbolHeight = symbolInfo.getSymbolDataHeight();
|
||||
|
||||
ByteMatrix matrix = new ByteMatrix(symbolInfo.getSymbolWidth(), symbolInfo.getSymbolHeight());
|
||||
|
||||
int matrixY = 0;
|
||||
|
||||
for (int y = 0; y < symbolHeight; y++) {
|
||||
// Fill the top edge with alternate 0 / 1
|
||||
int matrixX;
|
||||
if ((y % symbolInfo.matrixHeight) == 0) {
|
||||
matrixX = 0;
|
||||
for (int x = 0; x < symbolInfo.getSymbolWidth(); x++) {
|
||||
matrix.set(matrixX, matrixY, (x % 2) == 0);
|
||||
matrixX++;
|
||||
fn encode_with_hints(
|
||||
&self,
|
||||
contents: &str,
|
||||
format: &crate::BarcodeFormat,
|
||||
width: i32,
|
||||
height: i32,
|
||||
hints: &crate::EncodingHintDictionary,
|
||||
) -> Result<crate::common::BitMatrix, crate::Exceptions> {
|
||||
if contents.is_empty() {
|
||||
return Err(Exceptions::IllegalArgumentException(
|
||||
"Found empty contents".to_owned(),
|
||||
));
|
||||
}
|
||||
matrixY++;
|
||||
}
|
||||
matrixX = 0;
|
||||
for (int x = 0; x < symbolWidth; x++) {
|
||||
// Fill the right edge with full 1
|
||||
if ((x % symbolInfo.matrixWidth) == 0) {
|
||||
matrix.set(matrixX, matrixY, true);
|
||||
matrixX++;
|
||||
|
||||
if format != &BarcodeFormat::DATA_MATRIX {
|
||||
return Err(Exceptions::IllegalArgumentException(format!(
|
||||
"Can only encode DATA_MATRIX, but got {:?}",
|
||||
format
|
||||
)));
|
||||
}
|
||||
matrix.set(matrixX, matrixY, placement.getBit(x, y));
|
||||
matrixX++;
|
||||
// Fill the right edge with alternate 0 / 1
|
||||
if ((x % symbolInfo.matrixWidth) == symbolInfo.matrixWidth - 1) {
|
||||
matrix.set(matrixX, matrixY, (y % 2) == 0);
|
||||
matrixX++;
|
||||
|
||||
if width < 0 || height < 0 {
|
||||
return Err(Exceptions::IllegalArgumentException(format!(
|
||||
"Requested dimensions can't be negative: {}x{}",
|
||||
width, height
|
||||
)));
|
||||
}
|
||||
}
|
||||
matrixY++;
|
||||
// Fill the bottom edge with full 1
|
||||
if ((y % symbolInfo.matrixHeight) == symbolInfo.matrixHeight - 1) {
|
||||
matrixX = 0;
|
||||
for (int x = 0; x < symbolInfo.getSymbolWidth(); x++) {
|
||||
matrix.set(matrixX, matrixY, true);
|
||||
matrixX++;
|
||||
|
||||
// Try to get force shape & min / max size
|
||||
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;
|
||||
}
|
||||
// @SuppressWarnings("deprecation")
|
||||
let requestedMinSize = hints.get(&EncodeHintType::MIN_SIZE);
|
||||
if let Some(EncodeHintValue::MinSize(rq)) = requestedMinSize {
|
||||
minSize = Some(*rq);
|
||||
}
|
||||
// @SuppressWarnings("deprecation")
|
||||
let requestedMaxSize = hints.get(&EncodeHintType::MAX_SIZE);
|
||||
if let Some(EncodeHintValue::MinSize(rq)) = requestedMaxSize {
|
||||
maxSize = Some(*rq);
|
||||
}
|
||||
}
|
||||
matrixY++;
|
||||
}
|
||||
|
||||
//1. step: Data encodation
|
||||
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<EncodingRef> = 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::IllegalArgumentException("charset does not exist".to_owned()))
|
||||
};
|
||||
charset = encoding::label::encoding_from_whatwg_label(char_set_name);
|
||||
// charset = Charset.forName(hints.get(EncodeHintType.CHARACTER_SET).toString());
|
||||
}
|
||||
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.len() as u32, *shape, &minSize, &maxSize, true)? else {
|
||||
return Err(Exceptions::NotFoundException("symbol info is bad".to_owned()))
|
||||
};
|
||||
|
||||
//2. step: ECC generation
|
||||
let codewords = error_correction::encodeECC200(&encoded, symbolInfo)?;
|
||||
|
||||
//3. step: Module placement in Matrix
|
||||
let mut placement = DefaultPlacement::new(
|
||||
codewords,
|
||||
symbolInfo.getSymbolDataWidth()? as usize,
|
||||
symbolInfo.getSymbolDataHeight()? as usize,
|
||||
);
|
||||
placement.place();
|
||||
|
||||
//4. step: low-level encoding
|
||||
Self::encodeLowLevel(
|
||||
&placement,
|
||||
symbolInfo,
|
||||
width.try_into().unwrap(),
|
||||
height.try_into().unwrap(),
|
||||
)
|
||||
}
|
||||
|
||||
return convertByteMatrixToBitMatrix(matrix, width, height);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the ByteMatrix to BitMatrix.
|
||||
*
|
||||
* @param reqHeight The requested height of the image (in pixels) with the Datamatrix code
|
||||
* @param reqWidth The requested width of the image (in pixels) with the Datamatrix code
|
||||
* @param matrix The input matrix.
|
||||
* @return The output matrix.
|
||||
*/
|
||||
private static BitMatrix convertByteMatrixToBitMatrix(ByteMatrix matrix, int reqWidth, int reqHeight) {
|
||||
int matrixWidth = matrix.getWidth();
|
||||
int matrixHeight = matrix.getHeight();
|
||||
int outputWidth = Math.max(reqWidth, matrixWidth);
|
||||
int outputHeight = Math.max(reqHeight, matrixHeight);
|
||||
|
||||
int multiple = Math.min(outputWidth / matrixWidth, outputHeight / matrixHeight);
|
||||
|
||||
int leftPadding = (outputWidth - (matrixWidth * multiple)) / 2 ;
|
||||
int topPadding = (outputHeight - (matrixHeight * multiple)) / 2 ;
|
||||
|
||||
BitMatrix output;
|
||||
|
||||
// remove padding if requested width and height are too small
|
||||
if (reqHeight < matrixHeight || reqWidth < matrixWidth) {
|
||||
leftPadding = 0;
|
||||
topPadding = 0;
|
||||
output = new BitMatrix(matrixWidth, matrixHeight);
|
||||
} else {
|
||||
output = new BitMatrix(reqWidth, reqHeight);
|
||||
}
|
||||
|
||||
output.clear();
|
||||
for (int inputY = 0, outputY = topPadding; inputY < matrixHeight; inputY++, outputY += multiple) {
|
||||
// Write the contents of this row of the bytematrix
|
||||
for (int inputX = 0, outputX = leftPadding; inputX < matrixWidth; inputX++, outputX += multiple) {
|
||||
if (matrix.get(inputX, inputY) == 1) {
|
||||
output.setRegion(outputX, outputY, multiple, multiple);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl DataMatrixWriter {
|
||||
/**
|
||||
* Encode the given symbol info to a bit matrix.
|
||||
*
|
||||
* @param placement The DataMatrix placement.
|
||||
* @param symbolInfo The symbol info to encode.
|
||||
* @return The bit matrix generated.
|
||||
*/
|
||||
fn encodeLowLevel(
|
||||
placement: &DefaultPlacement,
|
||||
symbolInfo: &SymbolInfo,
|
||||
width: u32,
|
||||
height: u32,
|
||||
) -> Result<BitMatrix, Exceptions> {
|
||||
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 {
|
||||
// for (int y = 0; y < symbolHeight; y++) {
|
||||
// Fill the top edge with alternate 0 / 1
|
||||
let mut matrixX;
|
||||
if (y % symbolInfo.matrixHeight) == 0 {
|
||||
matrixX = 0;
|
||||
for x in 0..symbolInfo.getSymbolWidth()? {
|
||||
// for (int x = 0; x < symbolInfo.getSymbolWidth(); x++) {
|
||||
matrix.set_bool(matrixX, matrixY, (x % 2) == 0);
|
||||
matrixX += 1;
|
||||
}
|
||||
matrixY += 1;
|
||||
}
|
||||
matrixX = 0;
|
||||
for x in 0..symbolWidth {
|
||||
// for (int x = 0; x < symbolWidth; x++) {
|
||||
// Fill the right edge with full 1
|
||||
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;
|
||||
// Fill the right edge with alternate 0 / 1
|
||||
if (x % symbolInfo.matrixWidth) == symbolInfo.matrixWidth - 1 {
|
||||
matrix.set_bool(matrixX, matrixY, (y % 2) == 0);
|
||||
matrixX += 1;
|
||||
}
|
||||
}
|
||||
matrixY += 1;
|
||||
// Fill the bottom edge with full 1
|
||||
if (y % symbolInfo.matrixHeight) == symbolInfo.matrixHeight - 1 {
|
||||
matrixX = 0;
|
||||
for _x in 0..symbolInfo.getSymbolWidth()? {
|
||||
// for (int x = 0; x < symbolInfo.getSymbolWidth(); x++) {
|
||||
matrix.set_bool(matrixX, matrixY, true);
|
||||
matrixX += 1;
|
||||
}
|
||||
matrixY += 1;
|
||||
}
|
||||
}
|
||||
|
||||
Self::convertByteMatrixToBitMatrix(&matrix, width, height)
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the ByteMatrix to BitMatrix.
|
||||
*
|
||||
* @param reqHeight The requested height of the image (in pixels) with the Datamatrix code
|
||||
* @param reqWidth The requested width of the image (in pixels) with the Datamatrix code
|
||||
* @param matrix The input matrix.
|
||||
* @return The output matrix.
|
||||
*/
|
||||
fn convertByteMatrixToBitMatrix(
|
||||
matrix: &ByteMatrix,
|
||||
reqWidth: u32,
|
||||
reqHeight: u32,
|
||||
) -> Result<BitMatrix, Exceptions> {
|
||||
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;
|
||||
|
||||
// remove padding if requested width and height are too small
|
||||
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 {
|
||||
// for (int inputY = 0, outputY = topPadding; inputY < matrixHeight; inputY++, outputY += multiple) {
|
||||
// Write the contents of this row of the bytematrix
|
||||
let mut inputX = 0;
|
||||
let mut outputX = leftPadding;
|
||||
while inputX < matrixWidth {
|
||||
// for (int inputX = 0, outputX = leftPadding; inputX < matrixWidth; inputX++, outputX += multiple) {
|
||||
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::{EncodeHintType, BarcodeFormat, datamatrix::{encoder::SymbolShapeHint, DataMatrixWriter}, Writer, EncodeHintValue};
|
||||
|
||||
|
||||
#[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() {
|
||||
// The DataMatrix will not fit in this size, so the matrix should come back bigger
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -65,8 +65,8 @@ pub struct SymbolInfo {
|
||||
rectangular: bool,
|
||||
dataCapacity: u32,
|
||||
errorCodewords: u32,
|
||||
matrixWidth: u32,
|
||||
matrixHeight: u32,
|
||||
pub(crate) matrixWidth: u32,
|
||||
pub(crate) matrixHeight: u32,
|
||||
dataRegions: u32,
|
||||
rsBlockData: i32,
|
||||
rsBlockError: u32,
|
||||
|
||||
@@ -3,4 +3,6 @@ pub mod detector;
|
||||
pub mod encoder;
|
||||
|
||||
mod data_matrix_reader;
|
||||
pub use data_matrix_reader::*;
|
||||
mod data_matrix_writer;
|
||||
pub use data_matrix_reader::*;
|
||||
pub use data_matrix_writer::*;
|
||||
|
||||
@@ -196,7 +196,7 @@ pub enum EncodeHintValue {
|
||||
/**
|
||||
* Specifies the matrix shape for Data Matrix (type {@link com.google.zxing.datamatrix.encoder.SymbolShapeHint})
|
||||
*/
|
||||
DataMatrixShape,
|
||||
DataMatrixShape(crate::datamatrix::encoder::SymbolShapeHint),
|
||||
|
||||
/**
|
||||
* Specifies whether to use compact mode for Data Matrix (type {@link Boolean}, or "true" or "false"
|
||||
@@ -212,7 +212,7 @@ pub enum EncodeHintValue {
|
||||
* for the purpose of delimiting AIs.
|
||||
* This option and {@link #FORCE_C40} are mutually exclusive.
|
||||
*/
|
||||
DataMatrixCompact(String),
|
||||
DataMatrixCompact(bool),
|
||||
|
||||
/**
|
||||
* Specifies a minimum barcode size (type {@link Dimension}). Only applicable to Data Matrix now.
|
||||
@@ -221,7 +221,7 @@ pub enum EncodeHintValue {
|
||||
* {@link com.google.zxing.datamatrix.DataMatrixWriter#encode(String, BarcodeFormat, int, int)}
|
||||
*/
|
||||
#[deprecated]
|
||||
MinSize,
|
||||
MinSize(Dimension),
|
||||
|
||||
/**
|
||||
* Specifies a maximum barcode size (type {@link Dimension}). Only applicable to Data Matrix now.
|
||||
@@ -304,7 +304,7 @@ pub enum EncodeHintValue {
|
||||
* Specifies whether the data should be encoded to the GS1 standard (type {@link Boolean}, or "true" or "false"
|
||||
* {@link String } value).
|
||||
*/
|
||||
Gs1Format(String),
|
||||
Gs1Format(bool),
|
||||
|
||||
/**
|
||||
* Forces which encoding will be used. Currently only used for Code-128 code sets (Type {@link String}).
|
||||
@@ -317,7 +317,7 @@ pub enum EncodeHintValue {
|
||||
* Forces C40 encoding for data-matrix (type {@link Boolean}, or "true" or "false") {@link String } value). This
|
||||
* option and {@link #DATA_MATRIX_COMPACT} are mutually exclusive.
|
||||
*/
|
||||
ForceC40(String),
|
||||
ForceC40(bool),
|
||||
|
||||
/**
|
||||
* Specifies whether to use compact mode for Code-128 code (type {@link Boolean}, or "true" or "false"
|
||||
|
||||
@@ -305,10 +305,7 @@ fn testEncodeShiftjisNumeric() {
|
||||
fn testEncodeGS1WithStringTypeHint() {
|
||||
let mut hints = HashMap::new();
|
||||
|
||||
hints.insert(
|
||||
EncodeHintType::GS1_FORMAT,
|
||||
EncodeHintValue::Gs1Format("true".to_owned()),
|
||||
);
|
||||
hints.insert(EncodeHintType::GS1_FORMAT, EncodeHintValue::Gs1Format(true));
|
||||
let qrCode = encoder::encode_with_hints("100001%11171218", ErrorCorrectionLevel::H, &hints)
|
||||
.expect("encode");
|
||||
verifyGS1EncodedData(&qrCode);
|
||||
@@ -318,10 +315,7 @@ fn testEncodeGS1WithStringTypeHint() {
|
||||
fn testEncodeGS1WithBooleanTypeHint() {
|
||||
let mut hints = HashMap::new();
|
||||
|
||||
hints.insert(
|
||||
EncodeHintType::GS1_FORMAT,
|
||||
EncodeHintValue::Gs1Format("true".to_owned()),
|
||||
);
|
||||
hints.insert(EncodeHintType::GS1_FORMAT, EncodeHintValue::Gs1Format(true));
|
||||
let qrCode = encoder::encode_with_hints("100001%11171218", ErrorCorrectionLevel::H, &hints)
|
||||
.expect("encode");
|
||||
verifyGS1EncodedData(&qrCode);
|
||||
@@ -333,7 +327,7 @@ fn testDoesNotEncodeGS1WhenBooleanTypeHintExplicitlyFalse() {
|
||||
|
||||
hints.insert(
|
||||
EncodeHintType::GS1_FORMAT,
|
||||
EncodeHintValue::Gs1Format("false".to_owned()),
|
||||
EncodeHintValue::Gs1Format(false),
|
||||
);
|
||||
|
||||
let qrCode =
|
||||
@@ -347,7 +341,7 @@ fn testDoesNotEncodeGS1WhenStringTypeHintExplicitlyFalse() {
|
||||
|
||||
hints.insert(
|
||||
EncodeHintType::GS1_FORMAT,
|
||||
EncodeHintValue::Gs1Format("false".to_owned()),
|
||||
EncodeHintValue::Gs1Format(false),
|
||||
);
|
||||
let qrCode =
|
||||
encoder::encode_with_hints("ABCDEF", ErrorCorrectionLevel::H, &hints).expect("encode");
|
||||
@@ -362,10 +356,7 @@ fn testGS1ModeHeaderWithECI() {
|
||||
EncodeHintType::CHARACTER_SET,
|
||||
EncodeHintValue::CharacterSet("utf8".to_owned()),
|
||||
);
|
||||
hints.insert(
|
||||
EncodeHintType::GS1_FORMAT,
|
||||
EncodeHintValue::Gs1Format("true".to_owned()),
|
||||
);
|
||||
hints.insert(EncodeHintType::GS1_FORMAT, EncodeHintValue::Gs1Format(true));
|
||||
let qrCode =
|
||||
encoder::encode_with_hints("hello", ErrorCorrectionLevel::H, &hints).expect("encode");
|
||||
let expected = r"<<
|
||||
|
||||
@@ -104,11 +104,7 @@ pub fn encode_with_hints(
|
||||
|
||||
let has_gs1_format_hint = hints.contains_key(&EncodeHintType::GS1_FORMAT)
|
||||
&& if let EncodeHintValue::Gs1Format(v) = hints.get(&EncodeHintType::GS1_FORMAT).unwrap() {
|
||||
if let Ok(vb) = v.parse::<bool>() {
|
||||
vb
|
||||
} else {
|
||||
false
|
||||
}
|
||||
*v
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user