diff --git a/src/datamatrix/encoder/DataMatrixSymbolInfo144.java b/src/datamatrix/encoder/DataMatrixSymbolInfo144.java deleted file mode 100644 index 3146353..0000000 --- a/src/datamatrix/encoder/DataMatrixSymbolInfo144.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2006 Jeremias Maerki - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing.datamatrix.encoder; - -final class DataMatrixSymbolInfo144 extends SymbolInfo { - - DataMatrixSymbolInfo144() { - super(false, 1558, 620, 22, 22, 36, -1, 62); - } - - @Override - public int getInterleavedBlockCount() { - return 10; - } - - @Override - public int getDataLengthForInterleavedBlock(int index) { - return (index <= 8) ? 156 : 155; - } - -} diff --git a/src/datamatrix/encoder/EncoderContext.java b/src/datamatrix/encoder/EncoderContext.java deleted file mode 100644 index cc0b0cb..0000000 --- a/src/datamatrix/encoder/EncoderContext.java +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright 2006-2007 Jeremias Maerki. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing.datamatrix.encoder; - -import com.google.zxing.Dimension; - -import java.nio.charset.StandardCharsets; - -final class EncoderContext { - - private final String msg; - private SymbolShapeHint shape; - private Dimension minSize; - private Dimension maxSize; - private final StringBuilder codewords; - int pos; - private int newEncoding; - private SymbolInfo symbolInfo; - private int skipAtEnd; - - EncoderContext(String msg) { - //From this point on Strings are not Unicode anymore! - byte[] msgBinary = msg.getBytes(StandardCharsets.ISO_8859_1); - StringBuilder sb = new StringBuilder(msgBinary.length); - for (int i = 0, c = msgBinary.length; i < c; i++) { - char ch = (char) (msgBinary[i] & 0xff); - if (ch == '?' && msg.charAt(i) != '?') { - throw new IllegalArgumentException("Message contains characters outside ISO-8859-1 encoding."); - } - sb.append(ch); - } - this.msg = sb.toString(); //Not Unicode here! - shape = SymbolShapeHint.FORCE_NONE; - this.codewords = new StringBuilder(msg.length()); - newEncoding = -1; - } - - public void setSymbolShape(SymbolShapeHint shape) { - this.shape = shape; - } - - public void setSizeConstraints(Dimension minSize, Dimension maxSize) { - this.minSize = minSize; - this.maxSize = maxSize; - } - - public String getMessage() { - return this.msg; - } - - public void setSkipAtEnd(int count) { - this.skipAtEnd = count; - } - - public char getCurrentChar() { - return msg.charAt(pos); - } - - public char getCurrent() { - return msg.charAt(pos); - } - - public StringBuilder getCodewords() { - return codewords; - } - - public void writeCodewords(String codewords) { - this.codewords.append(codewords); - } - - public void writeCodeword(char codeword) { - this.codewords.append(codeword); - } - - public int getCodewordCount() { - return this.codewords.length(); - } - - public int getNewEncoding() { - return newEncoding; - } - - public void signalEncoderChange(int encoding) { - this.newEncoding = encoding; - } - - public void resetEncoderSignal() { - this.newEncoding = -1; - } - - public boolean hasMoreCharacters() { - return pos < getTotalMessageCharCount(); - } - - private int getTotalMessageCharCount() { - return msg.length() - skipAtEnd; - } - - public int getRemainingCharacters() { - return getTotalMessageCharCount() - pos; - } - - public SymbolInfo getSymbolInfo() { - return symbolInfo; - } - - public void updateSymbolInfo() { - updateSymbolInfo(getCodewordCount()); - } - - public void updateSymbolInfo(int len) { - if (this.symbolInfo == null || len > this.symbolInfo.getDataCapacity()) { - this.symbolInfo = SymbolInfo.lookup(len, shape, minSize, maxSize, true); - } - } - - public void resetSymbolInfo() { - this.symbolInfo = null; - } -} diff --git a/src/datamatrix/encoder/SymbolInfo.java b/src/datamatrix/encoder/SymbolInfo.java deleted file mode 100644 index d57ffd0..0000000 --- a/src/datamatrix/encoder/SymbolInfo.java +++ /dev/null @@ -1,236 +0,0 @@ -/* - * Copyright 2006 Jeremias Maerki - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing.datamatrix.encoder; - -import com.google.zxing.Dimension; - -/** - * Symbol info table for DataMatrix. - * - * @version $Id$ - */ -public class SymbolInfo { - - static final SymbolInfo[] PROD_SYMBOLS = { - new SymbolInfo(false, 3, 5, 8, 8, 1), - new SymbolInfo(false, 5, 7, 10, 10, 1), - /*rect*/new SymbolInfo(true, 5, 7, 16, 6, 1), - new SymbolInfo(false, 8, 10, 12, 12, 1), - /*rect*/new SymbolInfo(true, 10, 11, 14, 6, 2), - new SymbolInfo(false, 12, 12, 14, 14, 1), - /*rect*/new SymbolInfo(true, 16, 14, 24, 10, 1), - - new SymbolInfo(false, 18, 14, 16, 16, 1), - new SymbolInfo(false, 22, 18, 18, 18, 1), - /*rect*/new SymbolInfo(true, 22, 18, 16, 10, 2), - new SymbolInfo(false, 30, 20, 20, 20, 1), - /*rect*/new SymbolInfo(true, 32, 24, 16, 14, 2), - new SymbolInfo(false, 36, 24, 22, 22, 1), - new SymbolInfo(false, 44, 28, 24, 24, 1), - /*rect*/new SymbolInfo(true, 49, 28, 22, 14, 2), - - new SymbolInfo(false, 62, 36, 14, 14, 4), - new SymbolInfo(false, 86, 42, 16, 16, 4), - new SymbolInfo(false, 114, 48, 18, 18, 4), - new SymbolInfo(false, 144, 56, 20, 20, 4), - new SymbolInfo(false, 174, 68, 22, 22, 4), - - new SymbolInfo(false, 204, 84, 24, 24, 4, 102, 42), - new SymbolInfo(false, 280, 112, 14, 14, 16, 140, 56), - new SymbolInfo(false, 368, 144, 16, 16, 16, 92, 36), - new SymbolInfo(false, 456, 192, 18, 18, 16, 114, 48), - new SymbolInfo(false, 576, 224, 20, 20, 16, 144, 56), - new SymbolInfo(false, 696, 272, 22, 22, 16, 174, 68), - new SymbolInfo(false, 816, 336, 24, 24, 16, 136, 56), - new SymbolInfo(false, 1050, 408, 18, 18, 36, 175, 68), - new SymbolInfo(false, 1304, 496, 20, 20, 36, 163, 62), - new DataMatrixSymbolInfo144(), - }; - - private static SymbolInfo[] symbols = PROD_SYMBOLS; - - private final boolean rectangular; - private final int dataCapacity; - private final int errorCodewords; - public final int matrixWidth; - public final int matrixHeight; - private final int dataRegions; - private final int rsBlockData; - private final int rsBlockError; - - /** - * Overrides the symbol info set used by this class. Used for testing purposes. - * - * @param override the symbol info set to use - */ - public static void overrideSymbolSet(SymbolInfo[] override) { - symbols = override; - } - - public SymbolInfo(boolean rectangular, int dataCapacity, int errorCodewords, - int matrixWidth, int matrixHeight, int dataRegions) { - this(rectangular, dataCapacity, errorCodewords, matrixWidth, matrixHeight, dataRegions, - dataCapacity, errorCodewords); - } - - SymbolInfo(boolean rectangular, int dataCapacity, int errorCodewords, - int matrixWidth, int matrixHeight, int dataRegions, - int rsBlockData, int rsBlockError) { - this.rectangular = rectangular; - this.dataCapacity = dataCapacity; - this.errorCodewords = errorCodewords; - this.matrixWidth = matrixWidth; - this.matrixHeight = matrixHeight; - this.dataRegions = dataRegions; - this.rsBlockData = rsBlockData; - this.rsBlockError = rsBlockError; - } - - public static SymbolInfo lookup(int dataCodewords) { - return lookup(dataCodewords, SymbolShapeHint.FORCE_NONE, true); - } - - public static SymbolInfo lookup(int dataCodewords, SymbolShapeHint shape) { - return lookup(dataCodewords, shape, true); - } - - public static SymbolInfo lookup(int dataCodewords, boolean allowRectangular, boolean fail) { - SymbolShapeHint shape = allowRectangular - ? SymbolShapeHint.FORCE_NONE : SymbolShapeHint.FORCE_SQUARE; - return lookup(dataCodewords, shape, fail); - } - - private static SymbolInfo lookup(int dataCodewords, SymbolShapeHint shape, boolean fail) { - return lookup(dataCodewords, shape, null, null, fail); - } - - public static SymbolInfo lookup(int dataCodewords, - SymbolShapeHint shape, - Dimension minSize, - Dimension maxSize, - boolean fail) { - for (SymbolInfo symbol : symbols) { - if (shape == SymbolShapeHint.FORCE_SQUARE && symbol.rectangular) { - continue; - } - if (shape == SymbolShapeHint.FORCE_RECTANGLE && !symbol.rectangular) { - continue; - } - if (minSize != null - && (symbol.getSymbolWidth() < minSize.getWidth() - || symbol.getSymbolHeight() < minSize.getHeight())) { - continue; - } - if (maxSize != null - && (symbol.getSymbolWidth() > maxSize.getWidth() - || symbol.getSymbolHeight() > maxSize.getHeight())) { - continue; - } - if (dataCodewords <= symbol.dataCapacity) { - return symbol; - } - } - if (fail) { - throw new IllegalArgumentException( - "Can't find a symbol arrangement that matches the message. Data codewords: " - + dataCodewords); - } - return null; - } - - private int getHorizontalDataRegions() { - switch (dataRegions) { - case 1: - return 1; - case 2: - case 4: - return 2; - case 16: - return 4; - case 36: - return 6; - default: - throw new IllegalStateException("Cannot handle this number of data regions"); - } - } - - private int getVerticalDataRegions() { - switch (dataRegions) { - case 1: - case 2: - return 1; - case 4: - return 2; - case 16: - return 4; - case 36: - return 6; - default: - throw new IllegalStateException("Cannot handle this number of data regions"); - } - } - - public final int getSymbolDataWidth() { - return getHorizontalDataRegions() * matrixWidth; - } - - public final int getSymbolDataHeight() { - return getVerticalDataRegions() * matrixHeight; - } - - public final int getSymbolWidth() { - return getSymbolDataWidth() + (getHorizontalDataRegions() * 2); - } - - public final int getSymbolHeight() { - return getSymbolDataHeight() + (getVerticalDataRegions() * 2); - } - - public int getCodewordCount() { - return dataCapacity + errorCodewords; - } - - public int getInterleavedBlockCount() { - return dataCapacity / rsBlockData; - } - - public final int getDataCapacity() { - return dataCapacity; - } - - public final int getErrorCodewords() { - return errorCodewords; - } - - public int getDataLengthForInterleavedBlock(int index) { - return rsBlockData; - } - - public final int getErrorLengthForInterleavedBlock(int index) { - return rsBlockError; - } - - @Override - public final String toString() { - return (rectangular ? "Rectangular Symbol:" : "Square Symbol:") + - " data region " + matrixWidth + 'x' + matrixHeight + - ", symbol size " + getSymbolWidth() + 'x' + getSymbolHeight() + - ", symbol data size " + getSymbolDataWidth() + 'x' + getSymbolDataHeight() + - ", codewords " + dataCapacity + '+' + errorCodewords; - } - -} diff --git a/src/datamatrix/encoder/SymbolInfoTestCase.java b/src/datamatrix/encoder/SymbolInfoTestCase.java deleted file mode 100644 index 3a6c8bf..0000000 --- a/src/datamatrix/encoder/SymbolInfoTestCase.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright 2006 Jeremias Maerki - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing.datamatrix.encoder; - -import com.google.zxing.Dimension; -import org.junit.Assert; -import org.junit.Test; - -/** - * Tests the SymbolInfo class. - */ -public final class SymbolInfoTestCase extends Assert { - - @Test - public void testSymbolInfo() { - SymbolInfo info = SymbolInfo.lookup(3); - assertEquals(5, info.getErrorCodewords()); - assertEquals(8, info.matrixWidth); - assertEquals(8, info.matrixHeight); - assertEquals(10, info.getSymbolWidth()); - assertEquals(10, info.getSymbolHeight()); - - info = SymbolInfo.lookup(3, SymbolShapeHint.FORCE_RECTANGLE); - assertEquals(7, info.getErrorCodewords()); - assertEquals(16, info.matrixWidth); - assertEquals(6, info.matrixHeight); - assertEquals(18, info.getSymbolWidth()); - assertEquals(8, info.getSymbolHeight()); - - info = SymbolInfo.lookup(9); - assertEquals(11, info.getErrorCodewords()); - assertEquals(14, info.matrixWidth); - assertEquals(6, info.matrixHeight); - assertEquals(32, info.getSymbolWidth()); - assertEquals(8, info.getSymbolHeight()); - - info = SymbolInfo.lookup(9, SymbolShapeHint.FORCE_SQUARE); - assertEquals(12, info.getErrorCodewords()); - assertEquals(14, info.matrixWidth); - assertEquals(14, info.matrixHeight); - assertEquals(16, info.getSymbolWidth()); - assertEquals(16, info.getSymbolHeight()); - - try { - SymbolInfo.lookup(1559); - fail("There's no rectangular symbol for more than 1558 data codewords"); - } catch (IllegalArgumentException iae) { - //expected - } - try { - SymbolInfo.lookup(50, SymbolShapeHint.FORCE_RECTANGLE); - fail("There's no rectangular symbol for 50 data codewords"); - } catch (IllegalArgumentException iae) { - //expected - } - - info = SymbolInfo.lookup(35); - assertEquals(24, info.getSymbolWidth()); - assertEquals(24, info.getSymbolHeight()); - - Dimension fixedSize = new Dimension(26, 26); - info = SymbolInfo.lookup(35, - SymbolShapeHint.FORCE_NONE, fixedSize, fixedSize, false); - assertNotNull(info); - assertEquals(26, info.getSymbolWidth()); - assertEquals(26, info.getSymbolHeight()); - - info = SymbolInfo.lookup(45, - SymbolShapeHint.FORCE_NONE, fixedSize, fixedSize, false); - assertNull(info); - - Dimension minSize = fixedSize; - Dimension maxSize = new Dimension(32, 32); - - info = SymbolInfo.lookup(35, - SymbolShapeHint.FORCE_NONE, minSize, maxSize, false); - assertNotNull(info); - assertEquals(26, info.getSymbolWidth()); - assertEquals(26, info.getSymbolHeight()); - - info = SymbolInfo.lookup(40, - SymbolShapeHint.FORCE_NONE, minSize, maxSize, false); - assertNotNull(info); - assertEquals(26, info.getSymbolWidth()); - assertEquals(26, info.getSymbolHeight()); - - info = SymbolInfo.lookup(45, - SymbolShapeHint.FORCE_NONE, minSize, maxSize, false); - assertNotNull(info); - assertEquals(32, info.getSymbolWidth()); - assertEquals(32, info.getSymbolHeight()); - - info = SymbolInfo.lookup(63, - SymbolShapeHint.FORCE_NONE, minSize, maxSize, false); - assertNull(info); - } - -} diff --git a/src/datamatrix/encoder/Encoder.java b/src/datamatrix/encoder/encoder.rs similarity index 82% rename from src/datamatrix/encoder/Encoder.java rename to src/datamatrix/encoder/encoder.rs index 7a91e1e..7270014 100644 --- a/src/datamatrix/encoder/Encoder.java +++ b/src/datamatrix/encoder/encoder.rs @@ -14,12 +14,12 @@ * limitations under the License. */ -package com.google.zxing.datamatrix.encoder; +use super::EncoderContext; -interface Encoder { +pub trait Encoder { - int getEncodingMode(); + fn getEncodingMode(&self) -> i32; - void encode(EncoderContext context); + fn encode(&self, context:&mut EncoderContext); } diff --git a/src/datamatrix/encoder/encoder_context.rs b/src/datamatrix/encoder/encoder_context.rs new file mode 100644 index 0000000..dae22c1 --- /dev/null +++ b/src/datamatrix/encoder/encoder_context.rs @@ -0,0 +1,171 @@ +/* + * Copyright 2006-2007 Jeremias Maerki. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use std::rc::Rc; + +use crate::{Dimension, Exceptions}; + +use super::{SymbolInfo, SymbolShapeHint, SymbolInfoLookup}; +use encoding::{self, EncodingRef}; +use unicode_segmentation::UnicodeSegmentation; + +const ISO_8859_1_ENCODER: EncodingRef = encoding::all::ISO_8859_1; + +pub struct EncoderContext<'a> { + symbol_lookup:Rc>, + msg: String, + shape: SymbolShapeHint, + minSize: Option, + maxSize: Option, + codewords: String, + pos: u32, + newEncoding: i32, + symbolInfo: Option<&'a SymbolInfo>, + skipAtEnd: u32, +} + +impl EncoderContext<'_> { + pub fn new(msg: &str) -> Result { + //From this point on Strings are not Unicode anymore! + // let msgBinary = ISO_8859_1_ENCODER.encode(msg, encoding::EncoderTrap::Strict).expect("encode to bytes");//msg.getBytes(StandardCharsets.ISO_8859_1); + // let sb = String::with_capacity(msgBinary.len()); + // for (int i = 0, c = msgBinary.length; i < c; i++) { + // char ch = (char) (msgBinary[i] & 0xff); + // if (ch == '?' && msg.charAt(i) != '?') { + // throw new IllegalArgumentException("Message contains characters outside ISO-8859-1 encoding."); + // } + // sb.append(ch); + // } + let sb = if let Ok(encoded_bytes) = + ISO_8859_1_ENCODER.encode(msg, encoding::EncoderTrap::Strict) + { + ISO_8859_1_ENCODER + .decode(&encoded_bytes, encoding::DecoderTrap::Strict) + .expect("round trip decode should always work") + } else { + return Err(Exceptions::IllegalArgumentException( + "Message contains characters outside ISO-8859-1 encoding.".to_owned(), + )); + }; + Ok(Self { + symbol_lookup:Rc::new(SymbolInfoLookup::new()), + msg: sb, + shape: SymbolShapeHint::FORCE_NONE, + codewords: String::with_capacity(msg.len()), + newEncoding: -1, + minSize: None, + maxSize: None, + pos: 0, + symbolInfo: None, + skipAtEnd: 0, + }) + } + + pub fn setSymbolShape(&mut self, shape: SymbolShapeHint) { + self.shape = shape; + } + + pub fn setSizeConstraints(&mut self, minSize: Dimension, maxSize: Dimension) { + self.minSize = Some(minSize); + self.maxSize = Some(maxSize); + } + + pub fn getMessage(&self) -> &str { + &self.msg + } + + pub fn setSkipAtEnd(&mut self, count: u32) { + self.skipAtEnd = count; + } + + pub fn getCurrentChar(&self) -> &str { + self.msg.graphemes(true).nth(self.pos as usize).unwrap() + } + + pub fn getCurrent(&self) -> &str { + self.msg.graphemes(true).nth(self.pos as usize).unwrap() + } + + pub fn getCodewords(&self) -> &str { + &self.codewords + } + + pub fn writeCodewords(&mut self, codewords: &str) { + self.codewords.push_str(codewords); + } + + pub fn writeCodeword(&mut self, codeword: &str) { + self.codewords.push_str(codeword); + } + + pub fn getCodewordCount(&self) -> usize { + self.codewords.len() + } + + pub fn getNewEncoding(&self) -> i32 { + self.newEncoding + } + + pub fn signalEncoderChange(&mut self, encoding: i32) { + self.newEncoding = encoding; + } + + pub fn resetEncoderSignal(&mut self) { + self.newEncoding = -1; + } + + pub fn hasMoreCharacters(&self) -> bool { + self.pos < self.getTotalMessageCharCount() + } + + fn getTotalMessageCharCount(&self) -> u32 { + self.msg.len() as u32 - self.skipAtEnd + } + + pub fn getRemainingCharacters(&self) -> u32 { + self.getTotalMessageCharCount() - self.pos + } + + pub fn getSymbolInfo(&self) -> &Option<&SymbolInfo> { + &self.symbolInfo + } + + pub fn updateSymbolInfo(&mut self) { + self.updateSymbolInfoWithLength(self.getCodewordCount()); + } + + pub fn updateSymbolInfoWithLength(&mut self, len: usize) { + if self.symbolInfo.is_none() + || len > self.symbolInfo.as_ref().unwrap().getDataCapacity() as usize + { + self.symbolInfo = Some( + self.symbol_lookup.lookup_with_codewords_shape_size_fail( + len as u32, + self.shape, + &self.minSize, + &self.maxSize, + true, + ) + .unwrap() + .unwrap(), + ); + } + } + + pub fn resetSymbolInfo(&mut self) { + self.symbolInfo = None; + } +} diff --git a/src/datamatrix/encoder/mod.rs b/src/datamatrix/encoder/mod.rs index e69de29..272b716 100644 --- a/src/datamatrix/encoder/mod.rs +++ b/src/datamatrix/encoder/mod.rs @@ -0,0 +1,9 @@ +mod encoder; +mod encoder_context; +mod symbol_shape_hint; +mod symbol_info; + +pub use encoder::*; +pub use encoder_context::*; +pub use symbol_shape_hint::*; +pub use symbol_info::*; diff --git a/src/datamatrix/encoder/symbol_info.rs b/src/datamatrix/encoder/symbol_info.rs new file mode 100644 index 0000000..ad18ff4 --- /dev/null +++ b/src/datamatrix/encoder/symbol_info.rs @@ -0,0 +1,508 @@ +/* + * Copyright 2006 Jeremias Maerki + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use std::fmt; + +use crate::{Dimension, Exceptions}; + +use super::SymbolShapeHint; +use lazy_static::lazy_static; + +lazy_static! { +static ref PROD_SYMBOLS: Vec = vec![ + SymbolInfo::new(false, 3, 5, 8, 8, 1), + SymbolInfo::new(false, 5, 7, 10, 10, 1), + /*rect*/ SymbolInfo::new(true, 5, 7, 16, 6, 1), + SymbolInfo::new(false, 8, 10, 12, 12, 1), + /*rect*/ SymbolInfo::new(true, 10, 11, 14, 6, 2), + SymbolInfo::new(false, 12, 12, 14, 14, 1), + /*rect*/ SymbolInfo::new(true, 16, 14, 24, 10, 1), + SymbolInfo::new(false, 18, 14, 16, 16, 1), + SymbolInfo::new(false, 22, 18, 18, 18, 1), + /*rect*/ SymbolInfo::new(true, 22, 18, 16, 10, 2), + SymbolInfo::new(false, 30, 20, 20, 20, 1), + /*rect*/ SymbolInfo::new(true, 32, 24, 16, 14, 2), + SymbolInfo::new(false, 36, 24, 22, 22, 1), + SymbolInfo::new(false, 44, 28, 24, 24, 1), + /*rect*/ SymbolInfo::new(true, 49, 28, 22, 14, 2), + SymbolInfo::new(false, 62, 36, 14, 14, 4), + SymbolInfo::new(false, 86, 42, 16, 16, 4), + SymbolInfo::new(false, 114, 48, 18, 18, 4), + SymbolInfo::new(false, 144, 56, 20, 20, 4), + SymbolInfo::new(false, 174, 68, 22, 22, 4), + SymbolInfo::with_details(false, 204, 84, 24, 24, 4, 102, 42), + SymbolInfo::with_details(false, 280, 112, 14, 14, 16, 140, 56), + SymbolInfo::with_details(false, 368, 144, 16, 16, 16, 92, 36), + SymbolInfo::with_details(false, 456, 192, 18, 18, 16, 114, 48), + SymbolInfo::with_details(false, 576, 224, 20, 20, 16, 144, 56), + SymbolInfo::with_details(false, 696, 272, 22, 22, 16, 174, 68), + SymbolInfo::with_details(false, 816, 336, 24, 24, 16, 136, 56), + SymbolInfo::with_details(false, 1050, 408, 18, 18, 36, 175, 68), + SymbolInfo::with_details(false, 1304, 496, 20, 20, 36, 163, 62), + SymbolInfo::new_symbol_info_144(), +]; +} + +/** + * Symbol info table for DataMatrix. + * + * @version $Id$ + */ +pub struct SymbolInfo { + rectangular: bool, + dataCapacity: u32, + errorCodewords: u32, + matrixWidth: u32, + matrixHeight: u32, + dataRegions: u32, + rsBlockData: i32, + rsBlockError: u32, + isSymbolInfo144: bool, +} +impl SymbolInfo { + pub fn new( + rectangular: bool, + dataCapacity: u32, + errorCodewords: u32, + matrixWidth: u32, + matrixHeight: u32, + dataRegions: u32, + ) -> Self { + Self::with_details( + rectangular, + dataCapacity, + errorCodewords, + matrixWidth, + matrixHeight, + dataRegions, + dataCapacity as i32, + errorCodewords, + ) + } + + pub fn with_details( + rectangular: bool, + dataCapacity: u32, + errorCodewords: u32, + matrixWidth: u32, + matrixHeight: u32, + dataRegions: u32, + rsBlockData: i32, + rsBlockError: u32, + ) -> Self { + Self { + rectangular, + dataCapacity, + errorCodewords, + matrixWidth, + matrixHeight, + dataRegions, + rsBlockData, + rsBlockError, + isSymbolInfo144: false, + } + } + pub fn new_symbol_info_144() -> Self { + let mut new_symbol = Self::with_details(false, 1558, 620, 22, 22, 36, -1, 62); + new_symbol.isSymbolInfo144 = true; + new_symbol + } + + fn getHorizontalDataRegions(&self) -> Result { + match self.dataRegions { + 1 => Ok(1), + 2 | 4 => Ok(2), + 16 => Ok(4), + 36 => Ok(6), + _ => Err(Exceptions::IllegalStateException( + "Cannot handle this number of data regions".to_owned(), + )), + } + // switch (dataRegions) { + // case 1: + // return 1; + // case 2: + // case 4: + // return 2; + // case 16: + // return 4; + // case 36: + // return 6; + // default: + // throw new IllegalStateException("Cannot handle this number of data regions"); + // } + } + + fn getVerticalDataRegions(&self) -> Result { + match self.dataRegions { + 1 | 2 => Ok(1), + 4 => Ok(2), + 16 => Ok(4), + 36 => Ok(6), + _ => Err(Exceptions::IllegalStateException( + "Cannot handle this number of data regions".to_owned(), + )), + } + // switch (dataRegions) { + // case 1: + // case 2: + // return 1; + // case 4: + // return 2; + // case 16: + // return 4; + // case 36: + // return 6; + // default: + // throw new IllegalStateException("Cannot handle this number of data regions"); + // } + } + + pub fn getSymbolDataWidth(&self) -> Result { + Ok(self.getHorizontalDataRegions()? * self.matrixWidth) + } + + pub fn getSymbolDataHeight(&self) -> Result { + Ok(self.getVerticalDataRegions()? * self.matrixHeight) + } + + pub fn getSymbolWidth(&self) -> Result { + Ok(self.getSymbolDataWidth()? + (self.getHorizontalDataRegions()? * 2)) + } + + pub fn getSymbolHeight(&self) -> Result { + Ok(self.getSymbolDataHeight()? + (self.getVerticalDataRegions()? * 2)) + } + + pub fn getCodewordCount(&self) -> u32 { + self.dataCapacity + self.errorCodewords + } + + pub fn getInterleavedBlockCount(&self) -> u32 { + if self.isSymbolInfo144 { + 10 + } else { + self.dataCapacity / self.rsBlockData as u32 + } + } + + pub fn getDataCapacity(&self) -> u32 { + self.dataCapacity + } + + pub fn getErrorCodewords(&self) -> u32 { + self.errorCodewords + } + + pub fn getDataLengthForInterleavedBlock(&self, index: u32) -> i32 { + if self.isSymbolInfo144 { + if index <= 8 { + 156 + } else { + 155 + } + } else { + self.rsBlockData + } + } + + pub fn getErrorLengthForInterleavedBlock(&self, index: u32) -> u32 { + self.rsBlockError + } + + // @Override + // public final String toString() { + // return (rectangular ? "Rectangular Symbol:" : "Square Symbol:") + + // " data region " + matrixWidth + 'x' + matrixHeight + + // ", symbol size " + getSymbolWidth() + 'x' + getSymbolHeight() + + // ", symbol data size " + getSymbolDataWidth() + 'x' + getSymbolDataHeight() + + // ", codewords " + dataCapacity + '+' + errorCodewords; + // } +} + +impl fmt::Display for SymbolInfo { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "{} data region {}x{}, symbol size {}x{}, symbol data size {}x{}, codewords {}+{}", + if self.rectangular { + "Rectangular Symbol:" + } else { + "Square Symbol:" + }, + self.matrixWidth, + self.matrixHeight, + self.getSymbolWidth().unwrap_or_default(), + self.getSymbolHeight().unwrap_or_default(), + self.getSymbolDataWidth().unwrap_or_default(), + self.getSymbolDataHeight().unwrap_or_default(), + self.dataCapacity, + self.errorCodewords + ) + } +} + +pub struct SymbolInfoLookup<'a>(Option<&'a Vec>); +impl<'a> SymbolInfoLookup<'a> { + pub const fn new() -> Self { + Self(None) + } + /** + * Overrides the symbol info set used by this class. Used for testing purposes. + * + * @param override the symbol info set to use + */ + pub fn overrideSymbolSet(&mut self, override_symbols: &'a Vec) { + self.0 = Some(override_symbols); + } + + pub fn lookup(&self, dataCodewords: u32) -> Result, Exceptions> { + self.lookup_with_codewords_shape_fail(dataCodewords, SymbolShapeHint::FORCE_NONE, true) + } + + pub fn lookup_with_shape( + &self, + dataCodewords: u32, + shape: SymbolShapeHint, + ) -> Result, Exceptions> { + self.lookup_with_codewords_shape_fail(dataCodewords, shape, true) + } + + pub fn lookup_codwords_rectangule_fail( + &self, + dataCodewords: u32, + allowRectangular: bool, + fail: bool, + ) -> Result, Exceptions> { + let shape = if allowRectangular { + SymbolShapeHint::FORCE_NONE + } else { + SymbolShapeHint::FORCE_SQUARE + }; + self.lookup_with_codewords_shape_fail(dataCodewords, shape, fail) + } + + fn lookup_with_codewords_shape_fail( + &self, + dataCodewords: u32, + shape: SymbolShapeHint, + fail: bool, + ) -> Result, Exceptions> { + self.lookup_with_codewords_shape_size_fail(dataCodewords, shape, &None, &None, fail) + } + + pub fn lookup_with_codewords_shape_size_fail( + &self, + dataCodewords: u32, + shape: SymbolShapeHint, + minSize: &Option, + maxSize: &Option, + fail: bool, + // alternate_symbols_chart: Option<&'a Vec>, + ) -> Result, Exceptions> { + let symbol_search_chart: &Vec = if self.0.is_none() { + &PROD_SYMBOLS + } else { + self.0.as_ref().unwrap() + }; + for symbol in symbol_search_chart { + // for (SymbolInfo symbol : symbols) { + if shape == SymbolShapeHint::FORCE_SQUARE && symbol.rectangular { + continue; + } + if shape == SymbolShapeHint::FORCE_RECTANGLE && !symbol.rectangular { + continue; + } + if minSize.is_some() + && ((symbol.getSymbolWidth()? as usize) < minSize.as_ref().unwrap().getWidth() + || (symbol.getSymbolHeight()? as usize) < minSize.as_ref().unwrap().getHeight()) + { + continue; + } + if maxSize.is_some() + && ((symbol.getSymbolWidth()? as usize) > maxSize.as_ref().unwrap().getWidth() + || (symbol.getSymbolHeight()? as usize) > maxSize.as_ref().unwrap().getHeight()) + { + continue; + } + if dataCodewords <= symbol.dataCapacity { + return Ok(Some(symbol)); + } + } + if fail { + return Err(Exceptions::IllegalArgumentException(format!( + "Can't find a symbol arrangement that matches the message. Data codewords: {}", + dataCodewords + ))); + } + Ok(None) + } +} + +mod tests { + use crate::{ + datamatrix::encoder::{SymbolInfo, SymbolShapeHint}, + Dimension, + }; + + use super::SymbolInfoLookup; + + const LOOKUP: SymbolInfoLookup = SymbolInfoLookup::new(); + + /** + * Tests the SymbolInfo class. + */ + #[test] + fn testSymbolInfo() { + let info = LOOKUP.lookup(3).expect("returns").expect("exists"); + assert_eq!(5, info.getErrorCodewords()); + assert_eq!(8, info.matrixWidth); + assert_eq!(8, info.matrixHeight); + assert_eq!(10, info.getSymbolWidth().expect("returns")); + assert_eq!(10, info.getSymbolHeight().expect("returns")); + + let info = LOOKUP + .lookup_with_shape(3, SymbolShapeHint::FORCE_RECTANGLE) + .expect("returns") + .expect("exists"); + assert_eq!(7, info.getErrorCodewords()); + assert_eq!(16, info.matrixWidth); + assert_eq!(6, info.matrixHeight); + assert_eq!(18, info.getSymbolWidth().expect("returns")); + assert_eq!(8, info.getSymbolHeight().expect("returns")); + + let info = LOOKUP.lookup(9).expect("returns").expect("exists"); + assert_eq!(11, info.getErrorCodewords()); + assert_eq!(14, info.matrixWidth); + assert_eq!(6, info.matrixHeight); + assert_eq!(32, info.getSymbolWidth().expect("returns")); + assert_eq!(8, info.getSymbolHeight().expect("returns")); + + let info = LOOKUP + .lookup_with_shape(9, SymbolShapeHint::FORCE_SQUARE) + .expect("returns") + .expect("exists"); + assert_eq!(12, info.getErrorCodewords()); + assert_eq!(14, info.matrixWidth); + assert_eq!(14, info.matrixHeight); + assert_eq!(16, info.getSymbolWidth().expect("returns")); + assert_eq!(16, info.getSymbolHeight().expect("returns")); + + assert!(LOOKUP.lookup(1559).is_err()); + + // try { + // SymbolInfo.lookup(1559); + // fail("There's no rectangular symbol for more than 1558 data codewords"); + // } catch (IllegalArgumentException iae) { + // //expected + // } + assert!(LOOKUP + .lookup_with_shape(50, SymbolShapeHint::FORCE_RECTANGLE) + .is_err()); + // try { + // SymbolInfo.lookup(50, SymbolShapeHint.FORCE_RECTANGLE); + // fail("There's no rectangular symbol for 50 data codewords"); + // } catch (IllegalArgumentException iae) { + // //expected + // } + + let info = LOOKUP.lookup(35).expect("returns").expect("exists"); + assert_eq!(24, info.getSymbolWidth().expect("return")); + assert_eq!(24, info.getSymbolHeight().expect("return")); + + let fixedSize = Dimension::new(26, 26).expect("new dimension"); + let info = LOOKUP + .lookup_with_codewords_shape_size_fail( + 35, + SymbolShapeHint::FORCE_NONE, + &Some(fixedSize), + &Some(fixedSize), + false, + ) + .expect("returns"); + assert!(info.is_some()); + let info = info.unwrap(); + assert_eq!(26, info.getSymbolWidth().expect("return")); + assert_eq!(26, info.getSymbolHeight().expect("return")); + + let info = LOOKUP + .lookup_with_codewords_shape_size_fail( + 45, + SymbolShapeHint::FORCE_NONE, + &Some(fixedSize), + &Some(fixedSize), + false, + ) + .expect("return"); + assert!(info.is_none()); + + let minSize = fixedSize; + let maxSize = Dimension::new(32, 32).expect("new dimension"); + + let info = LOOKUP + .lookup_with_codewords_shape_size_fail( + 35, + SymbolShapeHint::FORCE_NONE, + &Some(minSize), + &Some(maxSize), + false, + ) + .expect("return"); + assert!(info.is_some()); + let info = info.unwrap(); + assert_eq!(26, info.getSymbolWidth().expect("return")); + assert_eq!(26, info.getSymbolHeight().expect("return")); + + let info = LOOKUP + .lookup_with_codewords_shape_size_fail( + 40, + SymbolShapeHint::FORCE_NONE, + &Some(minSize), + &Some(maxSize), + false, + ) + .expect("return"); + assert!(info.is_some()); + let info = info.unwrap(); + assert_eq!(26, info.getSymbolWidth().expect("return")); + assert_eq!(26, info.getSymbolHeight().expect("return")); + + let info = LOOKUP + .lookup_with_codewords_shape_size_fail( + 45, + SymbolShapeHint::FORCE_NONE, + &Some(minSize), + &Some(maxSize), + false, + ) + .expect("return"); + assert!(info.is_some()); + let info = info.unwrap(); + assert_eq!(32, info.getSymbolWidth().expect("return")); + assert_eq!(32, info.getSymbolHeight().expect("return")); + + let info = LOOKUP + .lookup_with_codewords_shape_size_fail( + 63, + SymbolShapeHint::FORCE_NONE, + &Some(minSize), + &Some(maxSize), + false, + ) + .expect("return"); + assert!(info.is_none()); + } +} diff --git a/src/datamatrix/encoder/SymbolShapeHint.java b/src/datamatrix/encoder/symbol_shape_hint.rs similarity index 84% rename from src/datamatrix/encoder/SymbolShapeHint.java rename to src/datamatrix/encoder/symbol_shape_hint.rs index 898727f..d0eabf5 100644 --- a/src/datamatrix/encoder/SymbolShapeHint.java +++ b/src/datamatrix/encoder/symbol_shape_hint.rs @@ -14,16 +14,13 @@ * limitations under the License. */ -package com.google.zxing.datamatrix.encoder; - /** * Enumeration for DataMatrix symbol shape hint. It can be used to force square or rectangular * symbols. */ -public enum SymbolShapeHint { - - FORCE_NONE, - FORCE_SQUARE, - FORCE_RECTANGLE, - +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum SymbolShapeHint { + FORCE_NONE, + FORCE_SQUARE, + FORCE_RECTANGLE, } diff --git a/src/dimension.rs b/src/dimension.rs index 5976a16..b0de16c 100644 --- a/src/dimension.rs +++ b/src/dimension.rs @@ -23,7 +23,7 @@ use crate::Exceptions; /** * Simply encapsulates a width and height. */ -#[derive(Eq, PartialEq, Hash)] +#[derive(Eq, PartialEq, Hash, Copy, Clone)] pub struct Dimension(usize, usize); impl Dimension {