progress porting minimal endoder

This commit is contained in:
Henry Schimke
2022-10-28 12:37:16 -05:00
parent 7ac08007e7
commit 2171d025f9
5 changed files with 395 additions and 311 deletions

View File

@@ -14,39 +14,41 @@
* limitations under the License. * limitations under the License.
*/ */
package com.google.zxing.datamatrix.encoder; use lazy_static::lazy_static;
import junit.framework.ComparisonFailure; use crate::datamatrix::encoder::SymbolInfo;
import org.junit.Assert;
import org.junit.Test;
import java.nio.charset.StandardCharsets;
/** use super::{SymbolInfoLookup, symbol_info, high_level_encoder};
lazy_static!{
/**
* Tests for {@link HighLevelEncoder} and {@link MinimalEncoder} * Tests for {@link HighLevelEncoder} and {@link MinimalEncoder}
*/ */
public final class HighLevelEncodeTestCase extends Assert { static ref TEST_SYMBOLS :Vec<SymbolInfo>= vec![
SymbolInfo::new(false, 3, 5, 8, 8, 1),
private static final SymbolInfo[] TEST_SYMBOLS = { SymbolInfo::new(false, 5, 7, 10, 10, 1),
new SymbolInfo(false, 3, 5, 8, 8, 1), /*rect*/ SymbolInfo::new(true, 5, 7, 16, 6, 1),
new SymbolInfo(false, 5, 7, 10, 10, 1), SymbolInfo::new(false, 8, 10, 12, 12, 1),
/*rect*/new SymbolInfo(true, 5, 7, 16, 6, 1), /*rect*/ SymbolInfo::new(true, 10, 11, 14, 6, 2),
new SymbolInfo(false, 8, 10, 12, 12, 1), SymbolInfo::new(false, 13, 0, 0, 0, 1),
/*rect*/new SymbolInfo(true, 10, 11, 14, 6, 2), SymbolInfo::new(false, 77, 0, 0, 0, 1)
new SymbolInfo(false, 13, 0, 0, 0, 1),
new SymbolInfo(false, 77, 0, 0, 0, 1)
//The last entries are fake entries to test special conditions with C40 encoding //The last entries are fake entries to test special conditions with C40 encoding
}; ];
}
private static void useTestSymbols() { fn useTestSymbols(lookup:SymbolInfoLookup) -> SymbolInfoLookup {
SymbolInfo.overrideSymbolSet(TEST_SYMBOLS); lookup.overrideSymbolSet(&TEST_SYMBOLS);
lookup
} }
private static void resetSymbols() { fn resetSymbols(lookup:SymbolInfoLookup) -> SymbolInfoLookup {
SymbolInfo.overrideSymbolSet(SymbolInfo.PROD_SYMBOLS); lookup.overrideSymbolSet(&symbol_info::PROD_SYMBOLS);
lookup
} }
@Test #[test]
public void testASCIIEncodation() { fn testASCIIEncodation() {
String visualized = encodeHighLevel("123456"); String visualized = encodeHighLevel("123456");
assertEquals("142 164 186", visualized); assertEquals("142 164 186", visualized);
@@ -58,16 +60,16 @@ public final class HighLevelEncodeTestCase extends Assert {
assertEquals("160 82 162 173 173 173 137 224 61 80 82 82", visualized); assertEquals("160 82 162 173 173 173 137 224 61 80 82 82", visualized);
} }
@Test #[test]
public void testC40EncodationBasic1() { fn testC40EncodationBasic1() {
String visualized = encodeHighLevel("AIMAIMAIM"); String visualized = encodeHighLevel("AIMAIMAIM");
assertEquals("230 91 11 91 11 91 11 254", visualized); assertEquals("230 91 11 91 11 91 11 254", visualized);
//230 shifts to C40 encodation, 254 unlatches, "else" case //230 shifts to C40 encodation, 254 unlatches, "else" case
} }
@Test #[test]
public void testC40EncodationBasic2() { fn testC40EncodationBasic2() {
String visualized = encodeHighLevel("AIMAIAB"); String visualized = encodeHighLevel("AIMAIAB");
assertEquals("230 91 11 90 255 254 67 129", visualized); assertEquals("230 91 11 90 255 254 67 129", visualized);
@@ -98,15 +100,15 @@ public final class HighLevelEncodeTestCase extends Assert {
//"else" case //"else" case
} }
@Test #[test]
public void testC40EncodationSpecExample() { fn testC40EncodationSpecExample() {
//Example in Figure 1 in the spec //Example in Figure 1 in the spec
String visualized = encodeHighLevel("A1B2C3D4E5F6G7H8I9J0K1L2"); String visualized = encodeHighLevel("A1B2C3D4E5F6G7H8I9J0K1L2");
assertEquals("230 88 88 40 8 107 147 59 67 126 206 78 126 144 121 35 47 254", visualized); assertEquals("230 88 88 40 8 107 147 59 67 126 206 78 126 144 121 35 47 254", visualized);
} }
@Test #[test]
public void testC40EncodationSpecialCases1() { fn testC40EncodationSpecialCases1() {
//Special tests avoiding ultra-long test strings because these tests are only used //Special tests avoiding ultra-long test strings because these tests are only used
//with the 16x48 symbol (47 data codewords) //with the 16x48 symbol (47 data codewords)
@@ -134,16 +136,16 @@ public final class HighLevelEncodeTestCase extends Assert {
//case "d": Skip Unlatch and write last character in ASCII //case "d": Skip Unlatch and write last character in ASCII
} }
@Test #[test]
public void testC40EncodationSpecialCases2() { fn testC40EncodationSpecialCases2() {
String visualized = encodeHighLevel("AIMAIMAIMAIMAIMAIMAI"); String visualized = encodeHighLevel("AIMAIMAIMAIMAIMAIMAI");
assertEquals("230 91 11 91 11 91 11 91 11 91 11 91 11 254 66 74", visualized); assertEquals("230 91 11 91 11 91 11 91 11 91 11 91 11 254 66 74", visualized);
//available > 2, rest = 2 --> unlatch and encode as ASCII //available > 2, rest = 2 --> unlatch and encode as ASCII
} }
@Test #[test]
public void testTextEncodation() { fn testTextEncodation() {
String visualized = encodeHighLevel("aimaimaim"); String visualized = encodeHighLevel("aimaimaim");
assertEquals("239 91 11 91 11 91 11 254", visualized); assertEquals("239 91 11 91 11 91 11 254", visualized);
@@ -164,8 +166,8 @@ public final class HighLevelEncodeTestCase extends Assert {
assertEquals("239 91 11 91 11 91 11 254 124 117 121 117 126 5 129 237", visualized); assertEquals("239 91 11 91 11 91 11 254 124 117 121 117 126 5 129 237", visualized);
} }
@Test #[test]
public void testX12Encodation() { fn testX12Encodation() {
//238 shifts to X12 encodation, 254 unlatches //238 shifts to X12 encodation, 254 unlatches
@@ -186,8 +188,8 @@ public final class HighLevelEncodeTestCase extends Assert {
} }
@Test #[test]
public void testEDIFACTEncodation() { fn testEDIFACTEncodation() {
//240 shifts to EDIFACT encodation //240 shifts to EDIFACT encodation
@@ -221,8 +223,8 @@ public final class HighLevelEncodeTestCase extends Assert {
visualized); visualized);
} }
@Test #[test]
public void testBase256Encodation() { fn testBase256Encodation() {
//231 shifts to Base256 encodation //231 shifts to Base256 encodation
@@ -259,7 +261,7 @@ public final class HighLevelEncodeTestCase extends Assert {
assertEndsWith("146 40 190 87", visualized); assertEndsWith("146 40 190 87", visualized);
} }
private static String createBinaryMessage(int len) { fn createBinaryMessage( len:usize) ->String{
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.append("\u00ABäöüéàá-"); sb.append("\u00ABäöüéàá-");
for (int i = 0; i < len - 9; i++) { for (int i = 0; i < len - 9; i++) {
@@ -269,41 +271,41 @@ public final class HighLevelEncodeTestCase extends Assert {
return sb.toString(); return sb.toString();
} }
private static void assertStartsWith(String expected, String actual) { fn assertStartsWith( expected:&str, actual:&str) {
if (!actual.startsWith(expected)) { if (!actual.startsWith(expected)) {
throw new ComparisonFailure(null, expected, actual.substring(0, expected.length())); throw new ComparisonFailure(null, expected, actual.substring(0, expected.length()));
} }
} }
private static void assertEndsWith(String expected, String actual) { fn assertEndsWith( expected:&str, actual:&str) {
if (!actual.endsWith(expected)) { if (!actual.endsWith(expected)) {
throw new ComparisonFailure(null, expected, actual.substring(actual.length() - expected.length())); throw new ComparisonFailure(null, expected, actual.substring(actual.length() - expected.length()));
} }
} }
@Test #[test]
public void testUnlatchingFromC40() { fn testUnlatchingFromC40() {
String visualized = encodeHighLevel("AIMAIMAIMAIMaimaimaim"); String visualized = encodeHighLevel("AIMAIMAIMAIMaimaimaim");
assertEquals("230 91 11 91 11 91 11 254 66 74 78 239 91 11 91 11 91 11", visualized); assertEquals("230 91 11 91 11 91 11 254 66 74 78 239 91 11 91 11 91 11", visualized);
} }
@Test #[test]
public void testUnlatchingFromText() { fn testUnlatchingFromText() {
String visualized = encodeHighLevel("aimaimaimaim12345678"); String visualized = encodeHighLevel("aimaimaimaim12345678");
assertEquals("239 91 11 91 11 91 11 91 11 254 142 164 186 208 129 237", visualized); assertEquals("239 91 11 91 11 91 11 91 11 254 142 164 186 208 129 237", visualized);
} }
@Test #[test]
public void testHelloWorld() { fn testHelloWorld() {
String visualized = encodeHighLevel("Hello World!"); String visualized = encodeHighLevel("Hello World!");
assertEquals("73 239 116 130 175 123 148 64 158 233 254 34", visualized); assertEquals("73 239 116 130 175 123 148 64 158 233 254 34", visualized);
} }
@Test #[test]
public void testBug1664266() { fn testBug1664266() {
//There was an exception and the encoder did not handle the unlatching from //There was an exception and the encoder did not handle the unlatching from
//EDIFACT encoding correctly //EDIFACT encoding correctly
@@ -317,20 +319,20 @@ public final class HighLevelEncodeTestCase extends Assert {
assertEquals("68 83 70 89 46 85 66 79 59 105 105 105", visualized); assertEquals("68 83 70 89 46 85 66 79 59 105 105 105", visualized);
} }
@Test #[test]
public void testX12Unlatch() { fn testX12Unlatch() {
String visualized = encodeHighLevel("*DTCP01"); String visualized = encodeHighLevel("*DTCP01");
assertEquals("43 69 85 68 81 131 129 56", visualized); assertEquals("43 69 85 68 81 131 129 56", visualized);
} }
@Test #[test]
public void testX12Unlatch2() { fn testX12Unlatch2() {
String visualized = encodeHighLevel("*DTCP0"); String visualized = encodeHighLevel("*DTCP0");
assertEquals("238 9 10 104 141", visualized); assertEquals("238 9 10 104 141", visualized);
} }
@Test #[test]
public void testBug3048549() { fn testBug3048549() {
//There was an IllegalArgumentException for an illegal character here because //There was an IllegalArgumentException for an illegal character here because
//of an encoding problem of the character 0x0060 in Java source code. //of an encoding problem of the character 0x0060 in Java source code.
@@ -339,23 +341,23 @@ public final class HighLevelEncodeTestCase extends Assert {
} }
@Test #[test]
public void testMacroCharacters() { fn testMacroCharacters() {
String visualized = encodeHighLevel("[)>\u001E05\u001D5555\u001C6666\u001E\u0004"); String visualized = encodeHighLevel("[)>\u001E05\u001D5555\u001C6666\u001E\u0004");
//assertEquals("92 42 63 31 135 30 185 185 29 196 196 31 5 129 87 237", visualized); //assertEquals("92 42 63 31 135 30 185 185 29 196 196 31 5 129 87 237", visualized);
assertEquals("236 185 185 29 196 196 129 56", visualized); assertEquals("236 185 185 29 196 196 129 56", visualized);
} }
@Test #[test]
public void testEncodingWithStartAsX12AndLatchToEDIFACTInTheMiddle() { fn testEncodingWithStartAsX12AndLatchToEDIFACTInTheMiddle() {
String visualized = encodeHighLevel("*MEMANT-1F-MESTECH"); String visualized = encodeHighLevel("*MEMANT-1F-MESTECH");
assertEquals("240 168 209 77 4 229 45 196 107 77 21 53 5 12 135 192", visualized); assertEquals("240 168 209 77 4 229 45 196 107 77 21 53 5 12 135 192", visualized);
} }
@Test #[test]
public void testX12AndEDIFACTSpecErrors() { fn testX12AndEDIFACTSpecErrors() {
//X12 encoding error with spec conform float point comparisons in lookAheadTest() //X12 encoding error with spec conform float point comparisons in lookAheadTest()
String visualized = encodeHighLevel("AAAAAAAAAAA**\u00FCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"); String visualized = encodeHighLevel("AAAAAAAAAAA**\u00FCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
assertEquals("230 89 191 89 191 89 191 89 178 56 114 10 243 177 63 89 191 89 191 89 191 89 191 89 191 89 191 89 " + assertEquals("230 89 191 89 191 89 191 89 178 56 114 10 243 177 63 89 191 89 191 89 191 89 191 89 191 89 191 89 " +
@@ -380,8 +382,8 @@ public final class HighLevelEncodeTestCase extends Assert {
"191 89 191 89 191 254 66 66", visualized); "191 89 191 89 191 254 66 66", visualized);
} }
@Test #[test]
public void testSizes() { fn testSizes() {
int[] sizes = new int[2]; int[] sizes = new int[2];
encodeHighLevel("A", sizes); encodeHighLevel("A", sizes);
assertEquals(3, sizes[0]); assertEquals(3, sizes[0]);
@@ -493,8 +495,8 @@ public final class HighLevelEncodeTestCase extends Assert {
assertEquals(62, sizes[1]); assertEquals(62, sizes[1]);
} }
@Test #[test]
public void testECIs() { fn testECIs() {
String visualized = visualize(MinimalEncoder.encodeHighLevel("that particularly stands out to me is \u0625\u0650" + String visualized = visualize(MinimalEncoder.encodeHighLevel("that particularly stands out to me is \u0625\u0650" +
"\u062C\u064E\u0651\u0627\u0635 (\u02BE\u0101\u1E63) \"pear\", suggested to have originated from Hebrew " + "\u062C\u064E\u0651\u0627\u0635 (\u02BE\u0101\u1E63) \"pear\", suggested to have originated from Hebrew " +
@@ -516,8 +518,8 @@ public final class HighLevelEncodeTestCase extends Assert {
visualized); visualized);
} }
@Test #[test]
public void testPadding() { fn testPadding() {
int[] sizes = new int[2]; int[] sizes = new int[2];
encodeHighLevel("IS010000000000000000000000S1118058599124123S21.2.250.1.213.1.4.8 S3FIRST NAMETEST S5MS618-06" + encodeHighLevel("IS010000000000000000000000S1118058599124123S21.2.250.1.213.1.4.8 S3FIRST NAMETEST S5MS618-06" +
"-1985S713201S4LASTNAMETEST", sizes); "-1985S713201S4LASTNAMETEST", sizes);
@@ -526,20 +528,20 @@ public final class HighLevelEncodeTestCase extends Assert {
} }
private static void encodeHighLevel(String msg, int[] sizes) { fn encodeHighLevelWithSizes( msg:&str, sizes:&[u32]) {
sizes[0] = HighLevelEncoder.encodeHighLevel(msg).length(); sizes[0] = high_level_encoder::encodeHighLevel(msg).expect("encodes").len();
sizes[1] = MinimalEncoder.encodeHighLevel(msg).length(); sizes[1] = MinimalEncoder.encodeHighLevel(msg).length();
} }
private static String encodeHighLevel(String msg) { fn encodeHighLevel( msg:&str) ->String{
return encodeHighLevel(msg, true); encodeHighLevelCompare(msg, true)
} }
private static String encodeHighLevel(String msg, boolean compareSizeToMinimalEncoder) { fn encodeHighLevelCompare( msg:&str, compareSizeToMinimalEncoder:bool) -> String{
CharSequence encoded = HighLevelEncoder.encodeHighLevel(msg); let encoded = high_level_encoder::encodeHighLevel(msg).expect("encodes");
CharSequence encoded2 = MinimalEncoder.encodeHighLevel(msg); let encoded2 = high_level_encoder::encodeHighLevel(msg).expect("encodes");
assertTrue(!compareSizeToMinimalEncoder || encoded2.length() <= encoded.length()); assert!(!compareSizeToMinimalEncoder || encoded2.len() <= encoded.len());
return visualize(encoded); visualize(&encoded)
} }
/** /**
@@ -549,15 +551,16 @@ public final class HighLevelEncodeTestCase extends Assert {
* @param codewords the codewords * @param codewords the codewords
* @return the visualized codewords * @return the visualized codewords
*/ */
static String visualize(CharSequence codewords) { fn visualize( codewords:&str) -> String{
StringBuilder sb = new StringBuilder(); let mut sb = String::new();
for (int i = 0; i < codewords.length(); i++) { for i in 0..codewords.len() {
// for (int i = 0; i < codewords.length(); i++) {
if (i > 0) { if (i > 0) {
sb.append(' '); sb.push(' ');
} }
sb.append((int) codewords.charAt(i)); sb.push_str(&format!("{}", codewords.chars().nth(i).unwrap() as u32);
} }
return sb.toString();
sb
} }
}

View File

@@ -511,15 +511,15 @@ pub fn isExtendedASCII(ch: char) -> bool {
(ch as u8) >= 128 && (ch as u8) <= 255 (ch as u8) >= 128 && (ch as u8) <= 255
} }
fn isNativeC40(ch: char) -> bool { pub fn isNativeC40(ch: char) -> bool {
(ch == ' ') || (ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z') (ch == ' ') || (ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z')
} }
fn isNativeText(ch: char) -> bool { pub fn isNativeText(ch: char) -> bool {
(ch == ' ') || (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'z') (ch == ' ') || (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'z')
} }
fn isNativeX12(ch: char) -> bool { pub fn isNativeX12(ch: char) -> bool {
return isX12TermSep(ch) return isX12TermSep(ch)
|| (ch == ' ') || (ch == ' ')
|| (ch >= '0' && ch <= '9') || (ch >= '0' && ch <= '9')
@@ -532,7 +532,7 @@ fn isX12TermSep(ch: char) -> bool {
|| (ch == '>') || (ch == '>')
} }
fn isNativeEDIFACT(ch: char) -> bool { pub fn isNativeEDIFACT(ch: char) -> bool {
ch >= ' ' && ch <= '^' ch >= ' ' && ch <= '^'
} }

View File

@@ -14,14 +14,15 @@
* limitations under the License. * limitations under the License.
*/ */
package com.google.zxing.datamatrix.encoder; use std::{rc::Rc, fmt};
import java.nio.charset.Charset; use encoding::{self,EncodingRef};
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import com.google.zxing.common.MinimalECIInput; use crate::{common::{MinimalECIInput, ECIInput}, Exceptions};
use super::{SymbolShapeHint, high_level_encoder};
const ISO_8859_1_ENCODER : EncodingRef = encoding::all::ISO_8859_1;
/** /**
* Encoder that encodes minimally * Encoder that encodes minimally
@@ -30,7 +31,7 @@ import com.google.zxing.common.MinimalECIInput;
* *
* Uses Dijkstra to produce mathematically minimal encodings that are in some cases smaller than the results produced * Uses Dijkstra to produce mathematically minimal encodings that are in some cases smaller than the results produced
* by the algorithm described in annex S in the specification ISO/IEC 16022:200(E). The biggest improvment of this * by the algorithm described in annex S in the specification ISO/IEC 16022:200(E). The biggest improvment of this
* algorithm over that one is the case when the algorithm enters the most inefficient mode, the B256 mode. The * algorithm over that one is the case when the algorithm enters the most inefficient mode, the B256 Mode:: The
* algorithm from the specification algorithm will exit this mode only if it encounters digits so that arbitrarily * algorithm from the specification algorithm will exit this mode only if it encounters digits so that arbitrarily
* inefficient results can be produced if the postfix contains no digits. * inefficient results can be produced if the postfix contains no digits.
* *
@@ -58,8 +59,8 @@ import com.google.zxing.common.MinimalECIInput;
* *
* @author Alex Geller * @author Alex Geller
*/ */
public final class MinimalEncoder {
#[derive(Debug,Copy,Clone,PartialEq, Eq)]
enum Mode { enum Mode {
ASCII, ASCII,
C40, C40,
@@ -68,36 +69,59 @@ public final class MinimalEncoder {
EDF, EDF,
B256 B256
} }
static final char[] C40_SHIFT2_CHARS = {'!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/',
':', ';', '<', '=', '>', '?', '@', '[', '\\', ']', '^', '_' };
impl Mode {
private MinimalEncoder() { pub fn ordinal(&self) -> usize {
match self {
Mode::ASCII => 0,
Mode::C40 => 1,
Mode::TEXT => 2,
Mode::X12 => 3,
Mode::EDF => 4,
Mode::B256 => 5,
}
}
} }
static boolean isExtendedASCII(char ch, int fnc1) { const C40_SHIFT2_CHARS :[char;27] = ['!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/',
return ch != fnc1 && ch >= 128 && ch <= 255; ':', ';', '<', '=', '>', '?', '@', '[', '\\', ']', '^', '_' ];
pub fn isExtendedASCII( ch:char, fnc1:Option<char>) -> bool{
let is_fnc1 = if let Some(fnc1) = fnc1 {
ch != fnc1
}else {
true
};
is_fnc1 && ch as u8 >= 128 && ch as u8 <= 255
// return ch != fnc1 && ch as u8 >= 128 && ch as u8 <= 255;
} }
private static boolean isInC40Shift1Set(char ch) { fn isInC40Shift1Set( ch:char) -> bool{
return ch <= 31; ch as u8 <= 31
} }
private static boolean isInC40Shift2Set(char ch, int fnc1) { fn isInC40Shift2Set( ch:char, fnc1:Option<char>) -> bool{
for (char c40Shift2Char : C40_SHIFT2_CHARS) { for c40Shift2Char in C40_SHIFT2_CHARS {
if (c40Shift2Char == ch) { // for (char c40Shift2Char : C40_SHIFT2_CHARS) {
if c40Shift2Char == ch {
return true; return true;
} }
} }
return ch == fnc1; if let Some(fnc1) = fnc1 {
ch == fnc1
}else {
false
}
// return ch as u8 as i32 == fnc1;
} }
private static boolean isInTextShift1Set(char ch) { fn isInTextShift1Set( ch:char) -> bool{
return isInC40Shift1Set(ch); isInC40Shift1Set(ch)
} }
private static boolean isInTextShift2Set(char ch, int fnc1) { fn isInTextShift2Set( ch:char, fnc1:Option<char>) -> bool{
return isInC40Shift2Set(ch, fnc1); isInC40Shift2Set(ch, fnc1)
} }
/** /**
@@ -106,8 +130,8 @@ public final class MinimalEncoder {
* @param msg the message * @param msg the message
* @return the encoded message (the char values range from 0 to 255) * @return the encoded message (the char values range from 0 to 255)
*/ */
public static String encodeHighLevel(String msg) { pub fn encodeHighLevel( msg:&str) -> Result<String,Exceptions>{
return encodeHighLevel(msg, null, -1, SymbolShapeHint.FORCE_NONE); encodeHighLevelWithDetails(msg, None, None, SymbolShapeHint::FORCE_NONE)
} }
/** /**
@@ -123,16 +147,20 @@ public final class MinimalEncoder {
* @param shape requested shape. * @param shape requested shape.
* @return the encoded message (the char values range from 0 to 255) * @return the encoded message (the char values range from 0 to 255)
*/ */
public static String encodeHighLevel(String msg, Charset priorityCharset, int fnc1, SymbolShapeHint shape) { pub fn encodeHighLevelWithDetails( msg:&str, priorityCharset:Option<EncodingRef>, fnc1:Option<char>, shape:SymbolShapeHint)->Result<String,Exceptions> {
int macroId = 0; let mut msg = msg;
if (msg.startsWith(HighLevelEncoder.MACRO_05_HEADER) && msg.endsWith(HighLevelEncoder.MACRO_TRAILER)) { let macroId = 0;
if msg.starts_with(high_level_encoder::MACRO_05_HEADER) && msg.ends_with(high_level_encoder::MACRO_TRAILER) {
macroId = 5; macroId = 5;
msg = msg.substring(HighLevelEncoder.MACRO_05_HEADER.length(), msg.length() - 2); // msg = msg.substring(high_level_encoder::MACRO_05_HEADER.len(), msg.len() - 2);
} else if (msg.startsWith(HighLevelEncoder.MACRO_06_HEADER) && msg.endsWith(HighLevelEncoder.MACRO_TRAILER)) { msg = &msg[high_level_encoder::MACRO_05_HEADER.len()..(msg.len() - 2)];
} else if msg.starts_with(high_level_encoder::MACRO_06_HEADER) && msg.ends_with(high_level_encoder::MACRO_TRAILER) {
macroId = 6; macroId = 6;
msg = msg.substring(HighLevelEncoder.MACRO_06_HEADER.length(), msg.length() - 2); // msg = msg.substring(high_level_encoder::MACRO_06_HEADER.len(), msg.len() - 2);
msg = &msg[high_level_encoder::MACRO_06_HEADER.len()..(msg.len() - 2)];
} }
return new String(encode(msg, priorityCharset, fnc1, shape, macroId), StandardCharsets.ISO_8859_1); Ok(ISO_8859_1_ENCODER.decode(&encode(msg, priorityCharset, fnc1, shape, macroId)?, encoding::DecoderTrap::Strict).expect("should decode").to_owned())
// return new String(encode(msg, priorityCharset, fnc1, shape, macroId), StandardCharsets.ISO_8859_1);
} }
/** /**
@@ -149,111 +177,119 @@ public final class MinimalEncoder {
* @param macroId Prepends the specified macro function in case that a value of 5 or 6 is specified. * @param macroId Prepends the specified macro function in case that a value of 5 or 6 is specified.
* @return An array of bytes representing the codewords of a minimal encoding. * @return An array of bytes representing the codewords of a minimal encoding.
*/ */
static byte[] encode(String input, Charset priorityCharset, int fnc1, SymbolShapeHint shape, int macroId) { fn encode( input:&str, priorityCharset:Option<EncodingRef>, fnc1:Option<char>, shape:SymbolShapeHint, macroId:i32) -> Result<Vec<u8>,Exceptions> {
return encodeMinimally(new Input(input, priorityCharset, fnc1, shape, macroId)).getBytes(); Ok(encodeMinimally( &Input::new(input, priorityCharset, fnc1, shape, macroId))?.getBytes().to_vec())
} }
static void addEdge(Edge[][] edges, Edge edge) { fn addEdge( edges:&Vec<Vec<Option<Rc<Edge>>>>, edge:Rc<Edge>) {
int vertexIndex = edge.fromPosition + edge.characterLength; let vertexIndex = (edge.fromPosition + edge.characterLength) as usize;
if (edges[vertexIndex][edge.getEndMode().ordinal()] == null || if edges[vertexIndex][edge.getEndMode().ordinal()].is_none() ||
edges[vertexIndex][edge.getEndMode().ordinal()].cachedTotalSize > edge.cachedTotalSize) { edges[vertexIndex][edge.getEndMode().ordinal()].as_ref().unwrap().cachedTotalSize > edge.cachedTotalSize {
edges[vertexIndex][edge.getEndMode().ordinal()] = edge; edges[vertexIndex][edge.getEndMode().ordinal()] = Some(edge.clone());
} }
} }
/** @return the number of words in which the string starting at from can be encoded in c40 or text mode. /** @return the number of words in which the string starting at from can be encoded in c40 or text Mode::
* The number of characters encoded is returned in characterLength. * The number of characters encoded is returned in characterLength.
* The number of characters encoded is also minimal in the sense that the algorithm stops as soon * The number of characters encoded is also minimal in the sense that the algorithm stops as soon
* as a character encoding fills a C40 word competely (three C40 values). An exception is at the * as a character encoding fills a C40 word competely (three C40 values). An exception is at the
* end of the string where two C40 values are allowed (according to the spec the third c40 value * end of the string where two C40 values are allowed (according to the spec the third c40 value
* is filled with 0 (Shift 1) in this case). * is filled with 0 (Shift 1) in this case).
*/ */
static int getNumberOfC40Words(Input input, int from, boolean c40,int[] characterLength) { fn getNumberOfC40Words( input:&Input, from:u32, c40:bool, characterLength:&[u32]) -> Result<u32,Exceptions>{
int thirdsCount = 0; let thirdsCount = 0;
for (int i = from; i < input.length(); i++) { for i in (from as usize)..input.length() {
if (input.isECI(i)) { // for (int i = from; i < input.length(); i++) {
if input.isECI(i as u32)? {
characterLength[0] = 0; characterLength[0] = 0;
return 0; return Ok(0);
} }
char ci = input.charAt(i); let ci = input.charAt(i)?;
if (c40 && HighLevelEncoder.isNativeC40(ci) || !c40 && HighLevelEncoder.isNativeText(ci)) { if c40 && high_level_encoder::isNativeC40(ci) || !c40 && high_level_encoder::isNativeText(ci) {
thirdsCount++; //native thirdsCount+=1; //native
} else if (!isExtendedASCII(ci, input.getFNC1Character())) { } else if !isExtendedASCII(ci, Some(input.getFNC1Character())) {
thirdsCount += 2; //shift thirdsCount += 2; //shift
} else { } else {
int asciiValue = ci & 0xff; let asciiValue = ci as u8 & 0xff;
if (asciiValue >= 128 && (c40 && HighLevelEncoder.isNativeC40((char) (asciiValue - 128)) || if asciiValue >= 128 && (c40 && high_level_encoder::isNativeC40( (asciiValue - 128) as char) ||
!c40 && HighLevelEncoder.isNativeText((char) (asciiValue - 128)))) { !c40 && high_level_encoder::isNativeText( (asciiValue - 128) as char)) {
thirdsCount += 3; // shift, Upper shift thirdsCount += 3; // shift, Upper shift
} else { } else {
thirdsCount += 4; // shift, Upper shift, shift thirdsCount += 4; // shift, Upper shift, shift
} }
} }
if (thirdsCount % 3 == 0 || ((thirdsCount - 2) % 3 == 0 && i + 1 == input.length())) { if thirdsCount % 3 == 0 || ((thirdsCount - 2) % 3 == 0 && i + 1 == input.length()) {
characterLength[0] = i - from + 1; characterLength[0] = i as u32 - from + 1;
return (int) Math.ceil(((double) thirdsCount) / 3.0); // return (int) Math.ceil(((double) thirdsCount) / 3.0);
return Ok((( thirdsCount as f64) / 3.0).ceil() as u32);
} }
} }
characterLength[0] = 0; characterLength[0] = 0;
return 0;
Ok(0)
} }
static void addEdges(Input input, Edge[][] edges, int from, Edge previous) { fn addEdges( input:&Input, edges:&Vec<Vec<Option<Rc<Edge>>>>, from:u32, previous:Option<Rc<Edge>>) -> Result<(),Exceptions> {
if (input.isECI(from)) { if input.isECI(from)? {
addEdge(edges, new Edge(input, Mode.ASCII, from, 1, previous)); addEdge(edges, Rc::new(Edge::new(input, Mode::ASCII, from, 1, previous.clone())));
return; return Ok(());
} }
char ch = input.charAt(from); let ch = input.charAt(from as usize)?;
if (previous == null || previous.getEndMode() != Mode.EDF) { //not possible to unlatch a full EDF edge to something if previous.is_none() || previous.as_ref().unwrap().getEndMode() != Mode::EDF { //not possible to unlatch a full EDF edge to something
//else //else
if (HighLevelEncoder.isDigit(ch) && input.haveNCharacters(from, 2) && if high_level_encoder::isDigit(ch) && input.haveNCharacters(from as usize, 2) &&
HighLevelEncoder.isDigit(input.charAt(from + 1))) { high_level_encoder::isDigit(input.charAt(from as usize + 1)?) {
// two digits ASCII encoded // two digits ASCII encoded
addEdge(edges, new Edge(input, Mode.ASCII, from, 2, previous)); addEdge(edges, Rc::new(Edge::new(input, Mode::ASCII, from, 2, previous.clone())));
} else { } else {
// one ASCII encoded character or an extended character via Upper Shift // one ASCII encoded character or an extended character via Upper Shift
addEdge(edges, new Edge(input, Mode.ASCII, from, 1, previous)); addEdge(edges, Rc::new(Edge::new(input, Mode::ASCII, from, 1, previous.clone())));
} }
Mode[] modes = {Mode.C40, Mode.TEXT}; let modes = [Mode::C40, Mode::TEXT];
for (Mode mode : modes) { for mode in modes {
int[] characterLength = new int[1]; // for (Mode mode : modes) {
if (getNumberOfC40Words(input, from, mode == Mode.C40, characterLength) > 0) { let characterLength = [0u32;1];
addEdge(edges, new Edge(input, mode, from, characterLength[0], previous)); if getNumberOfC40Words(input, from, mode == Mode::C40, &characterLength)? > 0 {
addEdge(edges, Rc::new(Edge::new(input, mode, from, characterLength[0], previous.clone())));
} }
} }
if (input.haveNCharacters(from,3) && if input.haveNCharacters(from as usize,3) &&
HighLevelEncoder.isNativeX12(input.charAt(from)) && high_level_encoder::isNativeX12(input.charAt(from as usize)?) &&
HighLevelEncoder.isNativeX12(input.charAt(from + 1)) && high_level_encoder::isNativeX12(input.charAt(from as usize+ 1)?) &&
HighLevelEncoder.isNativeX12(input.charAt(from + 2))) { high_level_encoder::isNativeX12(input.charAt(from as usize + 2)?) {
addEdge(edges, new Edge(input, Mode.X12, from, 3, previous)); addEdge(edges, Rc::new(Edge::new(input, Mode::X12, from, 3, previous.clone())));
} }
addEdge(edges, new Edge(input, Mode.B256, from, 1, previous)); addEdge(edges, Rc::new(Edge::new(input, Mode::B256, from, 1, previous.clone())));
} }
//We create 4 EDF edges, with 1, 2 3 or 4 characters length. The fourth normally doesn't have a latch to ASCII //We create 4 EDF edges, with 1, 2 3 or 4 characters length. The fourth normally doesn't have a latch to ASCII
//unless it is 2 characters away from the end of the input. //unless it is 2 characters away from the end of the input.
int i; let i = 0u32;
for (i = 0; i < 3; i++) { while i < 3 {
int pos = from + i; // for (i = 0; i < 3; i++) {
if (input.haveNCharacters(pos,1) && HighLevelEncoder.isNativeEDIFACT(input.charAt(pos))) { let pos = from + i;
addEdge(edges, new Edge(input, Mode.EDF, from, i + 1, previous)); if input.haveNCharacters(pos as usize,1) && high_level_encoder::isNativeEDIFACT(input.charAt(pos as usize)?) {
addEdge(edges, Rc::new(Edge::new(input, Mode::EDF, from, i + 1, previous.clone())));
} else { } else {
break; break;
} }
i+=1;
} }
if (i == 3 && input.haveNCharacters(from, 4) && HighLevelEncoder.isNativeEDIFACT(input.charAt(from + 3))) { if i == 3 && input.haveNCharacters(from as usize, 4) && high_level_encoder::isNativeEDIFACT(input.charAt(from as usize + 3)?) {
addEdge(edges, new Edge(input, Mode.EDF, from, 4, previous)); addEdge(edges, Rc::new( Edge::new(input, Mode::EDF, from, 4, previous.clone())));
} }
Ok(())
} }
static RXingResult encodeMinimally(Input input) {
@SuppressWarnings("checkstyle:lineLength") fn encodeMinimally( input:&Input) -> Result<RXingResult,Exceptions>{
// @SuppressWarnings("checkstyle:lineLength")
/* The minimal encoding is computed by Dijkstra. The acyclic graph is modeled as follows: /* The minimal encoding is computed by Dijkstra. The acyclic graph is modeled as follows:
* A vertex represents a combination of a position in the input and an encoding mode where position 0 * A vertex represents a combination of a position in the input and an encoding mode where position 0
* denotes the position left of the first character, 1 the position left of the second character and so on. * denotes the position left of the first character, 1 the position left of the second character and so on.
@@ -267,7 +303,7 @@ public final class MinimalEncoder {
* An edge leading to such a vertex encodes one or more of the characters left of the position that the vertex * An edge leading to such a vertex encodes one or more of the characters left of the position that the vertex
* represents. It encodes the characters in the encoding mode of the vertex that it ends on. In other words, * represents. It encodes the characters in the encoding mode of the vertex that it ends on. In other words,
* all edges leading to a particular vertex encode the same characters (the length of the suffix can vary) using the same * all edges leading to a particular vertex encode the same characters (the length of the suffix can vary) using the same
* encoding mode. * encoding Mode::
* As an example consider the input string "ABC123" and the vertex (4,EDF). Possible edges leading to this vertex * As an example consider the input string "ABC123" and the vertex (4,EDF). Possible edges leading to this vertex
* are: * are:
* (0,ASCII) --EDF(ABC1)--> (4,EDF) * (0,ASCII) --EDF(ABC1)--> (4,EDF)
@@ -438,59 +474,68 @@ public final class MinimalEncoder {
* Hence a minimal encoding of "ABCDEFG" is either ASCII(A),C40(BCDEFG) or ASCII(A), X12(BCDEFG) with a size of 5 bytes. * Hence a minimal encoding of "ABCDEFG" is either ASCII(A),C40(BCDEFG) or ASCII(A), X12(BCDEFG) with a size of 5 bytes.
*/ */
int inputLength = input.length(); let inputLength = input.length();
// Array that represents vertices. There is a vertex for every character and mode. // Array that represents vertices. There is a vertex for every character and Mode::
// The last dimension in the array below encodes the 6 modes ASCII, C40, TEXT, X12, EDF and B256 // The last dimension in the array below encodes the 6 modes ASCII, C40, TEXT, X12, EDF and B256
Edge[][] edges = new Edge[inputLength + 1][6]; // let edges = new Edge[inputLength + 1][6];
addEdges(input, edges, 0, null); let edges = vec![vec![None;6];inputLength + 1];
addEdges(input, &edges, 0, None);
for (int i = 1; i <= inputLength; i++) { for i in 1..=inputLength {
for (int j = 0; j < 6; j++) { // for (int i = 1; i <= inputLength; i++) {
if (edges[i][j] != null && i < inputLength) { for j in 0..6 {
addEdges(input, edges, i, edges[i][j]); // for (int j = 0; j < 6; j++) {
if edges[i][j].is_some() && i < inputLength {
addEdges(input, &edges, i as u32, edges[i][j]);
} }
} }
//optimize memory by removing edges that have been passed. //optimize memory by removing edges that have been passed.
for (int j = 0; j < 6; j++) { for j in 0..6 {
edges[i - 1][j] = null; // for (int j = 0; j < 6; j++) {
edges[i - 1][j] = None;
} }
} }
int minimalJ = -1; let minimalJ:i32 = -1;
int minimalSize = Integer.MAX_VALUE; let minimalSize = i32::MAX;
for (int j = 0; j < 6; j++) { for j in 0..6 {
if (edges[inputLength][j] != null) { // for (int j = 0; j < 6; j++) {
Edge edge = edges[inputLength][j]; if edges[inputLength][j].is_some() {
int size = j >= 1 && j <= 3 ? edge.cachedTotalSize + 1 : edge.cachedTotalSize; //C40, TEXT and X12 need an let edge = edges[inputLength][j].as_ref().unwrap();
let size = if j >= 1 && j <= 3 {edge.cachedTotalSize + 1} else {edge.cachedTotalSize}; //C40, TEXT and X12 need an
// extra unlatch at the end // extra unlatch at the end
if (size < minimalSize) { if (size as i32) < minimalSize {
minimalSize = size; minimalSize = size as i32;
minimalJ = j; minimalJ = j as i32;
} }
} }
} }
if (minimalJ < 0) { if minimalJ < 0 {
throw new RuntimeException("Internal error: failed to encode \"" + input + "\""); return Err(Exceptions::RuntimeException(format!("Internal error: failed to encode \"{}\"",input)));
} }
return new RXingResult(edges[inputLength][minimalJ]); Ok(RXingResult::new(edges[inputLength][minimalJ as usize].clone().unwrap()))
} }
private static final class Edge { const allCodewordCapacities : [u32;28] = [3, 5, 8, 10, 12, 16, 18, 22, 30, 32, 36, 44, 49, 62, 86, 114,
private static final int[] allCodewordCapacities = {3, 5, 8, 10, 12, 16, 18, 22, 30, 32, 36, 44, 49, 62, 86, 114, 144, 174, 204, 280, 368, 456, 576, 696, 816, 1050, 1304, 1558];
144, 174, 204, 280, 368, 456, 576, 696, 816, 1050, 1304, 1558}; const squareCodewordCapacities : [u32;24]= [3, 5, 8, 12, 18, 22, 30, 36, 44, 62, 86, 114, 144, 174, 204,
private static final int[] squareCodewordCapacities = {3, 5, 8, 12, 18, 22, 30, 36, 44, 62, 86, 114, 144, 174, 204, 280, 368, 456, 576, 696, 816, 1050, 1304, 1558];
280, 368, 456, 576, 696, 816, 1050, 1304, 1558}; const rectangularCodewordCapacities :[u32;6]= [5, 10, 16, 33, 32, 49];
private static final int[] rectangularCodewordCapacities = {5, 10, 16, 33, 32, 49};
private final Input input;
private final Mode mode; //the mode at the start of this edge.
private final int fromPosition;
private final int characterLength;
private final Edge previous;
private final int cachedTotalSize;
private Edge(Input input, Mode mode, int fromPosition, int characterLength, Edge previous) { struct Edge {
input:Input,
mode:Mode, //the mode at the start of this edge.
fromPosition:u32,
characterLength:u32,
previous:Rc<Edge>,
cachedTotalSize:u32,
}
impl Edge{
fn new( input:&Input, mode:Mode, fromPosition:u32, characterLength:u32, previous:Option<Rc<Edge>>) -> Self{
this.input = input; this.input = input;
this.mode = mode; this.mode = mode;
this.fromPosition = fromPosition; this.fromPosition = fromPosition;
@@ -522,52 +567,52 @@ public final class MinimalEncoder {
if (input.isECI(fromPosition) || isExtendedASCII(input.charAt(fromPosition), input.getFNC1Character())) { if (input.isECI(fromPosition) || isExtendedASCII(input.charAt(fromPosition), input.getFNC1Character())) {
size++; size++;
} }
if (previousMode == Mode.C40 || if (previousMode == Mode::C40 ||
previousMode == Mode.TEXT || previousMode == Mode::TEXT ||
previousMode == Mode.X12) { previousMode == Mode::X12) {
size++; // unlatch 254 to ASCII size++; // unlatch 254 to ASCII
} }
break; break;
case B256: case B256:
size++; size++;
if (previousMode != Mode.B256) { if (previousMode != Mode::B256) {
size++; //byte count size++; //byte count
} else if (getB256Size() == 250) { } else if (getB256Size() == 250) {
size++; //extra byte count size++; //extra byte count
} }
if (previousMode == Mode.ASCII) { if (previousMode == Mode::ASCII) {
size++; //latch to B256 size++; //latch to B256
} else if (previousMode == Mode.C40 || } else if (previousMode == Mode::C40 ||
previousMode == Mode.TEXT || previousMode == Mode::TEXT ||
previousMode == Mode.X12) { previousMode == Mode::X12) {
size += 2; //unlatch to ASCII, latch to B256 size += 2; //unlatch to ASCII, latch to B256
} }
break; break;
case C40: case C40:
case TEXT: case TEXT:
case X12: case X12:
if (mode == Mode.X12) { if (mode == Mode::X12) {
size += 2; size += 2;
} else { } else {
int[] charLen = new int[1]; int[] charLen = new int[1];
size += getNumberOfC40Words(input, fromPosition, mode == Mode.C40, charLen) * 2; size += getNumberOfC40Words(input, fromPosition, mode == Mode::C40, charLen) * 2;
} }
if (previousMode == Mode.ASCII || previousMode == Mode.B256) { if (previousMode == Mode::ASCII || previousMode == Mode::B256) {
size++; //additional byte for latch from ASCII to this mode size++; //additional byte for latch from ASCII to this mode
} else if (previousMode != mode && (previousMode == Mode.C40 || } else if (previousMode != mode && (previousMode == Mode::C40 ||
previousMode == Mode.TEXT || previousMode == Mode::TEXT ||
previousMode == Mode.X12)) { previousMode == Mode::X12)) {
size += 2; //unlatch 254 to ASCII followed by latch to this mode size += 2; //unlatch 254 to ASCII followed by latch to this mode
} }
break; break;
case EDF: case EDF:
size += 3; size += 3;
if (previousMode == Mode.ASCII || previousMode == Mode.B256) { if (previousMode == Mode::ASCII || previousMode == Mode::B256) {
size++; //additional byte for latch from ASCII to this mode size++; //additional byte for latch from ASCII to this mode
} else if (previousMode == Mode.C40 || } else if (previousMode == Mode::C40 ||
previousMode == Mode.TEXT || previousMode == Mode::TEXT ||
previousMode == Mode.X12) { previousMode == Mode::X12) {
size += 2; //unlatch 254 to ASCII followed by latch to this mode size += 2; //unlatch 254 to ASCII followed by latch to this mode
} }
break; break;
@@ -576,57 +621,57 @@ public final class MinimalEncoder {
} }
// does not count beyond 250 // does not count beyond 250
int getB256Size() { pub fn getB256Size(&self) -> u32{
int cnt = 0; int cnt = 0;
Edge current = this; Edge current = this;
while (current != null && current.mode == Mode.B256 && cnt <= 250) { while (current != null && current.mode == Mode::B256 && cnt <= 250) {
cnt++; cnt++;
current = current.previous; current = current.previous;
} }
return cnt; return cnt;
} }
Mode getPreviousStartMode() { pub fn getPreviousStartMode(&self) -> Mode{
return previous == null ? Mode.ASCII : previous.mode; return previous == null ? Mode::ASCII : previous.mode;
} }
Mode getPreviousMode() { pub fn getPreviousMode(&self) -> Mode{
return previous == null ? Mode.ASCII : previous.getEndMode(); return previous == null ? Mode::ASCII : previous.getEndMode();
} }
/** Returns Mode.ASCII in case that: /** Returns Mode::ASCII in case that:
* - Mode is EDIFACT and characterLength is less than 4 or the remaining characters can be encoded in at most 2 * - Mode is EDIFACT and characterLength is less than 4 or the remaining characters can be encoded in at most 2
* ASCII bytes. * ASCII bytes.
* - Mode is C40, TEXT or X12 and the remaining characters can be encoded in at most 1 ASCII byte. * - Mode is C40, TEXT or X12 and the remaining characters can be encoded in at most 1 ASCII byte.
* Returns mode in all other cases. * Returns mode in all other cases.
* */ * */
Mode getEndMode() { pub fn getEndMode(&self)->Mode {
if (mode == Mode.EDF) { if (mode == Mode::EDF) {
if (characterLength < 4) { if (characterLength < 4) {
return Mode.ASCII; return Mode::ASCII;
} }
int lastASCII = getLastASCII(); // see 5.2.8.2 EDIFACT encodation Rules int lastASCII = getLastASCII(); // see 5.2.8.2 EDIFACT encodation Rules
if (lastASCII > 0 && getCodewordsRemaining(cachedTotalSize + lastASCII) <= 2 - lastASCII) { if (lastASCII > 0 && getCodewordsRemaining(cachedTotalSize + lastASCII) <= 2 - lastASCII) {
return Mode.ASCII; return Mode::ASCII;
} }
} }
if (mode == Mode.C40 || if (mode == Mode::C40 ||
mode == Mode.TEXT || mode == Mode::TEXT ||
mode == Mode.X12) { mode == Mode::X12) {
// see 5.2.5.2 C40 encodation rules and 5.2.7.2 ANSI X12 encodation rules // see 5.2.5.2 C40 encodation rules and 5.2.7.2 ANSI X12 encodation rules
if (fromPosition + characterLength >= input.length() && getCodewordsRemaining(cachedTotalSize) == 0) { if (fromPosition + characterLength >= input.length() && getCodewordsRemaining(cachedTotalSize) == 0) {
return Mode.ASCII; return Mode::ASCII;
} }
int lastASCII = getLastASCII(); int lastASCII = getLastASCII();
if (lastASCII == 1 && getCodewordsRemaining(cachedTotalSize + 1) == 0) { if (lastASCII == 1 && getCodewordsRemaining(cachedTotalSize + 1) == 0) {
return Mode.ASCII; return Mode::ASCII;
} }
} }
return mode; return mode;
} }
Mode getMode() { pub fn getMode(&self) -> Mode{
return mode; return mode;
} }
@@ -634,7 +679,7 @@ public final class MinimalEncoder {
* two consecutive digits and a non extended character or of 4 digits. * two consecutive digits and a non extended character or of 4 digits.
* Returns 0 in any other case * Returns 0 in any other case
**/ **/
int getLastASCII() { pub fn getLastASCII(&self) -> u32{
int length = input.length(); int length = input.length();
int from = fromPosition + characterLength; int from = fromPosition + characterLength;
if (length - from > 4 || from >= length) { if (length - from > 4 || from >= length) {
@@ -651,24 +696,24 @@ public final class MinimalEncoder {
input.getFNC1Character())) { input.getFNC1Character())) {
return 0; return 0;
} }
if (HighLevelEncoder.isDigit(input.charAt(from)) && HighLevelEncoder.isDigit(input.charAt(from + 1))) { if (high_level_encoder::isDigit(input.charAt(from)) && high_level_encoder::isDigit(input.charAt(from + 1))) {
return 1; return 1;
} }
return 2; return 2;
} }
if (length - from == 3) { if (length - from == 3) {
if (HighLevelEncoder.isDigit(input.charAt(from)) && HighLevelEncoder.isDigit(input.charAt(from + 1)) if (high_level_encoder::isDigit(input.charAt(from)) && high_level_encoder::isDigit(input.charAt(from + 1))
&& !isExtendedASCII(input.charAt(from + 2), input.getFNC1Character())) { && !isExtendedASCII(input.charAt(from + 2), input.getFNC1Character())) {
return 2; return 2;
} }
if (HighLevelEncoder.isDigit(input.charAt(from + 1)) && HighLevelEncoder.isDigit(input.charAt(from + 2)) if (high_level_encoder::isDigit(input.charAt(from + 1)) && high_level_encoder::isDigit(input.charAt(from + 2))
&& !isExtendedASCII(input.charAt(from), input.getFNC1Character())) { && !isExtendedASCII(input.charAt(from), input.getFNC1Character())) {
return 2; return 2;
} }
return 0; return 0;
} }
if (HighLevelEncoder.isDigit(input.charAt(from)) && HighLevelEncoder.isDigit(input.charAt(from + 1)) if (high_level_encoder::isDigit(input.charAt(from)) && high_level_encoder::isDigit(input.charAt(from + 1))
&& HighLevelEncoder.isDigit(input.charAt(from + 2)) && HighLevelEncoder.isDigit(input.charAt(from + 3))) { && high_level_encoder::isDigit(input.charAt(from + 2)) && high_level_encoder::isDigit(input.charAt(from + 3))) {
return 2; return 2;
} }
return 0; return 0;
@@ -677,7 +722,7 @@ public final class MinimalEncoder {
/** Returns the capacity in codewords of the smallest symbol that has enough capacity to fit the given minimal /** Returns the capacity in codewords of the smallest symbol that has enough capacity to fit the given minimal
* number of codewords. * number of codewords.
**/ **/
int getMinSymbolSize(int minimum) { pub fn getMinSymbolSize(&self, minimum:u32) -> u32{
switch (input.getShapeHint()) { switch (input.getShapeHint()) {
case FORCE_SQUARE: case FORCE_SQUARE:
for (int capacity : squareCodewordCapacities) { for (int capacity : squareCodewordCapacities) {
@@ -705,30 +750,30 @@ public final class MinimalEncoder {
/** Returns the remaining capacity in codewords of the smallest symbol that has enough capacity to fit the given /** Returns the remaining capacity in codewords of the smallest symbol that has enough capacity to fit the given
* minimal number of codewords. * minimal number of codewords.
**/ **/
int getCodewordsRemaining(int minimum) { pub fn getCodewordsRemaining( minimum:u32) -> u32{
return getMinSymbolSize(minimum) - minimum; return getMinSymbolSize(minimum) - minimum;
} }
static byte[] getBytes(int c) { pub fn getBytes1( c:u32) -> Vec<u8>{
byte[] result = new byte[1]; byte[] result = new byte[1];
result[0] = (byte) c; result[0] = (byte) c;
return result; return result;
} }
static byte[] getBytes(int c1,int c2) { pub fn getBytes2( c1:u32, c2:u32) -> Vec<u8>{
byte[] result = new byte[2]; byte[] result = new byte[2];
result[0] = (byte) c1; result[0] = (byte) c1;
result[1] = (byte) c2; result[1] = (byte) c2;
return result; return result;
} }
static void setC40Word(byte[] bytes, int offset, int c1, int c2, int c3) { pub fn setC40Word( bytes:&[u8], offset:u32, c1:u32, c2:u32, c3:u32) {
int val16 = (1600 * (c1 & 0xff)) + (40 * (c2 & 0xff)) + (c3 & 0xff) + 1; int val16 = (1600 * (c1 & 0xff)) + (40 * (c2 & 0xff)) + (c3 & 0xff) + 1;
bytes[offset] = (byte) (val16 / 256); bytes[offset] = (byte) (val16 / 256);
bytes[offset + 1] = (byte) (val16 % 256); bytes[offset + 1] = (byte) (val16 % 256);
} }
private static int getX12Value(char c) { fn getX12Value( c:char) -> u32{
return c == 13 ? 0 : return c == 13 ? 0 :
c == 42 ? 1 : c == 42 ? 1 :
c == 62 ? 2 : c == 62 ? 2 :
@@ -737,7 +782,7 @@ public final class MinimalEncoder {
c >= 65 && c <= 90 ? c - 51 : c; c >= 65 && c <= 90 ? c - 51 : c;
} }
byte[] getX12Words() { pub fn getX12Words(&self) -> Vec<u8> {
assert characterLength % 3 == 0; assert characterLength % 3 == 0;
byte[] result = new byte[characterLength / 3 * 2]; byte[] result = new byte[characterLength / 3 * 2];
for (int i = 0; i < result.length; i += 2) { for (int i = 0; i < result.length; i += 2) {
@@ -748,14 +793,14 @@ public final class MinimalEncoder {
return result; return result;
} }
static int getShiftValue(char c, boolean c40, int fnc1) { pub fn getShiftValue( c:char, c40:bool, fnc1:Option<char>) -> u32{
return (c40 && isInC40Shift1Set(c) || return (c40 && isInC40Shift1Set(c) ||
!c40 && isInTextShift1Set(c)) ? 0 : !c40 && isInTextShift1Set(c)) ? 0 :
(c40 && isInC40Shift2Set(c, fnc1) || (c40 && isInC40Shift2Set(c, fnc1) ||
!c40 && isInTextShift2Set(c, fnc1)) ? 1 : 2; !c40 && isInTextShift2Set(c, fnc1)) ? 1 : 2;
} }
private static int getC40Value(boolean c40, int setIndex, char c, int fnc1) { fn getC40Value( c40:bool, setIndex:u32, c:char, fnc1:Option<char>) -> u32{
if (c == fnc1) { if (c == fnc1) {
assert setIndex == 2; assert setIndex == 2;
return 27; return 27;
@@ -785,11 +830,11 @@ public final class MinimalEncoder {
} }
} }
byte[] getC40Words(boolean c40, int fnc1) { pub fn getC40Words(&self, c40:bool, fnc1:Option<char>) -> Vec<u8>{
List<Byte> c40Values = new ArrayList<>(); List<Byte> c40Values = new ArrayList<>();
for (int i = 0; i < characterLength; i++) { for (int i = 0; i < characterLength; i++) {
char ci = input.charAt(fromPosition + i); char ci = input.charAt(fromPosition + i);
if (c40 && HighLevelEncoder.isNativeC40(ci) || !c40 && HighLevelEncoder.isNativeText(ci)) { if (c40 && high_level_encoder::isNativeC40(ci) || !c40 && high_level_encoder::isNativeText(ci)) {
c40Values.add((byte) getC40Value(c40, 0, ci, fnc1)); c40Values.add((byte) getC40Value(c40, 0, ci, fnc1));
} else if (!isExtendedASCII(ci, fnc1)) { } else if (!isExtendedASCII(ci, fnc1)) {
int shiftValue = getShiftValue(ci, c40, fnc1); int shiftValue = getShiftValue(ci, c40, fnc1);
@@ -797,8 +842,8 @@ public final class MinimalEncoder {
c40Values.add((byte) getC40Value(c40, shiftValue, ci, fnc1)); c40Values.add((byte) getC40Value(c40, shiftValue, ci, fnc1));
} else { } else {
char asciiValue = (char) ((ci & 0xff) - 128); char asciiValue = (char) ((ci & 0xff) - 128);
if (c40 && HighLevelEncoder.isNativeC40(asciiValue) || if (c40 && high_level_encoder::isNativeC40(asciiValue) ||
!c40 && HighLevelEncoder.isNativeText(asciiValue)) { !c40 && high_level_encoder::isNativeText(asciiValue)) {
c40Values.add((byte) 1); //Shift 2 c40Values.add((byte) 1); //Shift 2
c40Values.add((byte) 30); //Upper Shift c40Values.add((byte) 30); //Upper Shift
c40Values.add((byte) getC40Value(c40, 0, asciiValue, fnc1)); c40Values.add((byte) getC40Value(c40, 0, asciiValue, fnc1));
@@ -826,7 +871,7 @@ public final class MinimalEncoder {
return result; return result;
} }
byte[] getEDFBytes() { pub fn getEDFBytes(&self) -> Vec<u8> {
int numberOfThirds = (int) Math.ceil(characterLength / 4.0); int numberOfThirds = (int) Math.ceil(characterLength / 4.0);
byte[] result = new byte[numberOfThirds * 3]; byte[] result = new byte[numberOfThirds * 3];
int pos = fromPosition; int pos = fromPosition;
@@ -851,7 +896,7 @@ public final class MinimalEncoder {
return result; return result;
} }
byte[] getLatchBytes() { pub fn getLatchBytes(&self) -> Vec<u8> {
switch (getPreviousMode()) { switch (getPreviousMode()) {
case ASCII: case ASCII:
case B256: //after B256 ends (via length) we are back to ASCII case B256: //after B256 ends (via length) we are back to ASCII
@@ -889,14 +934,14 @@ public final class MinimalEncoder {
} }
break; break;
case EDF: case EDF:
assert mode == Mode.EDF; //The rightmost EDIFACT edge always contains an unlatch character assert mode == Mode::EDF; //The rightmost EDIFACT edge always contains an unlatch character
break; break;
} }
return new byte[0]; return new byte[0];
} }
// Important: The function does not return the length bytes (one or two) in case of B256 encoding // Important: The function does not return the length bytes (one or two) in case of B256 encoding
byte[] getDataBytes() { pub fn getDataBytes(&self) -> Vec<u8> {
switch (mode) { switch (mode) {
case ASCII: case ASCII:
if (input.isECI(fromPosition)) { if (input.isECI(fromPosition)) {
@@ -926,28 +971,30 @@ public final class MinimalEncoder {
} }
} }
private static final class RXingResult { struct RXingResult {
private final byte[] bytes; bytes:Vec<u8>,
}
impl RXingResult{
RXingResult(Edge solution) { pub fn new( solution:Rc<Edge>) -> Self {
Input input = solution.input; let input = solution.input;
int size = 0; let size = 0;
List<Byte> bytesAL = new ArrayList<>(); let bytesAL = new ArrayList<>();
List<Integer> randomizePostfixLength = new ArrayList<>(); let randomizePostfixLength = new ArrayList<>();
List<Integer> randomizeLengths = new ArrayList<>(); let randomizeLengths = new ArrayList<>();
if ((solution.mode == Mode.C40 || if ((solution.mode == Mode::C40 ||
solution.mode == Mode.TEXT || solution.mode == Mode::TEXT ||
solution.mode == Mode.X12) && solution.mode == Mode::X12) &&
solution.getEndMode() != Mode.ASCII) { solution.getEndMode() != Mode::ASCII) {
size += prepend(MinimalEncoder.Edge.getBytes(254),bytesAL); size += prepend(Edge::getBytes(254),bytesAL);
} }
Edge current = solution; let current = solution;
while (current != null) { while (current != null) {
size += prepend(current.getDataBytes(),bytesAL); size += prepend(current.getDataBytes(),bytesAL);
if (current.previous == null || current.getPreviousStartMode() != current.getMode()) { if (current.previous == null || current.getPreviousStartMode() != current.getMode()) {
if (current.getMode() == Mode.B256) { if (current.getMode() == Mode::B256) {
if (size <= 249) { if (size <= 249) {
bytesAL.add(0, (byte) size); bytesAL.add(0, (byte) size);
size++; size++;
@@ -992,53 +1039,83 @@ public final class MinimalEncoder {
} }
} }
static int prepend(byte[] bytes, List<Byte> into) { pub fn prepend(bytes:&[u8], into:&[u8]) -> u32{
for (int i = bytes.length - 1; i >= 0; i--) { for (int i = bytes.length - 1; i >= 0; i--) {
into.add(0, bytes[i]); into.add(0, bytes[i]);
} }
return bytes.length; return bytes.length;
} }
private static int randomize253State(int codewordPosition) { fn randomize253State( codewordPosition:u32) -> u32{
int pseudoRandom = ((149 * codewordPosition) % 253) + 1; let pseudoRandom = ((149 * codewordPosition) % 253) + 1;
int tempVariable = 129 + pseudoRandom; let tempVariable = 129 + pseudoRandom;
return tempVariable <= 254 ? tempVariable : tempVariable - 254; return tempVariable <= 254 ? tempVariable : tempVariable - 254;
} }
static void applyRandomPattern(List<Byte> bytesAL,int startPosition, int length) { pub fn applyRandomPattern(bytesAL:&[u8], startPosition:u32, length:u32) {
for (int i = 0; i < length; i++) { for i in 0..length {
// for (int i = 0; i < length; i++) {
//See "B.1 253-state algorithm //See "B.1 253-state algorithm
int Pad_codeword_position = startPosition + i; let Pad_codeword_position = startPosition + i;
int Pad_codeword_value = bytesAL.get(Pad_codeword_position) & 0xff; let Pad_codeword_value = bytesAL.get(Pad_codeword_position) & 0xff;
int pseudo_random_number = ((149 * (Pad_codeword_position + 1)) % 255) + 1; let pseudo_random_number = ((149 * (Pad_codeword_position + 1)) % 255) + 1;
int temp_variable = Pad_codeword_value + pseudo_random_number; let temp_variable = Pad_codeword_value + pseudo_random_number;
bytesAL.set(Pad_codeword_position, (byte) (temp_variable <= 255 ? temp_variable : temp_variable - 256)); bytesAL.set(Pad_codeword_position, (byte) (temp_variable <= 255 ? temp_variable : temp_variable - 256));
} }
} }
public byte[] getBytes() { pub fn getBytes(&self) -> &[u8] {
return bytes; &self.bytes
} }
} }
private static final class Input extends MinimalECIInput { struct Input {
shape: SymbolShapeHint,
macroId:i32,
internal: MinimalECIInput
}
private final SymbolShapeHint shape; impl Input{
private final int macroId;
private Input(String stringToEncode, Charset priorityCharset, int fnc1, SymbolShapeHint shape, int macroId) { pub fn new( stringToEncode:&str, priorityCharset:Option<EncodingRef>, fnc1:Option<char>, shape:SymbolShapeHint, macroId:i32) -> Self{
super(stringToEncode, priorityCharset, fnc1); Self {
this.shape = shape; shape,
this.macroId = macroId; macroId,
internal: MinimalECIInput::new(stringToEncode, priorityCharset, if fnc1 >= 0 {Some(&(fnc1 as u8 as char).to_string())} else {None})
}
// super(stringToEncode, priorityCharset, fnc1);
// this.shape = shape;
// this.macroId = macroId;
} }
private int getMacroId() { pub fn getMacroId(&self) -> i32{
return macroId; self.macroId
} }
private SymbolShapeHint getShapeHint() { pub fn getShapeHint(&self) -> SymbolShapeHint{
return shape; self.shape
} }
pub fn length(&self) -> usize {
self.internal.length()
}
pub fn isECI(&self, index: u32) -> Result<bool, Exceptions> {
self.internal.isECI(index)
}
pub fn charAt(&self, index: usize) -> Result<char, Exceptions> {
self.internal.charAt(index)
}
pub fn getFNC1Character(&self) -> char {
self.internal.getFNC1Character() as u8 as char
}
fn haveNCharacters(&self, index: usize, n: usize) -> bool {
self.internal.haveNCharacters(index, n)
} }
} }
impl fmt::Display for Input {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.internal.fmt(f)
}
}

View File

@@ -3,6 +3,7 @@ mod encoder_context;
mod symbol_shape_hint; mod symbol_shape_hint;
mod symbol_info; mod symbol_info;
pub mod high_level_encoder; pub mod high_level_encoder;
pub mod minimal_encoder;
pub use encoder::*; pub use encoder::*;
pub use encoder_context::*; pub use encoder_context::*;
@@ -26,3 +27,6 @@ pub use edifact_encoder::*;
mod base256_encoder; mod base256_encoder;
pub use base256_encoder::*; pub use base256_encoder::*;
// #[cfg(test)]
// mod high_level_encode_test_case;

View File

@@ -22,7 +22,7 @@ use super::SymbolShapeHint;
use lazy_static::lazy_static; use lazy_static::lazy_static;
lazy_static! { lazy_static! {
static ref PROD_SYMBOLS: Vec<SymbolInfo> = vec![ pub(super) static ref PROD_SYMBOLS: Vec<SymbolInfo> = vec![
SymbolInfo::new(false, 3, 5, 8, 8, 1), SymbolInfo::new(false, 3, 5, 8, 8, 1),
SymbolInfo::new(false, 5, 7, 10, 10, 1), SymbolInfo::new(false, 5, 7, 10, 10, 1),
/*rect*/ SymbolInfo::new(true, 5, 7, 16, 6, 1), /*rect*/ SymbolInfo::new(true, 5, 7, 16, 6, 1),