mirror of
https://github.com/starovoid/rxing.git
synced 2026-09-26 09:55:11 +00:00
progress porting minimal endoder
This commit is contained in:
+89
-86
@@ -14,39 +14,41 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing.datamatrix.encoder;
|
||||
use lazy_static::lazy_static;
|
||||
|
||||
import junit.framework.ComparisonFailure;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
use crate::datamatrix::encoder::SymbolInfo;
|
||||
|
||||
use super::{SymbolInfoLookup, symbol_info, high_level_encoder};
|
||||
|
||||
|
||||
lazy_static!{
|
||||
/**
|
||||
* Tests for {@link HighLevelEncoder} and {@link MinimalEncoder}
|
||||
*/
|
||||
public final class HighLevelEncodeTestCase extends Assert {
|
||||
|
||||
private static final SymbolInfo[] TEST_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, 13, 0, 0, 0, 1),
|
||||
new SymbolInfo(false, 77, 0, 0, 0, 1)
|
||||
static ref TEST_SYMBOLS :Vec<SymbolInfo>= 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, 13, 0, 0, 0, 1),
|
||||
SymbolInfo::new(false, 77, 0, 0, 0, 1)
|
||||
//The last entries are fake entries to test special conditions with C40 encoding
|
||||
};
|
||||
|
||||
private static void useTestSymbols() {
|
||||
SymbolInfo.overrideSymbolSet(TEST_SYMBOLS);
|
||||
];
|
||||
}
|
||||
|
||||
private static void resetSymbols() {
|
||||
SymbolInfo.overrideSymbolSet(SymbolInfo.PROD_SYMBOLS);
|
||||
fn useTestSymbols(lookup:SymbolInfoLookup) -> SymbolInfoLookup {
|
||||
lookup.overrideSymbolSet(&TEST_SYMBOLS);
|
||||
lookup
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testASCIIEncodation() {
|
||||
fn resetSymbols(lookup:SymbolInfoLookup) -> SymbolInfoLookup {
|
||||
lookup.overrideSymbolSet(&symbol_info::PROD_SYMBOLS);
|
||||
lookup
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn testASCIIEncodation() {
|
||||
|
||||
String visualized = encodeHighLevel("123456");
|
||||
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);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testC40EncodationBasic1() {
|
||||
#[test]
|
||||
fn testC40EncodationBasic1() {
|
||||
|
||||
String visualized = encodeHighLevel("AIMAIMAIM");
|
||||
assertEquals("230 91 11 91 11 91 11 254", visualized);
|
||||
//230 shifts to C40 encodation, 254 unlatches, "else" case
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testC40EncodationBasic2() {
|
||||
#[test]
|
||||
fn testC40EncodationBasic2() {
|
||||
|
||||
String visualized = encodeHighLevel("AIMAIAB");
|
||||
assertEquals("230 91 11 90 255 254 67 129", visualized);
|
||||
@@ -98,15 +100,15 @@ public final class HighLevelEncodeTestCase extends Assert {
|
||||
//"else" case
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testC40EncodationSpecExample() {
|
||||
#[test]
|
||||
fn testC40EncodationSpecExample() {
|
||||
//Example in Figure 1 in the spec
|
||||
String visualized = encodeHighLevel("A1B2C3D4E5F6G7H8I9J0K1L2");
|
||||
assertEquals("230 88 88 40 8 107 147 59 67 126 206 78 126 144 121 35 47 254", visualized);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testC40EncodationSpecialCases1() {
|
||||
#[test]
|
||||
fn testC40EncodationSpecialCases1() {
|
||||
|
||||
//Special tests avoiding ultra-long test strings because these tests are only used
|
||||
//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
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testC40EncodationSpecialCases2() {
|
||||
#[test]
|
||||
fn testC40EncodationSpecialCases2() {
|
||||
|
||||
String visualized = encodeHighLevel("AIMAIMAIMAIMAIMAIMAI");
|
||||
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
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTextEncodation() {
|
||||
#[test]
|
||||
fn testTextEncodation() {
|
||||
|
||||
String visualized = encodeHighLevel("aimaimaim");
|
||||
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);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testX12Encodation() {
|
||||
#[test]
|
||||
fn testX12Encodation() {
|
||||
|
||||
//238 shifts to X12 encodation, 254 unlatches
|
||||
|
||||
@@ -186,8 +188,8 @@ public final class HighLevelEncodeTestCase extends Assert {
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEDIFACTEncodation() {
|
||||
#[test]
|
||||
fn testEDIFACTEncodation() {
|
||||
|
||||
//240 shifts to EDIFACT encodation
|
||||
|
||||
@@ -221,8 +223,8 @@ public final class HighLevelEncodeTestCase extends Assert {
|
||||
visualized);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBase256Encodation() {
|
||||
#[test]
|
||||
fn testBase256Encodation() {
|
||||
|
||||
//231 shifts to Base256 encodation
|
||||
|
||||
@@ -259,7 +261,7 @@ public final class HighLevelEncodeTestCase extends Assert {
|
||||
assertEndsWith("146 40 190 87", visualized);
|
||||
}
|
||||
|
||||
private static String createBinaryMessage(int len) {
|
||||
fn createBinaryMessage( len:usize) ->String{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("\u00ABäöüéàá-");
|
||||
for (int i = 0; i < len - 9; i++) {
|
||||
@@ -269,41 +271,41 @@ public final class HighLevelEncodeTestCase extends Assert {
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static void assertStartsWith(String expected, String actual) {
|
||||
fn assertStartsWith( expected:&str, actual:&str) {
|
||||
if (!actual.startsWith(expected)) {
|
||||
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)) {
|
||||
throw new ComparisonFailure(null, expected, actual.substring(actual.length() - expected.length()));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUnlatchingFromC40() {
|
||||
#[test]
|
||||
fn testUnlatchingFromC40() {
|
||||
|
||||
String visualized = encodeHighLevel("AIMAIMAIMAIMaimaimaim");
|
||||
assertEquals("230 91 11 91 11 91 11 254 66 74 78 239 91 11 91 11 91 11", visualized);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUnlatchingFromText() {
|
||||
#[test]
|
||||
fn testUnlatchingFromText() {
|
||||
|
||||
String visualized = encodeHighLevel("aimaimaimaim12345678");
|
||||
assertEquals("239 91 11 91 11 91 11 91 11 254 142 164 186 208 129 237", visualized);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHelloWorld() {
|
||||
#[test]
|
||||
fn testHelloWorld() {
|
||||
|
||||
String visualized = encodeHighLevel("Hello World!");
|
||||
assertEquals("73 239 116 130 175 123 148 64 158 233 254 34", visualized);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBug1664266() {
|
||||
#[test]
|
||||
fn testBug1664266() {
|
||||
//There was an exception and the encoder did not handle the unlatching from
|
||||
//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);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testX12Unlatch() {
|
||||
#[test]
|
||||
fn testX12Unlatch() {
|
||||
String visualized = encodeHighLevel("*DTCP01");
|
||||
assertEquals("43 69 85 68 81 131 129 56", visualized);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testX12Unlatch2() {
|
||||
#[test]
|
||||
fn testX12Unlatch2() {
|
||||
String visualized = encodeHighLevel("*DTCP0");
|
||||
assertEquals("238 9 10 104 141", visualized);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBug3048549() {
|
||||
#[test]
|
||||
fn testBug3048549() {
|
||||
//There was an IllegalArgumentException for an illegal character here because
|
||||
//of an encoding problem of the character 0x0060 in Java source code.
|
||||
|
||||
@@ -339,23 +341,23 @@ public final class HighLevelEncodeTestCase extends Assert {
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMacroCharacters() {
|
||||
#[test]
|
||||
fn testMacroCharacters() {
|
||||
|
||||
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("236 185 185 29 196 196 129 56", visualized);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncodingWithStartAsX12AndLatchToEDIFACTInTheMiddle() {
|
||||
#[test]
|
||||
fn testEncodingWithStartAsX12AndLatchToEDIFACTInTheMiddle() {
|
||||
|
||||
String visualized = encodeHighLevel("*MEMANT-1F-MESTECH");
|
||||
assertEquals("240 168 209 77 4 229 45 196 107 77 21 53 5 12 135 192", visualized);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testX12AndEDIFACTSpecErrors() {
|
||||
#[test]
|
||||
fn testX12AndEDIFACTSpecErrors() {
|
||||
//X12 encoding error with spec conform float point comparisons in lookAheadTest()
|
||||
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 " +
|
||||
@@ -380,8 +382,8 @@ public final class HighLevelEncodeTestCase extends Assert {
|
||||
"191 89 191 89 191 254 66 66", visualized);
|
||||
|
||||
}
|
||||
@Test
|
||||
public void testSizes() {
|
||||
#[test]
|
||||
fn testSizes() {
|
||||
int[] sizes = new int[2];
|
||||
encodeHighLevel("A", sizes);
|
||||
assertEquals(3, sizes[0]);
|
||||
@@ -493,8 +495,8 @@ public final class HighLevelEncodeTestCase extends Assert {
|
||||
assertEquals(62, sizes[1]);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testECIs() {
|
||||
#[test]
|
||||
fn testECIs() {
|
||||
|
||||
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 " +
|
||||
@@ -516,8 +518,8 @@ public final class HighLevelEncodeTestCase extends Assert {
|
||||
visualized);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPadding() {
|
||||
#[test]
|
||||
fn testPadding() {
|
||||
int[] sizes = new int[2];
|
||||
encodeHighLevel("IS010000000000000000000000S1118058599124123S21.2.250.1.213.1.4.8 S3FIRST NAMETEST S5MS618-06" +
|
||||
"-1985S713201S4LASTNAMETEST", sizes);
|
||||
@@ -526,20 +528,20 @@ public final class HighLevelEncodeTestCase extends Assert {
|
||||
}
|
||||
|
||||
|
||||
private static void encodeHighLevel(String msg, int[] sizes) {
|
||||
sizes[0] = HighLevelEncoder.encodeHighLevel(msg).length();
|
||||
fn encodeHighLevelWithSizes( msg:&str, sizes:&[u32]) {
|
||||
sizes[0] = high_level_encoder::encodeHighLevel(msg).expect("encodes").len();
|
||||
sizes[1] = MinimalEncoder.encodeHighLevel(msg).length();
|
||||
}
|
||||
|
||||
private static String encodeHighLevel(String msg) {
|
||||
return encodeHighLevel(msg, true);
|
||||
fn encodeHighLevel( msg:&str) ->String{
|
||||
encodeHighLevelCompare(msg, true)
|
||||
}
|
||||
|
||||
private static String encodeHighLevel(String msg, boolean compareSizeToMinimalEncoder) {
|
||||
CharSequence encoded = HighLevelEncoder.encodeHighLevel(msg);
|
||||
CharSequence encoded2 = MinimalEncoder.encodeHighLevel(msg);
|
||||
assertTrue(!compareSizeToMinimalEncoder || encoded2.length() <= encoded.length());
|
||||
return visualize(encoded);
|
||||
fn encodeHighLevelCompare( msg:&str, compareSizeToMinimalEncoder:bool) -> String{
|
||||
let encoded = high_level_encoder::encodeHighLevel(msg).expect("encodes");
|
||||
let encoded2 = high_level_encoder::encodeHighLevel(msg).expect("encodes");
|
||||
assert!(!compareSizeToMinimalEncoder || encoded2.len() <= encoded.len());
|
||||
visualize(&encoded)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -549,15 +551,16 @@ public final class HighLevelEncodeTestCase extends Assert {
|
||||
* @param codewords the codewords
|
||||
* @return the visualized codewords
|
||||
*/
|
||||
static String visualize(CharSequence codewords) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < codewords.length(); i++) {
|
||||
fn visualize( codewords:&str) -> String{
|
||||
let mut sb = String::new();
|
||||
for i in 0..codewords.len() {
|
||||
// for (int i = 0; i < codewords.length(); i++) {
|
||||
if (i > 0) {
|
||||
sb.append(' ');
|
||||
sb.push(' ');
|
||||
}
|
||||
sb.append((int) codewords.charAt(i));
|
||||
}
|
||||
return sb.toString();
|
||||
sb.push_str(&format!("{}", codewords.chars().nth(i).unwrap() as u32);
|
||||
}
|
||||
|
||||
sb
|
||||
}
|
||||
|
||||
@@ -511,15 +511,15 @@ pub fn isExtendedASCII(ch: char) -> bool {
|
||||
(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')
|
||||
}
|
||||
|
||||
fn isNativeText(ch: char) -> bool {
|
||||
pub fn isNativeText(ch: char) -> bool {
|
||||
(ch == ' ') || (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'z')
|
||||
}
|
||||
|
||||
fn isNativeX12(ch: char) -> bool {
|
||||
pub fn isNativeX12(ch: char) -> bool {
|
||||
return isX12TermSep(ch)
|
||||
|| (ch == ' ')
|
||||
|| (ch >= '0' && ch <= '9')
|
||||
@@ -532,7 +532,7 @@ fn isX12TermSep(ch: char) -> bool {
|
||||
|| (ch == '>')
|
||||
}
|
||||
|
||||
fn isNativeEDIFACT(ch: char) -> bool {
|
||||
pub fn isNativeEDIFACT(ch: char) -> bool {
|
||||
ch >= ' ' && ch <= '^'
|
||||
}
|
||||
|
||||
|
||||
+299
-222
@@ -14,14 +14,15 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing.datamatrix.encoder;
|
||||
use std::{rc::Rc, fmt};
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
use encoding::{self,EncodingRef};
|
||||
|
||||
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
|
||||
@@ -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
|
||||
* 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
|
||||
* inefficient results can be produced if the postfix contains no digits.
|
||||
*
|
||||
@@ -58,8 +59,8 @@ import com.google.zxing.common.MinimalECIInput;
|
||||
*
|
||||
* @author Alex Geller
|
||||
*/
|
||||
public final class MinimalEncoder {
|
||||
|
||||
#[derive(Debug,Copy,Clone,PartialEq, Eq)]
|
||||
enum Mode {
|
||||
ASCII,
|
||||
C40,
|
||||
@@ -68,36 +69,59 @@ public final class MinimalEncoder {
|
||||
EDF,
|
||||
B256
|
||||
}
|
||||
static final char[] C40_SHIFT2_CHARS = {'!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/',
|
||||
':', ';', '<', '=', '>', '?', '@', '[', '\\', ']', '^', '_' };
|
||||
|
||||
|
||||
private MinimalEncoder() {
|
||||
impl Mode {
|
||||
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) {
|
||||
return ch != fnc1 && ch >= 128 && ch <= 255;
|
||||
const C40_SHIFT2_CHARS :[char;27] = ['!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/',
|
||||
':', ';', '<', '=', '>', '?', '@', '[', '\\', ']', '^', '_' ];
|
||||
|
||||
|
||||
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) {
|
||||
return ch <= 31;
|
||||
fn isInC40Shift1Set( ch:char) -> bool{
|
||||
ch as u8 <= 31
|
||||
}
|
||||
|
||||
private static boolean isInC40Shift2Set(char ch, int fnc1) {
|
||||
for (char c40Shift2Char : C40_SHIFT2_CHARS) {
|
||||
if (c40Shift2Char == ch) {
|
||||
fn isInC40Shift2Set( ch:char, fnc1:Option<char>) -> bool{
|
||||
for c40Shift2Char in C40_SHIFT2_CHARS {
|
||||
// for (char c40Shift2Char : C40_SHIFT2_CHARS) {
|
||||
if c40Shift2Char == ch {
|
||||
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) {
|
||||
return isInC40Shift1Set(ch);
|
||||
fn isInTextShift1Set( ch:char) -> bool{
|
||||
isInC40Shift1Set(ch)
|
||||
}
|
||||
|
||||
private static boolean isInTextShift2Set(char ch, int fnc1) {
|
||||
return isInC40Shift2Set(ch, fnc1);
|
||||
fn isInTextShift2Set( ch:char, fnc1:Option<char>) -> bool{
|
||||
isInC40Shift2Set(ch, fnc1)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -106,8 +130,8 @@ public final class MinimalEncoder {
|
||||
* @param msg the message
|
||||
* @return the encoded message (the char values range from 0 to 255)
|
||||
*/
|
||||
public static String encodeHighLevel(String msg) {
|
||||
return encodeHighLevel(msg, null, -1, SymbolShapeHint.FORCE_NONE);
|
||||
pub fn encodeHighLevel( msg:&str) -> Result<String,Exceptions>{
|
||||
encodeHighLevelWithDetails(msg, None, None, SymbolShapeHint::FORCE_NONE)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -123,16 +147,20 @@ public final class MinimalEncoder {
|
||||
* @param shape requested shape.
|
||||
* @return the encoded message (the char values range from 0 to 255)
|
||||
*/
|
||||
public static String encodeHighLevel(String msg, Charset priorityCharset, int fnc1, SymbolShapeHint shape) {
|
||||
int macroId = 0;
|
||||
if (msg.startsWith(HighLevelEncoder.MACRO_05_HEADER) && msg.endsWith(HighLevelEncoder.MACRO_TRAILER)) {
|
||||
pub fn encodeHighLevelWithDetails( msg:&str, priorityCharset:Option<EncodingRef>, fnc1:Option<char>, shape:SymbolShapeHint)->Result<String,Exceptions> {
|
||||
let mut msg = msg;
|
||||
let macroId = 0;
|
||||
if msg.starts_with(high_level_encoder::MACRO_05_HEADER) && msg.ends_with(high_level_encoder::MACRO_TRAILER) {
|
||||
macroId = 5;
|
||||
msg = msg.substring(HighLevelEncoder.MACRO_05_HEADER.length(), msg.length() - 2);
|
||||
} else if (msg.startsWith(HighLevelEncoder.MACRO_06_HEADER) && msg.endsWith(HighLevelEncoder.MACRO_TRAILER)) {
|
||||
// msg = msg.substring(high_level_encoder::MACRO_05_HEADER.len(), msg.len() - 2);
|
||||
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;
|
||||
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.
|
||||
* @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) {
|
||||
return encodeMinimally(new Input(input, priorityCharset, fnc1, shape, macroId)).getBytes();
|
||||
fn encode( input:&str, priorityCharset:Option<EncodingRef>, fnc1:Option<char>, shape:SymbolShapeHint, macroId:i32) -> Result<Vec<u8>,Exceptions> {
|
||||
Ok(encodeMinimally( &Input::new(input, priorityCharset, fnc1, shape, macroId))?.getBytes().to_vec())
|
||||
}
|
||||
|
||||
static void addEdge(Edge[][] edges, Edge edge) {
|
||||
int vertexIndex = edge.fromPosition + edge.characterLength;
|
||||
if (edges[vertexIndex][edge.getEndMode().ordinal()] == null ||
|
||||
edges[vertexIndex][edge.getEndMode().ordinal()].cachedTotalSize > edge.cachedTotalSize) {
|
||||
edges[vertexIndex][edge.getEndMode().ordinal()] = edge;
|
||||
fn addEdge( edges:&Vec<Vec<Option<Rc<Edge>>>>, edge:Rc<Edge>) {
|
||||
let vertexIndex = (edge.fromPosition + edge.characterLength) as usize;
|
||||
if edges[vertexIndex][edge.getEndMode().ordinal()].is_none() ||
|
||||
edges[vertexIndex][edge.getEndMode().ordinal()].as_ref().unwrap().cachedTotalSize > edge.cachedTotalSize {
|
||||
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 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
|
||||
* 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).
|
||||
*/
|
||||
static int getNumberOfC40Words(Input input, int from, boolean c40,int[] characterLength) {
|
||||
int thirdsCount = 0;
|
||||
for (int i = from; i < input.length(); i++) {
|
||||
if (input.isECI(i)) {
|
||||
fn getNumberOfC40Words( input:&Input, from:u32, c40:bool, characterLength:&[u32]) -> Result<u32,Exceptions>{
|
||||
let thirdsCount = 0;
|
||||
for i in (from as usize)..input.length() {
|
||||
// for (int i = from; i < input.length(); i++) {
|
||||
if input.isECI(i as u32)? {
|
||||
characterLength[0] = 0;
|
||||
return 0;
|
||||
return Ok(0);
|
||||
}
|
||||
char ci = input.charAt(i);
|
||||
if (c40 && HighLevelEncoder.isNativeC40(ci) || !c40 && HighLevelEncoder.isNativeText(ci)) {
|
||||
thirdsCount++; //native
|
||||
} else if (!isExtendedASCII(ci, input.getFNC1Character())) {
|
||||
let ci = input.charAt(i)?;
|
||||
if c40 && high_level_encoder::isNativeC40(ci) || !c40 && high_level_encoder::isNativeText(ci) {
|
||||
thirdsCount+=1; //native
|
||||
} else if !isExtendedASCII(ci, Some(input.getFNC1Character())) {
|
||||
thirdsCount += 2; //shift
|
||||
} else {
|
||||
int asciiValue = ci & 0xff;
|
||||
if (asciiValue >= 128 && (c40 && HighLevelEncoder.isNativeC40((char) (asciiValue - 128)) ||
|
||||
!c40 && HighLevelEncoder.isNativeText((char) (asciiValue - 128)))) {
|
||||
let asciiValue = ci as u8 & 0xff;
|
||||
if asciiValue >= 128 && (c40 && high_level_encoder::isNativeC40( (asciiValue - 128) as char) ||
|
||||
!c40 && high_level_encoder::isNativeText( (asciiValue - 128) as char)) {
|
||||
thirdsCount += 3; // shift, Upper shift
|
||||
} else {
|
||||
thirdsCount += 4; // shift, Upper shift, shift
|
||||
}
|
||||
}
|
||||
|
||||
if (thirdsCount % 3 == 0 || ((thirdsCount - 2) % 3 == 0 && i + 1 == input.length())) {
|
||||
characterLength[0] = i - from + 1;
|
||||
return (int) Math.ceil(((double) thirdsCount) / 3.0);
|
||||
if thirdsCount % 3 == 0 || ((thirdsCount - 2) % 3 == 0 && i + 1 == input.length()) {
|
||||
characterLength[0] = i as u32 - from + 1;
|
||||
// return (int) Math.ceil(((double) thirdsCount) / 3.0);
|
||||
return Ok((( thirdsCount as f64) / 3.0).ceil() as u32);
|
||||
}
|
||||
}
|
||||
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)) {
|
||||
addEdge(edges, new Edge(input, Mode.ASCII, from, 1, previous));
|
||||
return;
|
||||
if input.isECI(from)? {
|
||||
addEdge(edges, Rc::new(Edge::new(input, Mode::ASCII, from, 1, previous.clone())));
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
char ch = input.charAt(from);
|
||||
if (previous == null || previous.getEndMode() != Mode.EDF) { //not possible to unlatch a full EDF edge to something
|
||||
let ch = input.charAt(from as usize)?;
|
||||
if previous.is_none() || previous.as_ref().unwrap().getEndMode() != Mode::EDF { //not possible to unlatch a full EDF edge to something
|
||||
//else
|
||||
if (HighLevelEncoder.isDigit(ch) && input.haveNCharacters(from, 2) &&
|
||||
HighLevelEncoder.isDigit(input.charAt(from + 1))) {
|
||||
if high_level_encoder::isDigit(ch) && input.haveNCharacters(from as usize, 2) &&
|
||||
high_level_encoder::isDigit(input.charAt(from as usize + 1)?) {
|
||||
// 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 {
|
||||
// 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};
|
||||
for (Mode mode : modes) {
|
||||
int[] characterLength = new int[1];
|
||||
if (getNumberOfC40Words(input, from, mode == Mode.C40, characterLength) > 0) {
|
||||
addEdge(edges, new Edge(input, mode, from, characterLength[0], previous));
|
||||
let modes = [Mode::C40, Mode::TEXT];
|
||||
for mode in modes {
|
||||
// for (Mode mode : modes) {
|
||||
let characterLength = [0u32;1];
|
||||
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) &&
|
||||
HighLevelEncoder.isNativeX12(input.charAt(from)) &&
|
||||
HighLevelEncoder.isNativeX12(input.charAt(from + 1)) &&
|
||||
HighLevelEncoder.isNativeX12(input.charAt(from + 2))) {
|
||||
addEdge(edges, new Edge(input, Mode.X12, from, 3, previous));
|
||||
if input.haveNCharacters(from as usize,3) &&
|
||||
high_level_encoder::isNativeX12(input.charAt(from as usize)?) &&
|
||||
high_level_encoder::isNativeX12(input.charAt(from as usize+ 1)?) &&
|
||||
high_level_encoder::isNativeX12(input.charAt(from as usize + 2)?) {
|
||||
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
|
||||
//unless it is 2 characters away from the end of the input.
|
||||
int i;
|
||||
for (i = 0; i < 3; i++) {
|
||||
int pos = from + i;
|
||||
if (input.haveNCharacters(pos,1) && HighLevelEncoder.isNativeEDIFACT(input.charAt(pos))) {
|
||||
addEdge(edges, new Edge(input, Mode.EDF, from, i + 1, previous));
|
||||
let i = 0u32;
|
||||
while i < 3 {
|
||||
// for (i = 0; i < 3; i++) {
|
||||
let pos = from + i;
|
||||
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 {
|
||||
break;
|
||||
}
|
||||
i+=1;
|
||||
}
|
||||
if (i == 3 && input.haveNCharacters(from, 4) && HighLevelEncoder.isNativeEDIFACT(input.charAt(from + 3))) {
|
||||
addEdge(edges, new Edge(input, Mode.EDF, from, 4, previous));
|
||||
if i == 3 && input.haveNCharacters(from as usize, 4) && high_level_encoder::isNativeEDIFACT(input.charAt(from as usize + 3)?) {
|
||||
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:
|
||||
* 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.
|
||||
@@ -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
|
||||
* 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
|
||||
* encoding mode.
|
||||
* encoding Mode::
|
||||
* As an example consider the input string "ABC123" and the vertex (4,EDF). Possible edges leading to this vertex
|
||||
* are:
|
||||
* (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.
|
||||
*/
|
||||
|
||||
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
|
||||
Edge[][] edges = new Edge[inputLength + 1][6];
|
||||
addEdges(input, edges, 0, null);
|
||||
// let edges = new Edge[inputLength + 1][6];
|
||||
let edges = vec![vec![None;6];inputLength + 1];
|
||||
addEdges(input, &edges, 0, None);
|
||||
|
||||
for (int i = 1; i <= inputLength; i++) {
|
||||
for (int j = 0; j < 6; j++) {
|
||||
if (edges[i][j] != null && i < inputLength) {
|
||||
addEdges(input, edges, i, edges[i][j]);
|
||||
for i in 1..=inputLength {
|
||||
// for (int i = 1; i <= inputLength; i++) {
|
||||
for j in 0..6 {
|
||||
// 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.
|
||||
for (int j = 0; j < 6; j++) {
|
||||
edges[i - 1][j] = null;
|
||||
for j in 0..6 {
|
||||
// for (int j = 0; j < 6; j++) {
|
||||
edges[i - 1][j] = None;
|
||||
}
|
||||
}
|
||||
|
||||
int minimalJ = -1;
|
||||
int minimalSize = Integer.MAX_VALUE;
|
||||
for (int j = 0; j < 6; j++) {
|
||||
if (edges[inputLength][j] != null) {
|
||||
Edge edge = edges[inputLength][j];
|
||||
int size = j >= 1 && j <= 3 ? edge.cachedTotalSize + 1 : edge.cachedTotalSize; //C40, TEXT and X12 need an
|
||||
let minimalJ:i32 = -1;
|
||||
let minimalSize = i32::MAX;
|
||||
for j in 0..6 {
|
||||
// for (int j = 0; j < 6; j++) {
|
||||
if edges[inputLength][j].is_some() {
|
||||
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
|
||||
if (size < minimalSize) {
|
||||
minimalSize = size;
|
||||
minimalJ = j;
|
||||
if (size as i32) < minimalSize {
|
||||
minimalSize = size as i32;
|
||||
minimalJ = j as i32;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (minimalJ < 0) {
|
||||
throw new RuntimeException("Internal error: failed to encode \"" + input + "\"");
|
||||
if minimalJ < 0 {
|
||||
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 {
|
||||
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};
|
||||
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};
|
||||
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;
|
||||
const allCodewordCapacities : [u32;28] = [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];
|
||||
const squareCodewordCapacities : [u32;24]= [3, 5, 8, 12, 18, 22, 30, 36, 44, 62, 86, 114, 144, 174, 204,
|
||||
280, 368, 456, 576, 696, 816, 1050, 1304, 1558];
|
||||
const rectangularCodewordCapacities :[u32;6]= [5, 10, 16, 33, 32, 49];
|
||||
|
||||
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.mode = mode;
|
||||
this.fromPosition = fromPosition;
|
||||
@@ -522,52 +567,52 @@ public final class MinimalEncoder {
|
||||
if (input.isECI(fromPosition) || isExtendedASCII(input.charAt(fromPosition), input.getFNC1Character())) {
|
||||
size++;
|
||||
}
|
||||
if (previousMode == Mode.C40 ||
|
||||
previousMode == Mode.TEXT ||
|
||||
previousMode == Mode.X12) {
|
||||
if (previousMode == Mode::C40 ||
|
||||
previousMode == Mode::TEXT ||
|
||||
previousMode == Mode::X12) {
|
||||
size++; // unlatch 254 to ASCII
|
||||
}
|
||||
break;
|
||||
case B256:
|
||||
size++;
|
||||
if (previousMode != Mode.B256) {
|
||||
if (previousMode != Mode::B256) {
|
||||
size++; //byte count
|
||||
} else if (getB256Size() == 250) {
|
||||
size++; //extra byte count
|
||||
}
|
||||
if (previousMode == Mode.ASCII) {
|
||||
if (previousMode == Mode::ASCII) {
|
||||
size++; //latch to B256
|
||||
} else if (previousMode == Mode.C40 ||
|
||||
previousMode == Mode.TEXT ||
|
||||
previousMode == Mode.X12) {
|
||||
} else if (previousMode == Mode::C40 ||
|
||||
previousMode == Mode::TEXT ||
|
||||
previousMode == Mode::X12) {
|
||||
size += 2; //unlatch to ASCII, latch to B256
|
||||
}
|
||||
break;
|
||||
case C40:
|
||||
case TEXT:
|
||||
case X12:
|
||||
if (mode == Mode.X12) {
|
||||
if (mode == Mode::X12) {
|
||||
size += 2;
|
||||
} else {
|
||||
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
|
||||
} else if (previousMode != mode && (previousMode == Mode.C40 ||
|
||||
previousMode == Mode.TEXT ||
|
||||
previousMode == Mode.X12)) {
|
||||
} else if (previousMode != mode && (previousMode == Mode::C40 ||
|
||||
previousMode == Mode::TEXT ||
|
||||
previousMode == Mode::X12)) {
|
||||
size += 2; //unlatch 254 to ASCII followed by latch to this mode
|
||||
}
|
||||
break;
|
||||
case EDF:
|
||||
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
|
||||
} else if (previousMode == Mode.C40 ||
|
||||
previousMode == Mode.TEXT ||
|
||||
previousMode == Mode.X12) {
|
||||
} else if (previousMode == Mode::C40 ||
|
||||
previousMode == Mode::TEXT ||
|
||||
previousMode == Mode::X12) {
|
||||
size += 2; //unlatch 254 to ASCII followed by latch to this mode
|
||||
}
|
||||
break;
|
||||
@@ -576,57 +621,57 @@ public final class MinimalEncoder {
|
||||
}
|
||||
|
||||
// does not count beyond 250
|
||||
int getB256Size() {
|
||||
pub fn getB256Size(&self) -> u32{
|
||||
int cnt = 0;
|
||||
Edge current = this;
|
||||
while (current != null && current.mode == Mode.B256 && cnt <= 250) {
|
||||
while (current != null && current.mode == Mode::B256 && cnt <= 250) {
|
||||
cnt++;
|
||||
current = current.previous;
|
||||
}
|
||||
return cnt;
|
||||
}
|
||||
|
||||
Mode getPreviousStartMode() {
|
||||
return previous == null ? Mode.ASCII : previous.mode;
|
||||
pub fn getPreviousStartMode(&self) -> Mode{
|
||||
return previous == null ? Mode::ASCII : previous.mode;
|
||||
}
|
||||
|
||||
Mode getPreviousMode() {
|
||||
return previous == null ? Mode.ASCII : previous.getEndMode();
|
||||
pub fn getPreviousMode(&self) -> Mode{
|
||||
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
|
||||
* ASCII bytes.
|
||||
* - 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.
|
||||
* */
|
||||
Mode getEndMode() {
|
||||
if (mode == Mode.EDF) {
|
||||
pub fn getEndMode(&self)->Mode {
|
||||
if (mode == Mode::EDF) {
|
||||
if (characterLength < 4) {
|
||||
return Mode.ASCII;
|
||||
return Mode::ASCII;
|
||||
}
|
||||
int lastASCII = getLastASCII(); // see 5.2.8.2 EDIFACT encodation Rules
|
||||
if (lastASCII > 0 && getCodewordsRemaining(cachedTotalSize + lastASCII) <= 2 - lastASCII) {
|
||||
return Mode.ASCII;
|
||||
return Mode::ASCII;
|
||||
}
|
||||
}
|
||||
if (mode == Mode.C40 ||
|
||||
mode == Mode.TEXT ||
|
||||
mode == Mode.X12) {
|
||||
if (mode == Mode::C40 ||
|
||||
mode == Mode::TEXT ||
|
||||
mode == Mode::X12) {
|
||||
|
||||
// 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) {
|
||||
return Mode.ASCII;
|
||||
return Mode::ASCII;
|
||||
}
|
||||
int lastASCII = getLastASCII();
|
||||
if (lastASCII == 1 && getCodewordsRemaining(cachedTotalSize + 1) == 0) {
|
||||
return Mode.ASCII;
|
||||
return Mode::ASCII;
|
||||
}
|
||||
}
|
||||
return mode;
|
||||
}
|
||||
|
||||
Mode getMode() {
|
||||
pub fn getMode(&self) -> Mode{
|
||||
return mode;
|
||||
}
|
||||
|
||||
@@ -634,7 +679,7 @@ public final class MinimalEncoder {
|
||||
* two consecutive digits and a non extended character or of 4 digits.
|
||||
* Returns 0 in any other case
|
||||
**/
|
||||
int getLastASCII() {
|
||||
pub fn getLastASCII(&self) -> u32{
|
||||
int length = input.length();
|
||||
int from = fromPosition + characterLength;
|
||||
if (length - from > 4 || from >= length) {
|
||||
@@ -651,24 +696,24 @@ public final class MinimalEncoder {
|
||||
input.getFNC1Character())) {
|
||||
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 2;
|
||||
}
|
||||
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())) {
|
||||
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())) {
|
||||
return 2;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
if (HighLevelEncoder.isDigit(input.charAt(from)) && HighLevelEncoder.isDigit(input.charAt(from + 1))
|
||||
&& HighLevelEncoder.isDigit(input.charAt(from + 2)) && HighLevelEncoder.isDigit(input.charAt(from + 3))) {
|
||||
if (high_level_encoder::isDigit(input.charAt(from)) && high_level_encoder::isDigit(input.charAt(from + 1))
|
||||
&& high_level_encoder::isDigit(input.charAt(from + 2)) && high_level_encoder::isDigit(input.charAt(from + 3))) {
|
||||
return 2;
|
||||
}
|
||||
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
|
||||
* number of codewords.
|
||||
**/
|
||||
int getMinSymbolSize(int minimum) {
|
||||
pub fn getMinSymbolSize(&self, minimum:u32) -> u32{
|
||||
switch (input.getShapeHint()) {
|
||||
case FORCE_SQUARE:
|
||||
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
|
||||
* minimal number of codewords.
|
||||
**/
|
||||
int getCodewordsRemaining(int minimum) {
|
||||
pub fn getCodewordsRemaining( minimum:u32) -> u32{
|
||||
return getMinSymbolSize(minimum) - minimum;
|
||||
}
|
||||
|
||||
static byte[] getBytes(int c) {
|
||||
pub fn getBytes1( c:u32) -> Vec<u8>{
|
||||
byte[] result = new byte[1];
|
||||
result[0] = (byte) c;
|
||||
return result;
|
||||
}
|
||||
|
||||
static byte[] getBytes(int c1,int c2) {
|
||||
pub fn getBytes2( c1:u32, c2:u32) -> Vec<u8>{
|
||||
byte[] result = new byte[2];
|
||||
result[0] = (byte) c1;
|
||||
result[1] = (byte) c2;
|
||||
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;
|
||||
bytes[offset] = (byte) (val16 / 256);
|
||||
bytes[offset + 1] = (byte) (val16 % 256);
|
||||
}
|
||||
|
||||
private static int getX12Value(char c) {
|
||||
fn getX12Value( c:char) -> u32{
|
||||
return c == 13 ? 0 :
|
||||
c == 42 ? 1 :
|
||||
c == 62 ? 2 :
|
||||
@@ -737,7 +782,7 @@ public final class MinimalEncoder {
|
||||
c >= 65 && c <= 90 ? c - 51 : c;
|
||||
}
|
||||
|
||||
byte[] getX12Words() {
|
||||
pub fn getX12Words(&self) -> Vec<u8> {
|
||||
assert characterLength % 3 == 0;
|
||||
byte[] result = new byte[characterLength / 3 * 2];
|
||||
for (int i = 0; i < result.length; i += 2) {
|
||||
@@ -748,14 +793,14 @@ public final class MinimalEncoder {
|
||||
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) ||
|
||||
!c40 && isInTextShift1Set(c)) ? 0 :
|
||||
(c40 && isInC40Shift2Set(c, fnc1) ||
|
||||
!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) {
|
||||
assert setIndex == 2;
|
||||
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<>();
|
||||
for (int i = 0; i < characterLength; 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));
|
||||
} else if (!isExtendedASCII(ci, fnc1)) {
|
||||
int shiftValue = getShiftValue(ci, c40, fnc1);
|
||||
@@ -797,8 +842,8 @@ public final class MinimalEncoder {
|
||||
c40Values.add((byte) getC40Value(c40, shiftValue, ci, fnc1));
|
||||
} else {
|
||||
char asciiValue = (char) ((ci & 0xff) - 128);
|
||||
if (c40 && HighLevelEncoder.isNativeC40(asciiValue) ||
|
||||
!c40 && HighLevelEncoder.isNativeText(asciiValue)) {
|
||||
if (c40 && high_level_encoder::isNativeC40(asciiValue) ||
|
||||
!c40 && high_level_encoder::isNativeText(asciiValue)) {
|
||||
c40Values.add((byte) 1); //Shift 2
|
||||
c40Values.add((byte) 30); //Upper Shift
|
||||
c40Values.add((byte) getC40Value(c40, 0, asciiValue, fnc1));
|
||||
@@ -826,7 +871,7 @@ public final class MinimalEncoder {
|
||||
return result;
|
||||
}
|
||||
|
||||
byte[] getEDFBytes() {
|
||||
pub fn getEDFBytes(&self) -> Vec<u8> {
|
||||
int numberOfThirds = (int) Math.ceil(characterLength / 4.0);
|
||||
byte[] result = new byte[numberOfThirds * 3];
|
||||
int pos = fromPosition;
|
||||
@@ -851,7 +896,7 @@ public final class MinimalEncoder {
|
||||
return result;
|
||||
}
|
||||
|
||||
byte[] getLatchBytes() {
|
||||
pub fn getLatchBytes(&self) -> Vec<u8> {
|
||||
switch (getPreviousMode()) {
|
||||
case ASCII:
|
||||
case B256: //after B256 ends (via length) we are back to ASCII
|
||||
@@ -889,14 +934,14 @@ public final class MinimalEncoder {
|
||||
}
|
||||
break;
|
||||
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;
|
||||
}
|
||||
return new byte[0];
|
||||
}
|
||||
|
||||
// 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) {
|
||||
case ASCII:
|
||||
if (input.isECI(fromPosition)) {
|
||||
@@ -926,28 +971,30 @@ public final class MinimalEncoder {
|
||||
}
|
||||
}
|
||||
|
||||
private static final class RXingResult {
|
||||
struct RXingResult {
|
||||
|
||||
private final byte[] bytes;
|
||||
|
||||
RXingResult(Edge solution) {
|
||||
Input input = solution.input;
|
||||
int size = 0;
|
||||
List<Byte> bytesAL = new ArrayList<>();
|
||||
List<Integer> randomizePostfixLength = new ArrayList<>();
|
||||
List<Integer> randomizeLengths = new ArrayList<>();
|
||||
if ((solution.mode == Mode.C40 ||
|
||||
solution.mode == Mode.TEXT ||
|
||||
solution.mode == Mode.X12) &&
|
||||
solution.getEndMode() != Mode.ASCII) {
|
||||
size += prepend(MinimalEncoder.Edge.getBytes(254),bytesAL);
|
||||
bytes:Vec<u8>,
|
||||
}
|
||||
Edge current = solution;
|
||||
impl RXingResult{
|
||||
|
||||
pub fn new( solution:Rc<Edge>) -> Self {
|
||||
let input = solution.input;
|
||||
let size = 0;
|
||||
let bytesAL = new ArrayList<>();
|
||||
let randomizePostfixLength = new ArrayList<>();
|
||||
let randomizeLengths = new ArrayList<>();
|
||||
if ((solution.mode == Mode::C40 ||
|
||||
solution.mode == Mode::TEXT ||
|
||||
solution.mode == Mode::X12) &&
|
||||
solution.getEndMode() != Mode::ASCII) {
|
||||
size += prepend(Edge::getBytes(254),bytesAL);
|
||||
}
|
||||
let current = solution;
|
||||
while (current != null) {
|
||||
size += prepend(current.getDataBytes(),bytesAL);
|
||||
|
||||
if (current.previous == null || current.getPreviousStartMode() != current.getMode()) {
|
||||
if (current.getMode() == Mode.B256) {
|
||||
if (current.getMode() == Mode::B256) {
|
||||
if (size <= 249) {
|
||||
bytesAL.add(0, (byte) 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--) {
|
||||
into.add(0, bytes[i]);
|
||||
}
|
||||
return bytes.length;
|
||||
}
|
||||
|
||||
private static int randomize253State(int codewordPosition) {
|
||||
int pseudoRandom = ((149 * codewordPosition) % 253) + 1;
|
||||
int tempVariable = 129 + pseudoRandom;
|
||||
fn randomize253State( codewordPosition:u32) -> u32{
|
||||
let pseudoRandom = ((149 * codewordPosition) % 253) + 1;
|
||||
let tempVariable = 129 + pseudoRandom;
|
||||
return tempVariable <= 254 ? tempVariable : tempVariable - 254;
|
||||
}
|
||||
|
||||
static void applyRandomPattern(List<Byte> bytesAL,int startPosition, int length) {
|
||||
for (int i = 0; i < length; i++) {
|
||||
pub fn applyRandomPattern(bytesAL:&[u8], startPosition:u32, length:u32) {
|
||||
for i in 0..length {
|
||||
// for (int i = 0; i < length; i++) {
|
||||
//See "B.1 253-state algorithm
|
||||
int Pad_codeword_position = startPosition + i;
|
||||
int Pad_codeword_value = bytesAL.get(Pad_codeword_position) & 0xff;
|
||||
int pseudo_random_number = ((149 * (Pad_codeword_position + 1)) % 255) + 1;
|
||||
int temp_variable = Pad_codeword_value + pseudo_random_number;
|
||||
let Pad_codeword_position = startPosition + i;
|
||||
let Pad_codeword_value = bytesAL.get(Pad_codeword_position) & 0xff;
|
||||
let pseudo_random_number = ((149 * (Pad_codeword_position + 1)) % 255) + 1;
|
||||
let temp_variable = Pad_codeword_value + pseudo_random_number;
|
||||
bytesAL.set(Pad_codeword_position, (byte) (temp_variable <= 255 ? temp_variable : temp_variable - 256));
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] getBytes() {
|
||||
return bytes;
|
||||
pub fn getBytes(&self) -> &[u8] {
|
||||
&self.bytes
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final class Input extends MinimalECIInput {
|
||||
|
||||
private final SymbolShapeHint shape;
|
||||
private final int macroId;
|
||||
|
||||
private Input(String stringToEncode, Charset priorityCharset, int fnc1, SymbolShapeHint shape, int macroId) {
|
||||
super(stringToEncode, priorityCharset, fnc1);
|
||||
this.shape = shape;
|
||||
this.macroId = macroId;
|
||||
struct Input {
|
||||
shape: SymbolShapeHint,
|
||||
macroId:i32,
|
||||
internal: MinimalECIInput
|
||||
}
|
||||
|
||||
private int getMacroId() {
|
||||
return macroId;
|
||||
impl Input{
|
||||
|
||||
pub fn new( stringToEncode:&str, priorityCharset:Option<EncodingRef>, fnc1:Option<char>, shape:SymbolShapeHint, macroId:i32) -> Self{
|
||||
Self {
|
||||
shape,
|
||||
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 SymbolShapeHint getShapeHint() {
|
||||
return shape;
|
||||
pub fn getMacroId(&self) -> i32{
|
||||
self.macroId
|
||||
}
|
||||
|
||||
pub fn getShapeHint(&self) -> SymbolShapeHint{
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ mod encoder_context;
|
||||
mod symbol_shape_hint;
|
||||
mod symbol_info;
|
||||
pub mod high_level_encoder;
|
||||
pub mod minimal_encoder;
|
||||
|
||||
pub use encoder::*;
|
||||
pub use encoder_context::*;
|
||||
@@ -26,3 +27,6 @@ pub use edifact_encoder::*;
|
||||
|
||||
mod base256_encoder;
|
||||
pub use base256_encoder::*;
|
||||
|
||||
// #[cfg(test)]
|
||||
// mod high_level_encode_test_case;
|
||||
@@ -22,7 +22,7 @@ use super::SymbolShapeHint;
|
||||
use 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, 5, 7, 10, 10, 1),
|
||||
/*rect*/ SymbolInfo::new(true, 5, 7, 16, 6, 1),
|
||||
|
||||
Reference in New Issue
Block a user