high level encoder builds

This commit is contained in:
Henry Schimke
2022-12-17 18:47:38 -06:00
parent f99a862f9d
commit d9b0d46f71
13 changed files with 1093 additions and 1004 deletions

View File

@@ -16,6 +16,8 @@
//package com.google.zxing.common;
use std::fmt::Display;
use crate::Exceptions;
/**
@@ -23,7 +25,7 @@ use crate::Exceptions;
*
* @author Alex Geller
*/
pub trait ECIInput {
pub trait ECIInput:Display {
/**
* Returns the length of this input. The length is the number
* of {@code byte}s in or ECIs in the sequence.
@@ -103,6 +105,6 @@ pub trait ECIInput {
* @throws IllegalArgumentException
* if the value at the {@code index} argument is not an ECI (@see #isECI)
*/
fn getECIValue(&self, index: usize) -> Result<u32, Exceptions>;
fn getECIValue(&self, index: usize) -> Result<i32, Exceptions>;
fn haveNCharacters(&self, index: usize, n: usize) -> bool;
}

View File

@@ -162,7 +162,7 @@ impl ECIInput for MinimalECIInput {
* @throws IllegalArgumentException
* if the value at the {@code index} argument is not an ECI (@see #isECI)
*/
fn getECIValue(&self, index: usize) -> Result<u32, Exceptions> {
fn getECIValue(&self, index: usize) -> Result<i32, Exceptions> {
if index >= self.length() {
return Err(Exceptions::IndexOutOfBoundsException(index.to_string()));
}
@@ -172,7 +172,7 @@ impl ECIInput for MinimalECIInput {
index
)));
}
Ok((self.bytes[index] as u32 - 256) as u32)
Ok((self.bytes[index] as u32 - 256) as i32)
}
fn haveNCharacters(&self, index: usize, n: usize) -> bool {

View File

@@ -1241,7 +1241,7 @@ impl Edge {
if self.input.isECI(self.fromPosition)? {
return Ok(Self::getBytes2(
241,
self.input.getECIValue(self.fromPosition as usize)? + 1,
self.input.getECIValue(self.fromPosition as usize)? as u32+ 1,
));
} else if isExtendedASCII(
self.input.charAt(self.fromPosition as usize)?,
@@ -1459,7 +1459,7 @@ impl Input {
fn isFNC1(&self, index: usize) -> Result<bool, Exceptions> {
self.internal.isFNC1(index)
}
fn getECIValue(&self, index: usize) -> Result<u32, Exceptions> {
fn getECIValue(&self, index: usize) -> Result<i32, Exceptions> {
self.internal.getECIValue(index)
}
}

View File

@@ -1,70 +0,0 @@
/*
* Copyright 2011 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.zxing.pdf417.encoder;
/**
* Holds all of the information for a barcode in a format where it can be easily accessible
*
* @author Jacob Haynes
*/
public final class BarcodeMatrix {
private final BarcodeRow[] matrix;
private int currentRow;
private final int height;
private final int width;
/**
* @param height the height of the matrix (Rows)
* @param width the width of the matrix (Cols)
*/
BarcodeMatrix(int height, int width) {
matrix = new BarcodeRow[height];
//Initializes the array to the correct width
for (int i = 0, matrixLength = matrix.length; i < matrixLength; i++) {
matrix[i] = new BarcodeRow((width + 4) * 17 + 1);
}
this.width = width * 17;
this.height = height;
this.currentRow = -1;
}
void set(int x, int y, byte value) {
matrix[y].set(x, value);
}
void startRow() {
++currentRow;
}
BarcodeRow getCurrentRow() {
return matrix[currentRow];
}
public byte[][] getMatrix() {
return getScaledMatrix(1, 1);
}
public byte[][] getScaledMatrix(int xScale, int yScale) {
byte[][] matrixOut = new byte[height * yScale][width * xScale];
int yMax = height * yScale;
for (int i = 0; i < yMax; i++) {
matrixOut[yMax - i - 1] = matrix[i / yScale].getScaledRow(xScale);
}
return matrixOut;
}
}

View File

@@ -1,79 +0,0 @@
/*
* Copyright 2011 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.zxing.pdf417.encoder;
/**
* @author Jacob Haynes
*/
final class BarcodeRow {
private final byte[] row;
//A tacker for position in the bar
private int currentLocation;
/**
* Creates a Barcode row of the width
*/
BarcodeRow(int width) {
this.row = new byte[width];
currentLocation = 0;
}
/**
* Sets a specific location in the bar
*
* @param x The location in the bar
* @param value Black if true, white if false;
*/
void set(int x, byte value) {
row[x] = value;
}
/**
* Sets a specific location in the bar
*
* @param x The location in the bar
* @param black Black if true, white if false;
*/
private void set(int x, boolean black) {
row[x] = (byte) (black ? 1 : 0);
}
/**
* @param black A boolean which is true if the bar black false if it is white
* @param width How many spots wide the bar is.
*/
void addBar(boolean black, int width) {
for (int ii = 0; ii < width; ii++) {
set(currentLocation++, black);
}
}
/**
* This function scales the row
*
* @param scale How much you want the image to be scaled, must be greater than or equal to 1.
* @return the scaled row
*/
byte[] getScaledRow(int scale) {
byte[] output = new byte[row.length * scale];
for (int i = 0; i < output.length; i++) {
output[i] = row[i / scale];
}
return output;
}
}

View File

@@ -1,712 +0,0 @@
/*
* Copyright 2006 Jeremias Maerki in part, and ZXing Authors in part
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* This file has been modified from its original form in Barcode4J.
*/
package com.google.zxing.pdf417.encoder;
import com.google.zxing.WriterException;
import com.google.zxing.common.CharacterSetECI;
import com.google.zxing.common.ECIInput;
import com.google.zxing.common.MinimalECIInput;
import java.math.BigInteger;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
/**
* PDF417 high-level encoder following the algorithm described in ISO/IEC 15438:2001(E) in
* annex P.
*/
final class PDF417HighLevelEncoder {
/**
* code for Text compaction
*/
private static final int TEXT_COMPACTION = 0;
/**
* code for Byte compaction
*/
private static final int BYTE_COMPACTION = 1;
/**
* code for Numeric compaction
*/
private static final int NUMERIC_COMPACTION = 2;
/**
* Text compaction submode Alpha
*/
private static final int SUBMODE_ALPHA = 0;
/**
* Text compaction submode Lower
*/
private static final int SUBMODE_LOWER = 1;
/**
* Text compaction submode Mixed
*/
private static final int SUBMODE_MIXED = 2;
/**
* Text compaction submode Punctuation
*/
private static final int SUBMODE_PUNCTUATION = 3;
/**
* mode latch to Text Compaction mode
*/
private static final int LATCH_TO_TEXT = 900;
/**
* mode latch to Byte Compaction mode (number of characters NOT a multiple of 6)
*/
private static final int LATCH_TO_BYTE_PADDED = 901;
/**
* mode latch to Numeric Compaction mode
*/
private static final int LATCH_TO_NUMERIC = 902;
/**
* mode shift to Byte Compaction mode
*/
private static final int SHIFT_TO_BYTE = 913;
/**
* mode latch to Byte Compaction mode (number of characters a multiple of 6)
*/
private static final int LATCH_TO_BYTE = 924;
/**
* identifier for a user defined Extended Channel Interpretation (ECI)
*/
private static final int ECI_USER_DEFINED = 925;
/**
* identifier for a general purpose ECO format
*/
private static final int ECI_GENERAL_PURPOSE = 926;
/**
* identifier for an ECI of a character set of code page
*/
private static final int ECI_CHARSET = 927;
/**
* Raw code table for text compaction Mixed sub-mode
*/
private static final byte[] TEXT_MIXED_RAW = {
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 38, 13, 9, 44, 58,
35, 45, 46, 36, 47, 43, 37, 42, 61, 94, 0, 32, 0, 0, 0};
/**
* Raw code table for text compaction: Punctuation sub-mode
*/
private static final byte[] TEXT_PUNCTUATION_RAW = {
59, 60, 62, 64, 91, 92, 93, 95, 96, 126, 33, 13, 9, 44, 58,
10, 45, 46, 36, 47, 34, 124, 42, 40, 41, 63, 123, 125, 39, 0};
private static final byte[] MIXED = new byte[128];
private static final byte[] PUNCTUATION = new byte[128];
private static final Charset DEFAULT_ENCODING = StandardCharsets.ISO_8859_1;
private PDF417HighLevelEncoder() {
}
static {
//Construct inverse lookups
Arrays.fill(MIXED, (byte) -1);
for (int i = 0; i < TEXT_MIXED_RAW.length; i++) {
byte b = TEXT_MIXED_RAW[i];
if (b > 0) {
MIXED[b] = (byte) i;
}
}
Arrays.fill(PUNCTUATION, (byte) -1);
for (int i = 0; i < TEXT_PUNCTUATION_RAW.length; i++) {
byte b = TEXT_PUNCTUATION_RAW[i];
if (b > 0) {
PUNCTUATION[b] = (byte) i;
}
}
}
/**
* Performs high-level encoding of a PDF417 message using the algorithm described in annex P
* of ISO/IEC 15438:2001(E). If byte compaction has been selected, then only byte compaction
* is used.
*
* @param msg the message
* @param compaction compaction mode to use
* @param encoding character encoding used to encode in default or byte compaction
* or {@code null} for default / not applicable
* @param autoECI encode input minimally using multiple ECIs if needed
* If autoECI encoding is specified and additionally {@code encoding} is specified, then the encoder
* will use the specified {@link Charset} for any character that can be encoded by it, regardless
* if a different encoding would lead to a more compact encoding. When no {@code encoding} is specified
* then charsets will be chosen so that the byte representation is minimal.
* @return the encoded message (the char values range from 0 to 928)
*/
static String encodeHighLevel(String msg, Compaction compaction, Charset encoding, boolean autoECI)
throws WriterException {
if (msg.isEmpty()) {
throw new WriterException("Empty message not allowed");
}
if (encoding == null && !autoECI) {
for (int i = 0; i < msg.length(); i++) {
if (msg.charAt(i) > 255) {
throw new WriterException("Non-encodable character detected: " + msg.charAt(i) + " (Unicode: " +
(int) msg.charAt(i) +
"). Consider specifying EncodeHintType.PDF417_AUTO_ECI and/or EncodeTypeHint.CHARACTER_SET.");
}
}
}
//the codewords 0..928 are encoded as Unicode characters
StringBuilder sb = new StringBuilder(msg.length());
ECIInput input;
if (autoECI) {
input = new MinimalECIInput(msg, encoding, -1);
} else {
input = new NoECIInput(msg);
if (encoding == null) {
encoding = DEFAULT_ENCODING;
} else if (!DEFAULT_ENCODING.equals(encoding)) {
CharacterSetECI eci = CharacterSetECI.getCharacterSetECI(encoding);
if (eci != null) {
encodingECI(eci.getValue(), sb);
}
}
}
int len = input.length();
int p = 0;
int textSubMode = SUBMODE_ALPHA;
// User selected encoding mode
switch (compaction) {
case TEXT:
encodeText(input, p, len, sb, textSubMode);
break;
case BYTE:
if (autoECI) {
encodeMultiECIBinary(input, 0, input.length(), TEXT_COMPACTION, sb);
} else {
byte[] msgBytes = input.toString().getBytes(encoding);
encodeBinary(msgBytes, p, msgBytes.length, BYTE_COMPACTION, sb);
}
break;
case NUMERIC:
sb.append((char) LATCH_TO_NUMERIC);
encodeNumeric(input, p, len, sb);
break;
default:
int encodingMode = TEXT_COMPACTION; //Default mode, see 4.4.2.1
while (p < len) {
while (p < len && input.isECI(p)) {
encodingECI(input.getECIValue(p), sb);
p++;
}
if (p >= len) {
break;
}
int n = determineConsecutiveDigitCount(input, p);
if (n >= 13) {
sb.append((char) LATCH_TO_NUMERIC);
encodingMode = NUMERIC_COMPACTION;
textSubMode = SUBMODE_ALPHA; //Reset after latch
encodeNumeric(input, p, n, sb);
p += n;
} else {
int t = determineConsecutiveTextCount(input, p);
if (t >= 5 || n == len) {
if (encodingMode != TEXT_COMPACTION) {
sb.append((char) LATCH_TO_TEXT);
encodingMode = TEXT_COMPACTION;
textSubMode = SUBMODE_ALPHA; //start with submode alpha after latch
}
textSubMode = encodeText(input, p, t, sb, textSubMode);
p += t;
} else {
int b = determineConsecutiveBinaryCount(input, p, autoECI ? null : encoding);
if (b == 0) {
b = 1;
}
byte[] bytes = autoECI ? null : input.subSequence(p, p + b).toString().getBytes(encoding);
if (((bytes == null && b == 1) || (bytes != null && bytes.length == 1))
&& encodingMode == TEXT_COMPACTION) {
//Switch for one byte (instead of latch)
if (autoECI) {
encodeMultiECIBinary(input, p, 1, TEXT_COMPACTION, sb);
} else {
encodeBinary(bytes, 0, 1, TEXT_COMPACTION, sb);
}
} else {
//Mode latch performed by encodeBinary()
if (autoECI) {
encodeMultiECIBinary(input, p, p + b, encodingMode, sb);
} else {
encodeBinary(bytes, 0, bytes.length, encodingMode, sb);
}
encodingMode = BYTE_COMPACTION;
textSubMode = SUBMODE_ALPHA; //Reset after latch
}
p += b;
}
}
}
break;
}
return sb.toString();
}
/**
* Encode parts of the message using Text Compaction as described in ISO/IEC 15438:2001(E),
* chapter 4.4.2.
*
* @param input the input
* @param startpos the start position within the message
* @param count the number of characters to encode
* @param sb receives the encoded codewords
* @param initialSubmode should normally be SUBMODE_ALPHA
* @return the text submode in which this method ends
*/
private static int encodeText(ECIInput input,
int startpos,
int count,
StringBuilder sb,
int initialSubmode) throws WriterException {
StringBuilder tmp = new StringBuilder(count);
int submode = initialSubmode;
int idx = 0;
while (true) {
if (input.isECI(startpos + idx)) {
encodingECI(input.getECIValue(startpos + idx), sb);
idx++;
} else {
char ch = input.charAt(startpos + idx);
switch (submode) {
case SUBMODE_ALPHA:
if (isAlphaUpper(ch)) {
if (ch == ' ') {
tmp.append((char) 26); //space
} else {
tmp.append((char) (ch - 65));
}
} else {
if (isAlphaLower(ch)) {
submode = SUBMODE_LOWER;
tmp.append((char) 27); //ll
continue;
} else if (isMixed(ch)) {
submode = SUBMODE_MIXED;
tmp.append((char) 28); //ml
continue;
} else {
tmp.append((char) 29); //ps
tmp.append((char) PUNCTUATION[ch]);
break;
}
}
break;
case SUBMODE_LOWER:
if (isAlphaLower(ch)) {
if (ch == ' ') {
tmp.append((char) 26); //space
} else {
tmp.append((char) (ch - 97));
}
} else {
if (isAlphaUpper(ch)) {
tmp.append((char) 27); //as
tmp.append((char) (ch - 65));
//space cannot happen here, it is also in "Lower"
break;
} else if (isMixed(ch)) {
submode = SUBMODE_MIXED;
tmp.append((char) 28); //ml
continue;
} else {
tmp.append((char) 29); //ps
tmp.append((char) PUNCTUATION[ch]);
break;
}
}
break;
case SUBMODE_MIXED:
if (isMixed(ch)) {
tmp.append((char) MIXED[ch]);
} else {
if (isAlphaUpper(ch)) {
submode = SUBMODE_ALPHA;
tmp.append((char) 28); //al
continue;
} else if (isAlphaLower(ch)) {
submode = SUBMODE_LOWER;
tmp.append((char) 27); //ll
continue;
} else {
if (startpos + idx + 1 < count) {
if (!input.isECI(startpos + idx + 1) && isPunctuation(input.charAt(startpos + idx + 1))) {
submode = SUBMODE_PUNCTUATION;
tmp.append((char) 25); //pl
continue;
}
}
tmp.append((char) 29); //ps
tmp.append((char) PUNCTUATION[ch]);
}
}
break;
default: //SUBMODE_PUNCTUATION
if (isPunctuation(ch)) {
tmp.append((char) PUNCTUATION[ch]);
} else {
submode = SUBMODE_ALPHA;
tmp.append((char) 29); //al
continue;
}
}
idx++;
if (idx >= count) {
break;
}
}
}
char h = 0;
int len = tmp.length();
for (int i = 0; i < len; i++) {
boolean odd = (i % 2) != 0;
if (odd) {
h = (char) ((h * 30) + tmp.charAt(i));
sb.append(h);
} else {
h = tmp.charAt(i);
}
}
if ((len % 2) != 0) {
sb.append((char) ((h * 30) + 29)); //ps
}
return submode;
}
/**
* Encode all of the message using Byte Compaction as described in ISO/IEC 15438:2001(E)
*
* @param input the input
* @param startpos the start position within the message
* @param count the number of bytes to encode
* @param startmode the mode from which this method starts
* @param sb receives the encoded codewords
*/
private static void encodeMultiECIBinary(ECIInput input,
int startpos,
int count,
int startmode,
StringBuilder sb) throws WriterException {
final int end = Math.min(startpos + count, input.length());
int localStart = startpos;
while (true) {
//encode all leading ECIs and advance localStart
while (localStart < end && input.isECI(localStart)) {
encodingECI(input.getECIValue(localStart), sb);
localStart++;
}
int localEnd = localStart;
//advance end until before the next ECI
while (localEnd < end && !input.isECI(localEnd)) {
localEnd++;
}
final int localCount = localEnd - localStart;
if (localCount <= 0) {
//done
break;
} else {
//encode the segment
encodeBinary(subBytes(input, localStart, localEnd),
0, localCount, localStart == startpos ? startmode : BYTE_COMPACTION, sb);
localStart = localEnd;
}
}
}
static byte[] subBytes(ECIInput input, int start, int end) {
final int count = end - start;
byte[] result = new byte[count];
for (int i = start; i < end; i++) {
result[i - start] = (byte) (input.charAt(i) & 0xff);
}
return result;
}
/**
* Encode parts of the message using Byte Compaction as described in ISO/IEC 15438:2001(E),
* chapter 4.4.3. The Unicode characters will be converted to binary using the cp437
* codepage.
*
* @param bytes the message converted to a byte array
* @param startpos the start position within the message
* @param count the number of bytes to encode
* @param startmode the mode from which this method starts
* @param sb receives the encoded codewords
*/
private static void encodeBinary(byte[] bytes,
int startpos,
int count,
int startmode,
StringBuilder sb) {
if (count == 1 && startmode == TEXT_COMPACTION) {
sb.append((char) SHIFT_TO_BYTE);
} else {
if ((count % 6) == 0) {
sb.append((char) LATCH_TO_BYTE);
} else {
sb.append((char) LATCH_TO_BYTE_PADDED);
}
}
int idx = startpos;
// Encode sixpacks
if (count >= 6) {
char[] chars = new char[5];
while ((startpos + count - idx) >= 6) {
long t = 0;
for (int i = 0; i < 6; i++) {
t <<= 8;
t += bytes[idx + i] & 0xff;
}
for (int i = 0; i < 5; i++) {
chars[i] = (char) (t % 900);
t /= 900;
}
for (int i = chars.length - 1; i >= 0; i--) {
sb.append(chars[i]);
}
idx += 6;
}
}
//Encode rest (remaining n<5 bytes if any)
for (int i = idx; i < startpos + count; i++) {
int ch = bytes[i] & 0xff;
sb.append((char) ch);
}
}
private static void encodeNumeric(ECIInput input, int startpos, int count, StringBuilder sb) {
int idx = 0;
StringBuilder tmp = new StringBuilder(count / 3 + 1);
BigInteger num900 = BigInteger.valueOf(900);
BigInteger num0 = BigInteger.valueOf(0);
while (idx < count) {
tmp.setLength(0);
int len = Math.min(44, count - idx);
String part = "1" + input.subSequence(startpos + idx, startpos + idx + len);
BigInteger bigint = new BigInteger(part);
do {
tmp.append((char) bigint.mod(num900).intValue());
bigint = bigint.divide(num900);
} while (!bigint.equals(num0));
//Reverse temporary string
for (int i = tmp.length() - 1; i >= 0; i--) {
sb.append(tmp.charAt(i));
}
idx += len;
}
}
private static boolean isDigit(char ch) {
return ch >= '0' && ch <= '9';
}
private static boolean isAlphaUpper(char ch) {
return ch == ' ' || (ch >= 'A' && ch <= 'Z');
}
private static boolean isAlphaLower(char ch) {
return ch == ' ' || (ch >= 'a' && ch <= 'z');
}
private static boolean isMixed(char ch) {
return MIXED[ch] != -1;
}
private static boolean isPunctuation(char ch) {
return PUNCTUATION[ch] != -1;
}
private static boolean isText(char ch) {
return ch == '\t' || ch == '\n' || ch == '\r' || (ch >= 32 && ch <= 126);
}
/**
* Determines the number of consecutive characters that are encodable using numeric compaction.
*
* @param input the input
* @param startpos the start position within the input
* @return the requested character count
*/
private static int determineConsecutiveDigitCount(ECIInput input, int startpos) {
int count = 0;
final int len = input.length();
int idx = startpos;
if (idx < len) {
while (idx < len && !input.isECI(idx) && isDigit(input.charAt(idx))) {
count++;
idx++;
}
}
return count;
}
/**
* Determines the number of consecutive characters that are encodable using text compaction.
*
* @param input the input
* @param startpos the start position within the input
* @return the requested character count
*/
private static int determineConsecutiveTextCount(ECIInput input, int startpos) {
final int len = input.length();
int idx = startpos;
while (idx < len) {
int numericCount = 0;
while (numericCount < 13 && idx < len && !input.isECI(idx) && isDigit(input.charAt(idx))) {
numericCount++;
idx++;
}
if (numericCount >= 13) {
return idx - startpos - numericCount;
}
if (numericCount > 0) {
//Heuristic: All text-encodable chars or digits are binary encodable
continue;
}
//Check if character is encodable
if (input.isECI(idx) || !isText(input.charAt(idx))) {
break;
}
idx++;
}
return idx - startpos;
}
/**
* Determines the number of consecutive characters that are encodable using binary compaction.
*
* @param input the input
* @param startpos the start position within the message
* @param encoding the charset used to convert the message to a byte array
* @return the requested character count
*/
private static int determineConsecutiveBinaryCount(ECIInput input, int startpos, Charset encoding)
throws WriterException {
CharsetEncoder encoder = encoding == null ? null : encoding.newEncoder();
int len = input.length();
int idx = startpos;
while (idx < len) {
int numericCount = 0;
int i = idx;
while (numericCount < 13 && !input.isECI(i) && isDigit(input.charAt(i))) {
numericCount++;
//textCount++;
i = idx + numericCount;
if (i >= len) {
break;
}
}
if (numericCount >= 13) {
return idx - startpos;
}
if (encoder != null && !encoder.canEncode(input.charAt(idx))) {
assert input instanceof NoECIInput;
char ch = input.charAt(idx);
throw new WriterException("Non-encodable character detected: " + ch + " (Unicode: " + (int) ch + ')');
}
idx++;
}
return idx - startpos;
}
private static void encodingECI(int eci, StringBuilder sb) throws WriterException {
if (eci >= 0 && eci < 900) {
sb.append((char) ECI_CHARSET);
sb.append((char) eci);
} else if (eci < 810900) {
sb.append((char) ECI_GENERAL_PURPOSE);
sb.append((char) (eci / 900 - 1));
sb.append((char) (eci % 900));
} else if (eci < 811800) {
sb.append((char) ECI_USER_DEFINED);
sb.append((char) (810900 - eci));
} else {
throw new WriterException("ECI number not in valid range from 0..811799, but was " + eci);
}
}
private static final class NoECIInput implements ECIInput {
String input;
private NoECIInput(String input) {
this.input = input;
}
public int length() {
return input.length();
}
public char charAt(int index) {
return input.charAt(index);
}
public boolean isECI(int index) {
return false;
}
public int getECIValue(int index) {
return -1;
}
public boolean haveNCharacters(int index, int n) {
return index + n <= input.length();
}
public CharSequence subSequence(int start, int end) {
return input.subSequence(start, end);
}
public String toString() {
return input;
}
}
}

View File

@@ -0,0 +1,81 @@
/*
* Copyright 2011 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
use super::BarcodeRow;
/**
* Holds all of the information for a barcode in a format where it can be easily accessible
*
* @author Jacob Haynes
*/
pub struct BarcodeMatrix {
matrix: Vec<BarcodeRow>,
currentRow: isize,
height: usize,
width: usize,
}
impl BarcodeMatrix {
/**
* @param height the height of the matrix (Rows)
* @param width the width of the matrix (Cols)
*/
pub fn new(height: usize, width: usize) -> Self {
let mut matrix = Vec::new(); //new BarcodeRow[height];
//Initializes the array to the correct width
for _i in 0..height {
// for (int i = 0, matrixLength = matrix.length; i < matrixLength; i++) {
//matrix[i] = new BarcodeRow((width + 4) * 17 + 1);
matrix.push(BarcodeRow::new((width + 4) * 17 + 1));
}
Self {
matrix,
currentRow: -1,
height,
width: width * 17,
}
// this.width = width * 17;
// this.height = height;
// this.currentRow = -1;
}
pub fn set(&mut self, x: usize, y: usize, value: u8) {
self.matrix[y].set(x, value);
}
pub fn startRow(&mut self) {
self.currentRow += 1;
}
pub fn getCurrentRow(&self) -> &BarcodeRow {
&self.matrix[self.currentRow as usize]
}
pub fn getMatrix(&self) -> Vec<Vec<u8>> {
self.getScaledMatrix(1, 1)
}
pub fn getScaledMatrix(&self, xScale: usize, yScale: usize) -> Vec<Vec<u8>> {
let mut matrixOut = vec![vec![0; self.height * yScale]; self.width * xScale];
// byte[][] matrixOut = new byte[height * yScale][width * xScale];
let yMax = self.height * yScale;
for i in 0..yMax {
// for (int i = 0; i < yMax; i++) {
matrixOut[yMax - i - 1] = self.matrix[i / yScale].getScaledRow(xScale);
}
matrixOut
}
}

View File

@@ -0,0 +1,84 @@
/*
* Copyright 2011 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @author Jacob Haynes
*/
pub struct BarcodeRow {
row: Vec<u8>,
//A tacker for position in the bar
currentLocation: usize,
}
impl BarcodeRow {
/**
* Creates a Barcode row of the width
*/
pub fn new(width: usize) -> Self {
Self {
row: vec![0; width],
currentLocation: 0,
}
}
/**
* Sets a specific location in the bar
*
* @param x The location in the bar
* @param value Black if true, white if false;
*/
pub fn set(&mut self, x: usize, value: u8) {
self.row[x] = value
}
/**
* Sets a specific location in the bar
*
* @param x The location in the bar
* @param black Black if true, white if false;
*/
fn set_bool(&mut self, x: usize, black: bool) {
self.row[x] = if black { 1 } else { 0 }
}
/**
* @param black A boolean which is true if the bar black false if it is white
* @param width How many spots wide the bar is.
*/
pub fn addBar(&mut self, black: bool, width: usize) {
for _ii in 0..width {
// for (int ii = 0; ii < width; ii++) {
self.set_bool(self.currentLocation, black);
self.currentLocation += 1;
}
}
/**
* This function scales the row
*
* @param scale How much you want the image to be scaled, must be greater than or equal to 1.
* @return the scaled row
*/
pub fn getScaledRow(&self, scale: usize) -> Vec<u8> {
let mut output = vec![0; self.row.len() * scale];
for i in 0..output.len() {
// for (int i = 0; i < output.length; i++) {
output[i] = self.row[i / scale];
}
output
}
}

View File

@@ -14,16 +14,15 @@
* limitations under the License.
*/
package com.google.zxing.pdf417.encoder;
/**
* Represents possible PDF417 barcode compaction types.
*/
public enum Compaction {
pub enum Compaction {
AUTO,
TEXT,
BYTE,
NUMERIC
AUTO=0,
TEXT=1,
BYTE=2,
NUMERIC=3
}

View File

@@ -14,41 +14,40 @@
* limitations under the License.
*/
package com.google.zxing.pdf417.encoder;
/**
* Data object to specify the minimum and maximum number of rows and columns for a PDF417 barcode.
*
* @author qwandor@google.com (Andrew Walbran)
*/
public final class Dimensions {
private final int minCols;
private final int maxCols;
private final int minRows;
private final int maxRows;
public Dimensions(int minCols, int maxCols, int minRows, int maxRows) {
this.minCols = minCols;
this.maxCols = maxCols;
this.minRows = minRows;
this.maxRows = maxRows;
}
public int getMinCols() {
return minCols;
}
public int getMaxCols() {
return maxCols;
}
public int getMinRows() {
return minRows;
}
public int getMaxRows() {
return maxRows;
}
pub struct Dimensions {
minCols: usize,
maxCols: usize,
minRows: usize,
maxRows: usize,
}
impl Dimensions {
pub fn new(minCols: usize, maxCols: usize, minRows: usize, maxRows: usize) -> Self {
Self {
minCols,
maxCols,
minRows,
maxRows,
}
}
pub fn getMinCols(&self) -> usize {
self.minCols
}
pub fn getMaxCols(&self) -> usize {
self.maxCols
}
pub fn getMinRows(&self) -> usize {
self.minRows
}
pub fn getMaxRows(&self) -> usize {
self.maxRows
}
}

View File

@@ -1 +1,15 @@
mod compaction;
pub use compaction::*;
mod barcode_row;
pub use barcode_row::*;
mod barcode_batrix;
pub use barcode_batrix::*;
mod dimensions;
pub use dimensions::*;
pub mod pdf_417_error_correction;
pub mod pdf_417_high_level_encoder;

View File

@@ -18,36 +18,35 @@
* This file has been modified from its original form in Barcode4J.
*/
package com.google.zxing.pdf417.encoder;
import com.google.zxing.WriterException;
/**
* PDF417 error correction code following the algorithm described in ISO/IEC 15438:2001(E) in
* chapter 4.10.
*/
final class PDF417ErrorCorrection {
use lazy_static::lazy_static;
use crate::Exceptions;
lazy_static! {
/**
* Tables of coefficients for calculating error correction words
* (see annex F, ISO/IEC 15438:2001(E))
*/
private static final int[][] EC_COEFFICIENTS = {
{27, 917},
{522, 568, 723, 809},
{237, 308, 436, 284, 646, 653, 428, 379},
{274, 562, 232, 755, 599, 524, 801, 132, 295, 116, 442, 428, 295,
42, 176, 65},
{361, 575, 922, 525, 176, 586, 640, 321, 536, 742, 677, 742, 687,
static ref EC_COEFFICIENTS : [Vec<u32>;9] = [
vec![27, 917],
vec![522, 568, 723, 809],
vec![237, 308, 436, 284, 646, 653, 428, 379],
vec![274, 562, 232, 755, 599, 524, 801, 132, 295, 116, 442, 428, 295,
42, 176, 65],
vec![361, 575, 922, 525, 176, 586, 640, 321, 536, 742, 677, 742, 687,
284, 193, 517, 273, 494, 263, 147, 593, 800, 571, 320, 803,
133, 231, 390, 685, 330, 63, 410},
{539, 422, 6, 93, 862, 771, 453, 106, 610, 287, 107, 505, 733,
133, 231, 390, 685, 330, 63, 410],
vec![539, 422, 6, 93, 862, 771, 453, 106, 610, 287, 107, 505, 733,
877, 381, 612, 723, 476, 462, 172, 430, 609, 858, 822, 543,
376, 511, 400, 672, 762, 283, 184, 440, 35, 519, 31, 460,
594, 225, 535, 517, 352, 605, 158, 651, 201, 488, 502, 648,
733, 717, 83, 404, 97, 280, 771, 840, 629, 4, 381, 843,
623, 264, 543},
{521, 310, 864, 547, 858, 580, 296, 379, 53, 779, 897, 444, 400,
623, 264, 543],
vec![521, 310, 864, 547, 858, 580, 296, 379, 53, 779, 897, 444, 400,
925, 749, 415, 822, 93, 217, 208, 928, 244, 583, 620, 246,
148, 447, 631, 292, 908, 490, 704, 516, 258, 457, 907, 594,
723, 674, 292, 272, 96, 684, 432, 686, 606, 860, 569, 193,
@@ -57,8 +56,8 @@ final class PDF417ErrorCorrection {
262, 380, 602, 754, 336, 89, 614, 87, 432, 670, 616, 157,
374, 242, 726, 600, 269, 375, 898, 845, 454, 354, 130, 814,
587, 804, 34, 211, 330, 539, 297, 827, 865, 37, 517, 834,
315, 550, 86, 801, 4, 108, 539},
{524, 894, 75, 766, 882, 857, 74, 204, 82, 586, 708, 250, 905,
315, 550, 86, 801, 4, 108, 539],
vec![524, 894, 75, 766, 882, 857, 74, 204, 82, 586, 708, 250, 905,
786, 138, 720, 858, 194, 311, 913, 275, 190, 375, 850, 438,
733, 194, 280, 201, 280, 828, 757, 710, 814, 919, 89, 68,
569, 11, 204, 796, 605, 540, 913, 801, 700, 799, 137, 439,
@@ -79,8 +78,8 @@ final class PDF417ErrorCorrection {
54, 834, 299, 922, 191, 910, 532, 609, 829, 189, 20, 167,
29, 872, 449, 83, 402, 41, 656, 505, 579, 481, 173, 404,
251, 688, 95, 497, 555, 642, 543, 307, 159, 924, 558, 648,
55, 497, 10},
{352, 77, 373, 504, 35, 599, 428, 207, 409, 574, 118, 498, 285,
55, 497, 10],
vec![352, 77, 373, 504, 35, 599, 428, 207, 409, 574, 118, 498, 285,
380, 350, 492, 197, 265, 920, 155, 914, 299, 229, 643, 294,
871, 306, 88, 87, 193, 352, 781, 846, 75, 327, 520, 435,
543, 203, 666, 249, 346, 781, 621, 640, 268, 794, 534, 539,
@@ -122,83 +121,94 @@ final class PDF417ErrorCorrection {
284, 736, 138, 646, 411, 877, 669, 141, 919, 45, 780, 407,
164, 332, 899, 165, 726, 600, 325, 498, 655, 357, 752, 768,
223, 849, 647, 63, 310, 863, 251, 366, 304, 282, 738, 675,
410, 389, 244, 31, 121, 303, 263}};
private PDF417ErrorCorrection() {
}
/**
* Determines the number of error correction codewords for a specified error correction
* level.
*
* @param errorCorrectionLevel the error correction level (0-8)
* @return the number of codewords generated for error correction
*/
static int getErrorCorrectionCodewordCount(int errorCorrectionLevel) {
if (errorCorrectionLevel < 0 || errorCorrectionLevel > 8) {
throw new IllegalArgumentException("Error correction level must be between 0 and 8!");
}
return 1 << (errorCorrectionLevel + 1);
}
/**
* Returns the recommended minimum error correction level as described in annex E of
* ISO/IEC 15438:2001(E).
*
* @param n the number of data codewords
* @return the recommended minimum error correction level
*/
static int getRecommendedMinimumErrorCorrectionLevel(int n) throws WriterException {
if (n <= 0) {
throw new IllegalArgumentException("n must be > 0");
}
if (n <= 40) {
return 2;
}
if (n <= 160) {
return 3;
}
if (n <= 320) {
return 4;
}
if (n <= 863) {
return 5;
}
throw new WriterException("No recommendation possible");
}
/**
* Generates the error correction codewords according to 4.10 in ISO/IEC 15438:2001(E).
*
* @param dataCodewords the data codewords
* @param errorCorrectionLevel the error correction level (0-8)
* @return the String representing the error correction codewords
*/
static String generateErrorCorrection(CharSequence dataCodewords, int errorCorrectionLevel) {
int k = getErrorCorrectionCodewordCount(errorCorrectionLevel);
char[] e = new char[k];
int sld = dataCodewords.length();
for (int i = 0; i < sld; i++) {
int t1 = (dataCodewords.charAt(i) + e[e.length - 1]) % 929;
int t2;
int t3;
for (int j = k - 1; j >= 1; j--) {
t2 = (t1 * EC_COEFFICIENTS[errorCorrectionLevel][j]) % 929;
t3 = 929 - t2;
e[j] = (char) ((e[j - 1] + t3) % 929);
}
t2 = (t1 * EC_COEFFICIENTS[errorCorrectionLevel][0]) % 929;
t3 = 929 - t2;
e[0] = (char) (t3 % 929);
}
StringBuilder sb = new StringBuilder(k);
for (int j = k - 1; j >= 0; j--) {
if (e[j] != 0) {
e[j] = (char) (929 - e[j]);
}
sb.append(e[j]);
}
return sb.toString();
}
410, 389, 244, 31, 121, 303, 263]];
}
/**
* Determines the number of error correction codewords for a specified error correction
* level.
*
* @param errorCorrectionLevel the error correction level (0-8)
* @return the number of codewords generated for error correction
*/
pub fn getErrorCorrectionCodewordCount(errorCorrectionLevel: u32) -> Result<u32, Exceptions> {
if errorCorrectionLevel > 8 {
return Err(Exceptions::IllegalArgumentException(
"Error correction level must be between 0 and 8!".to_owned(),
));
// throw new IllegalArgumentException("Error correction level must be between 0 and 8!");
}
Ok(1 << errorCorrectionLevel + 1)
}
/**
* Returns the recommended minimum error correction level as described in annex E of
* ISO/IEC 15438:2001(E).
*
* @param n the number of data codewords
* @return the recommended minimum error correction level
*/
pub fn getRecommendedMinimumErrorCorrectionLevel(n: u32) -> Result<u32, Exceptions> {
if n <= 0 {
Err(Exceptions::IllegalArgumentException(
"n must be > 0".to_owned(),
))
} else if n <= 40 {
Ok(2)
} else if n <= 160 {
Ok(3)
} else if n <= 320 {
Ok(4)
} else if n <= 863 {
Ok(5)
} else {
Err(Exceptions::WriterException(
"No recommendation possible".to_owned(),
))
}
}
/**
* Generates the error correction codewords according to 4.10 in ISO/IEC 15438:2001(E).
*
* @param dataCodewords the data codewords
* @param errorCorrectionLevel the error correction level (0-8)
* @return the String representing the error correction codewords
*/
pub fn generateErrorCorrection(
dataCodewords: &str,
errorCorrectionLevel: u32,
) -> Result<String, Exceptions> {
let k = getErrorCorrectionCodewordCount(errorCorrectionLevel)?;
let mut e = vec![0 as char; k as usize]; //new char[k];
let sld = dataCodewords.len();
for i in 0..sld {
// for (int i = 0; i < sld; i++) {
let t1 = (dataCodewords.chars().nth(i).unwrap() as u32 + e[e.len() - 1] as u32) % 929;
let mut t2;
let mut t3;
let mut j = k as usize - 1;
while j >= 1 {
// for (int j = k - 1; j >= 1; j--) {
t2 = (t1 * EC_COEFFICIENTS[errorCorrectionLevel as usize][j]) % 929;
t3 = 929 - t2;
e[j] = char::from_u32((e[j - 1] as u32 + t3) % 929).unwrap();
j -= 1;
}
t2 = (t1 * EC_COEFFICIENTS[errorCorrectionLevel as usize][0]) % 929;
t3 = 929 - t2;
e[0] = char::from_u32(t3 % 929).unwrap();
}
let mut sb = String::with_capacity(k as usize); // StringBuilder(k);
let mut j = k as isize - 1;
while j >= 0 {
// for (int j = k - 1; j >= 0; j--) {
if e[j as usize] as u32 != 0 {
e[j as usize] = char::from_u32(929 - e[j as usize] as u32).unwrap();
}
sb.push(e[j as usize]);
j -= 1;
}
Ok(sb)
}

View File

@@ -0,0 +1,761 @@
/*
* Copyright 2006 Jeremias Maerki in part, and ZXing Authors in part
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* This file has been modified from its original form in Barcode4J.
*/
use std::{fmt::Display, any::TypeId};
use encoding::EncodingRef;
use crate::{Exceptions, common::{ECIInput, MinimalECIInput, CharacterSetECI}};
use super::Compaction;
/**
* PDF417 high-level encoder following the algorithm described in ISO/IEC 15438:2001(E) in
* annex P.
*/
/**
* code for Text compaction
*/
const TEXT_COMPACTION : u32= 0;
/**
* code for Byte compaction
*/
const BYTE_COMPACTION:u32 = 1;
/**
* code for Numeric compaction
*/
const NUMERIC_COMPACTION:u32 = 2;
/**
* Text compaction submode Alpha
*/
const SUBMODE_ALPHA:u32 = 0;
/**
* Text compaction submode Lower
*/
const SUBMODE_LOWER:u32 = 1;
/**
* Text compaction submode Mixed
*/
const SUBMODE_MIXED:u32 = 2;
/**
* Text compaction submode Punctuation
*/
const SUBMODE_PUNCTUATION:u32 = 3;
/**
* mode latch to Text Compaction mode
*/
const LATCH_TO_TEXT:u32 = 900;
/**
* mode latch to Byte Compaction mode (number of characters NOT a multiple of 6)
*/
const LATCH_TO_BYTE_PADDED:u32 = 901;
/**
* mode latch to Numeric Compaction mode
*/
const LATCH_TO_NUMERIC:u32 = 902;
/**
* mode shift to Byte Compaction mode
*/
const SHIFT_TO_BYTE:u32 = 913;
/**
* mode latch to Byte Compaction mode (number of characters a multiple of 6)
*/
const LATCH_TO_BYTE:u32 = 924;
/**
* identifier for a user defined Extended Channel Interpretation (ECI)
*/
const ECI_USER_DEFINED :u32= 925;
/**
* identifier for a general purpose ECO format
*/
const ECI_GENERAL_PURPOSE:u32 = 926;
/**
* identifier for an ECI of a character set of code page
*/
const ECI_CHARSET:u32 = 927;
/**
* Raw code table for text compaction Mixed sub-mode
*/
const TEXT_MIXED_RAW : [u8;30]= [
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 38, 13, 9, 44, 58,
35, 45, 46, 36, 47, 43, 37, 42, 61, 94, 0, 32, 0, 0, 0];
/**
* Raw code table for text compaction: Punctuation sub-mode
*/
const TEXT_PUNCTUATION_RAW :[u8;30] = [
59, 60, 62, 64, 91, 92, 93, 95, 96, 126, 33, 13, 9, 44, 58,
10, 45, 46, 36, 47, 34, 124, 42, 40, 41, 63, 123, 125, 39, 0];
// const MIXED : [u8;128]= [0;128];//new byte[128];
// const PUNCTUATION : [u8;128]= [0;128];//new byte[128];
const DEFAULT_ENCODING : EncodingRef= encoding::all::ISO_8859_1; //StandardCharsets.ISO_8859_1;
const MIXED : [i8;128] = {
let mut mixed = [-1_i8;128];
let mut i = 0;
while i < TEXT_MIXED_RAW.len() {
let b = TEXT_MIXED_RAW[i] as usize;
if b > 0 {
mixed[b] = i as i8;
}
i+=1;
}
mixed
};
const PUNCTUATION : [i8;128] = {
let mut punct = [-1_i8;128];
let mut i = 0;
while i < TEXT_PUNCTUATION_RAW.len() {
let b = TEXT_PUNCTUATION_RAW[i] as usize;
if b > 0 {
punct[b] = i as i8;
}
i += 1;
}
punct
};
// static {
// //Construct inverse lookups
// Arrays.fill(MIXED, (byte) -1);
// for (int i = 0; i < TEXT_MIXED_RAW.length; i++) {
// byte b = TEXT_MIXED_RAW[i];
// if (b > 0) {
// MIXED[b] = (byte) i;
// }
// }
// Arrays.fill(PUNCTUATION, (byte) -1);
// for (int i = 0; i < TEXT_PUNCTUATION_RAW.length; i++) {
// byte b = TEXT_PUNCTUATION_RAW[i];
// if (b > 0) {
// PUNCTUATION[b] = (byte) i;
// }
// }
// }
/**
* Performs high-level encoding of a PDF417 message using the algorithm described in annex P
* of ISO/IEC 15438:2001(E). If byte compaction has been selected, then only byte compaction
* is used.
*
* @param msg the message
* @param compaction compaction mode to use
* @param encoding character encoding used to encode in default or byte compaction
* or {@code null} for default / not applicable
* @param autoECI encode input minimally using multiple ECIs if needed
* If autoECI encoding is specified and additionally {@code encoding} is specified, then the encoder
* will use the specified {@link Charset} for any character that can be encoded by it, regardless
* if a different encoding would lead to a more compact encoding. When no {@code encoding} is specified
* then charsets will be chosen so that the byte representation is minimal.
* @return the encoded message (the char values range from 0 to 928)
*/
pub fn encodeHighLevel( msg:&str, compaction:Compaction, encoding:Option<EncodingRef>, autoECI:bool)
-> Result<String,Exceptions> {
let mut encoding = encoding;
if msg.is_empty() {
return Err(Exceptions::WriterException("Empty message not allowed".to_owned()));
}
if encoding.is_none() && !autoECI {
for i in 0..msg.chars().count() {
// for (int i = 0; i < msg.length(); i++) {
if msg.chars().nth(i).unwrap() as u32 > 255 {
return Err(Exceptions::WriterException(format!("Non-encodable character detected: {} (Unicode: {}). Consider specifying EncodeHintType.PDF417_AUTO_ECI and/or EncodeTypeHint.CHARACTER_SET.",msg.chars().nth(i).unwrap() as u32,msg.chars().nth(i).unwrap())));
}
}
}
//the codewords 0..928 are encoded as Unicode characters
let mut sb = String::with_capacity(msg.len()); //new StringBuilder(msg.length());
let input:Box<dyn ECIInput>;
if autoECI {
input = Box::new(MinimalECIInput::new(msg, encoding, None));
} else {
input = Box::new(NoECIInput::new(msg.to_owned()));
if encoding.is_none() {
encoding = Some(DEFAULT_ENCODING);
} else if !(DEFAULT_ENCODING.name() == encoding.as_ref().unwrap().name()) {
if let Some(eci) = CharacterSetECI::getCharacterSetECI(encoding.unwrap()) {
// if (eci != null) {
encodingECI(CharacterSetECI::getValue(&eci) as i32, &mut sb)?;
}
}
}
let len = input.length();
let mut p = 0;
let mut textSubMode = SUBMODE_ALPHA;
// User selected encoding mode
match compaction {
Compaction::TEXT=>
{encodeText(&input, p, len as u32, &mut sb, textSubMode)?;},
Compaction:: BYTE=>
if autoECI {
encodeMultiECIBinary(&input, 0, input.length() as u32, TEXT_COMPACTION, &mut sb)?;
} else {
let msgBytes = encoding.as_ref().unwrap().encode(&input.to_string(), encoding::EncoderTrap::Strict).unwrap_or_default(); //input.to_string().getBytes(encoding);
encodeBinary(&msgBytes, p, msgBytes.len() as u32, BYTE_COMPACTION, &mut sb);
},
Compaction:: NUMERIC=>
{sb.push(char::from_u32( LATCH_TO_NUMERIC).unwrap());
encodeNumeric(&input, p, len as u32, &mut sb);},
_=>{
let mut encodingMode = TEXT_COMPACTION; //Default mode, see 4.4.2.1
while p < len as u32 {
while p < len as u32 && input.isECI(p)? {
encodingECI(input.getECIValue(p as usize)?, &mut sb)?;
p+=1;
}
if p >= len as u32 {
break;
}
let n = determineConsecutiveDigitCount(&input, p);
if n >= 13 {
sb.push(char::from_u32( LATCH_TO_NUMERIC).unwrap());
encodingMode = NUMERIC_COMPACTION;
textSubMode = SUBMODE_ALPHA; //Reset after latch
encodeNumeric(&input, p, n, &mut sb);
p += n;
} else {
let t = determineConsecutiveTextCount(&input, p);
if t >= 5 || n == len as u32{
if encodingMode != TEXT_COMPACTION {
sb.push(char::from_u32(LATCH_TO_TEXT).unwrap());
encodingMode = TEXT_COMPACTION;
textSubMode = SUBMODE_ALPHA; //start with submode alpha after latch
}
textSubMode = encodeText(&input, p, t, &mut sb, textSubMode)?;
p += t;
} else {
let mut b = determineConsecutiveBinaryCount(&input, p, if autoECI {None} else {encoding})?;
if b == 0 {
b = 1;
}
let bytes = if autoECI {None} else {
let str = input.subSequence(p as usize, (p + b) as usize)?.iter().collect::<String>();
if let Ok(enc_str) = encoding.as_ref().unwrap().encode(&str, encoding::EncoderTrap::Strict) {
Some(enc_str)
}else{
None
}
};//.getBytes(encoding))};
// if let Some(byts) = bytes {
let bytes_ok = if let Some(_) = bytes { true } else {false};
if ((bytes_ok && b == 1) || (!bytes_ok && bytes.as_ref().unwrap().len() == 1))
&& encodingMode == TEXT_COMPACTION {
//Switch for one byte (instead of latch)
if autoECI {
encodeMultiECIBinary(&input, p, 1, TEXT_COMPACTION, &mut sb)?;
} else {
encodeBinary(bytes.as_ref().unwrap(), 0, 1, TEXT_COMPACTION, &mut sb);
}
} else {
//Mode latch performed by encodeBinary()
if autoECI {
encodeMultiECIBinary(&input, p, p + b, encodingMode, &mut sb)?;
} else {
encodeBinary(bytes.as_ref().unwrap(), 0, bytes.as_ref().unwrap().len() as u32, encodingMode, &mut sb);
}
encodingMode = BYTE_COMPACTION;
textSubMode = SUBMODE_ALPHA; //Reset after latch
}
p += b;
}
}
}},
}
Ok(sb)
}
/**
* Encode parts of the message using Text Compaction as described in ISO/IEC 15438:2001(E),
* chapter 4.4.2.
*
* @param input the input
* @param startpos the start position within the message
* @param count the number of characters to encode
* @param sb receives the encoded codewords
* @param initialSubmode should normally be SUBMODE_ALPHA
* @return the text submode in which this method ends
*/
fn encodeText<T:ECIInput+?Sized>( input:&Box<T>,
startpos:u32,
count:u32,
sb:&mut String,
initialSubmode:u32) -> Result<u32,Exceptions> {
let mut tmp = String::with_capacity(count as usize);
let mut submode = initialSubmode;
let mut idx = 0;
loop {
if input.isECI(startpos + idx)? {
encodingECI(input.getECIValue((startpos + idx) as usize)?, sb)?;
idx+=1;
} else {
let ch = input.charAt((startpos + idx) as usize)?;
match submode {
SUBMODE_ALPHA=>
if isAlphaUpper(ch) {
if ch == ' ' {
tmp.push( 26 as char); //space
} else {
tmp.push(char::from_u32(ch as u32- 65).unwrap());
}
} else {
if isAlphaLower(ch) {
submode = SUBMODE_LOWER;
tmp.push( 27 as char); //ll
continue;
} else if isMixed(ch) {
submode = SUBMODE_MIXED;
tmp.push( 28 as char); //ml
continue;
} else {
tmp.push( 29 as char); //ps
tmp.push(char::from_u32(PUNCTUATION[ch as usize] as u32).unwrap());
break;
}
},
// break;
SUBMODE_LOWER=>
if isAlphaLower(ch) {
if ch == ' ' {
tmp.push( 26 as char); //space
} else {
tmp.push(char::from_u32( ch as u32 - 97).unwrap());
}
} else {
if isAlphaUpper(ch) {
tmp.push( 27 as char); //as
tmp.push(char::from_u32( ch as u32 - 65).unwrap());
//space cannot happen here, it is also in "Lower"
break;
} else if isMixed(ch) {
submode = SUBMODE_MIXED;
tmp.push( 28 as char); //ml
continue;
} else {
tmp.push( 29 as char); //ps
tmp.push(char::from_u32( PUNCTUATION[ch as usize] as u32).unwrap());
break;
}
},
// break;
SUBMODE_MIXED=>
if isMixed(ch) {
tmp.push( char::from_u32(MIXED[ch as usize] as u32).unwrap());
} else {
if isAlphaUpper(ch) {
submode = SUBMODE_ALPHA;
tmp.push(28 as char); //al
continue;
} else if isAlphaLower(ch) {
submode = SUBMODE_LOWER;
tmp.push( 27 as char); //ll
continue;
} else {
if startpos + idx + 1 < count {
if !input.isECI(startpos + idx + 1)? && isPunctuation(input.charAt((startpos + idx + 1) as usize)?) {
submode = SUBMODE_PUNCTUATION;
tmp.push( 25 as char); //pl
continue;
}
}
tmp.push( 29 as char); //ps
tmp.push(char::from_u32(PUNCTUATION[ch as usize] as u32).unwrap());
}
},
_=> //SUBMODE_PUNCTUATION
if isPunctuation(ch) {
tmp.push(char::from_u32( PUNCTUATION[ch as usize] as u32).unwrap());
} else {
submode = SUBMODE_ALPHA;
tmp.push( 29 as char); //al
continue;
},
}
idx+=1;
if idx >= count {
break;
}
}
}
let mut h = 0 as char;
let len = tmp.chars().count();
for i in 0..len {
// for (int i = 0; i < len; i++) {
let odd = (i % 2) != 0;
if odd {
h = char::from_u32( (h as u32 * 30) + tmp.chars().nth(i).unwrap() as u32).unwrap();
sb.push(h);
} else {
h = tmp.chars().nth(i).unwrap();
}
}
if (len % 2) != 0 {
sb.push(char::from_u32( (h as u32* 30) + 29).unwrap()); //ps
}
Ok(submode)
}
/**
* Encode all of the message using Byte Compaction as described in ISO/IEC 15438:2001(E)
*
* @param input the input
* @param startpos the start position within the message
* @param count the number of bytes to encode
* @param startmode the mode from which this method starts
* @param sb receives the encoded codewords
*/
fn encodeMultiECIBinary<T:ECIInput + ?Sized>( input:&Box<T>,
startpos:u32,
count:u32,
startmode:u32,
sb:&mut String) -> Result<(),Exceptions> {
let end = (startpos + count).min(input.length() as u32);
let mut localStart = startpos;
loop {
//encode all leading ECIs and advance localStart
while localStart < end && input.isECI(localStart)? {
encodingECI(input.getECIValue(localStart as usize)?, sb)?;
localStart+=1;
}
let mut localEnd = localStart;
//advance end until before the next ECI
while localEnd < end && !input.isECI(localEnd)? {
localEnd+=1;
}
let localCount = localEnd - localStart;
if localCount <= 0 {
//done
break;
} else {
//encode the segment
encodeBinary(&subBytes(input, localStart, localEnd),
0, localCount, if localStart == startpos {startmode} else {BYTE_COMPACTION}, sb);
localStart = localEnd;
}
}
Ok(())
}
pub fn subBytes<T:ECIInput+?Sized>( input:&Box<T>, start:u32, end:u32) -> Vec<u8>{
let count = (end - start) as usize;
let mut result = vec![0_u8;count];
for i in start as usize..end as usize {
// for (int i = start; i < end; i++) {
result[i - start as usize] = input.charAt(i).unwrap() as u8 ;
}
return result;
}
/**
* Encode parts of the message using Byte Compaction as described in ISO/IEC 15438:2001(E),
* chapter 4.4.3. The Unicode characters will be converted to binary using the cp437
* codepage.
*
* @param bytes the message converted to a byte array
* @param startpos the start position within the message
* @param count the number of bytes to encode
* @param startmode the mode from which this method starts
* @param sb receives the encoded codewords
*/
fn encodeBinary( bytes:&[u8],
startpos: u32,
count: u32,
startmode: u32,
sb:&mut String) {
if count == 1 && startmode == TEXT_COMPACTION {
sb.push(char::from_u32( SHIFT_TO_BYTE).unwrap());
} else {
if (count % 6) == 0 {
sb.push(char::from_u32( LATCH_TO_BYTE).unwrap());
} else {
sb.push(char::from_u32( LATCH_TO_BYTE_PADDED).unwrap());
}
}
let mut idx = startpos;
// Encode sixpacks
if count >= 6 {
let mut chars = [0 as char; 5];//new char[5];
while (startpos + count - idx) >= 6 {
let mut t: i64 = 0;
for i in 0..6 {
// for (int i = 0; i < 6; i++) {
t <<= 8;
t += bytes[idx as usize + i as usize] as i64;
}
for i in 0..5 {
// for (int i = 0; i < 5; i++) {
chars[i] = char::from_u32((t % 900)as u32).unwrap();
t /= 900;
}
for i in (0..chars.len()).rev() {
// for (int i = chars.length - 1; i >= 0; i--) {
sb.push(chars[i]);
}
idx += 6;
}
}
//Encode rest (remaining n<5 bytes if any)
for i in idx..(startpos+count) {
// for (int i = idx; i < startpos + count; i++) {
let ch = bytes[i as usize];
sb.push( ch as char);
}
}
fn encodeNumeric<T:ECIInput+?Sized>( input:&Box<T>, startpos:u32, count:u32, sb:&mut String) {
let mut idx = 0;
let mut tmp = String::with_capacity(count as usize / 3 + 1);
let num900 : u128 = 900;
let num0 : u128 = 0;
while idx < count {
tmp.clear();
let len = 44.min(count as isize - idx as isize);
let part = format!("1{}", input.subSequence((startpos + idx) as usize, (startpos + idx + len as u32) as usize).unwrap().iter().collect::<String>());
let mut bigint: u128 = part.parse().unwrap();
loop {
tmp.push(char::from_u32( (bigint % num900) as u32).unwrap());
bigint = bigint / num900;
if !(!bigint == num0) { break; }
} //while (!bigint.equals(num0));
//Reverse temporary string
let mut i = tmp.chars().count() as isize - 1;
while i >= 0 {
// for (int i = tmp.length() - 1; i >= 0; i--) {
sb.push(tmp.chars().nth(i as usize).unwrap());
i-=1;
}
idx += len as u32;
}
}
fn isDigit( ch:char) -> bool{
return ch >= '0' && ch <= '9';
}
fn isAlphaUpper( ch:char) -> bool{
return ch == ' ' || (ch >= 'A' && ch <= 'Z');
}
fn isAlphaLower( ch:char) -> bool{
return ch == ' ' || (ch >= 'a' && ch <= 'z');
}
fn isMixed( ch:char) -> bool{
return MIXED[ch as usize] != -1;
}
fn isPunctuation( ch:char) -> bool{
return PUNCTUATION[ch as usize] != -1;
}
fn isText( ch:char) -> bool{
return ch == '\t' || ch == '\n' || ch == '\r' || (ch as u32 >= 32 && ch as u32 <= 126);
}
/**
* Determines the number of consecutive characters that are encodable using numeric compaction.
*
* @param input the input
* @param startpos the start position within the input
* @return the requested character count
*/
fn determineConsecutiveDigitCount<T:ECIInput+?Sized>( input:&Box<T>, startpos:u32) -> u32{
let mut count = 0;
let len = input.length();
let mut idx = startpos as usize;
if idx < len {
while idx < len && !input.isECI(idx as u32).unwrap() && isDigit(input.charAt(idx).unwrap()) {
count+=1;
idx+=1;
}
}
count
}
/**
* Determines the number of consecutive characters that are encodable using text compaction.
*
* @param input the input
* @param startpos the start position within the input
* @return the requested character count
*/
fn determineConsecutiveTextCount<T:ECIInput+?Sized>( input:&Box<T>, startpos:u32) -> u32{
let len = input.length();
let mut idx = startpos as usize;
while idx < len {
let mut numericCount = 0;
while numericCount < 13 && idx < len && !input.isECI(idx as u32).unwrap() && isDigit(input.charAt(idx).unwrap()) {
numericCount+=1;
idx+=1;
}
if numericCount >= 13 {
return (idx - startpos as usize - numericCount) as u32;
}
if numericCount > 0 {
//Heuristic: All text-encodable chars or digits are binary encodable
continue;
}
//Check if character is encodable
if input.isECI(idx as u32).unwrap() || !isText(input.charAt(idx).unwrap()) {
break;
}
idx+=1;
}
(idx - startpos as usize) as u32
}
/**
* Determines the number of consecutive characters that are encodable using binary compaction.
*
* @param input the input
* @param startpos the start position within the message
* @param encoding the charset used to convert the message to a byte array
* @return the requested character count
*/
fn determineConsecutiveBinaryCount<T: ECIInput+?Sized+'static>( input:&Box<T>, startpos:u32, encoding:Option<EncodingRef>)
-> Result<u32,Exceptions> {
// CharsetEncoder encoder = encoding == null ? null : encoding.newEncoder();
let len = input.length();
let mut idx = startpos as usize;
while idx < len {
let mut numericCount = 0;
let mut i = idx;
while numericCount < 13 && !input.isECI(i as u32).unwrap() && isDigit(input.charAt(i).unwrap()) {
numericCount+=1;
//textCount++;
i = idx + numericCount;
if i >= len {
break;
}
}
if numericCount >= 13 {
return Ok(idx as u32- startpos);
}
if let Some(encoder) = encoding {
let can_encode = encoder.encode(&input.charAt(idx)?.to_string(), encoding::EncoderTrap::Strict).is_ok();
// if encoder != null && !encoder.canEncode(input.charAt(idx)) {
if !can_encode {
assert!(TypeId::of::<T>() == TypeId::of::<NoECIInput>());
// assert!(input instanceof NoECIInput);
let ch = input.charAt(idx).unwrap();
return Err(Exceptions::WriterException(format!("Non-encodable character detected: {} (Unicode: {})", ch, ch as u32)));
}}
idx+=1;
}
Ok(idx as u32- startpos)
}
fn encodingECI( eci:i32, sb:&mut String) -> Result<(),Exceptions> {
if eci >= 0 && eci < 900 {
sb.push(char::from_u32( ECI_CHARSET).unwrap());
sb.push(char::from_u32( eci as u32).unwrap());
} else if eci < 810900 {
sb.push(char::from_u32( ECI_GENERAL_PURPOSE).unwrap());
sb.push(char::from_u32( (eci / 900 - 1) as u32).unwrap());
sb.push(char::from_u32( (eci % 900) as u32).unwrap());
} else if eci < 811800 {
sb.push(char::from_u32( ECI_USER_DEFINED).unwrap());
sb.push(char::from_u32( (810900 - eci) as u32).unwrap());
} else {
return Err(Exceptions::WriterException(format!("ECI number not in valid range from 0..811799, but was {}" , eci)));
}
Ok(())
}
struct NoECIInput(String);
impl ECIInput for NoECIInput {
fn length(&self) -> usize {
self.0.chars().count()
}
fn charAt(&self, index: usize) -> Result<char, Exceptions> {
if let Some(ch) = self.0.chars().nth(index) {
Ok(ch)
}else {
Err(Exceptions::IndexOutOfBoundsException("".to_owned()))
}
}
fn subSequence(&self, start: usize, end: usize) -> Result<Vec<char>, Exceptions> {
let res: Vec<char> = self.0.chars().skip(start).take(end-start).collect();
Ok(res)
}
fn isECI(&self, _index: u32) -> Result<bool, Exceptions> {
Ok(false)
}
fn getECIValue(&self, _index: usize) -> Result<i32, Exceptions> {
Ok(-1)
}
fn haveNCharacters(&self, index: usize, n: usize) -> bool {
index + n <= self.0.len()
}
}
impl NoECIInput {
pub fn new( input:String) -> Self {
Self(input)
}
}
impl Display for NoECIInput {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f,"{}", self.0)
}
}