From d9b0d46f7157222d1f8b58e98f61df041a3ae384 Mon Sep 17 00:00:00 2001 From: Henry Schimke Date: Sat, 17 Dec 2022 18:47:38 -0600 Subject: [PATCH] high level encoder builds --- src/common/eci_input.rs | 6 +- src/common/minimal_eci_input.rs | 4 +- src/datamatrix/encoder/minimal_encoder.rs | 4 +- src/pdf417/encoder/BarcodeMatrix.java | 70 -- src/pdf417/encoder/BarcodeRow.java | 79 -- .../encoder/PDF417HighLevelEncoder.java | 712 ---------------- src/pdf417/encoder/barcode_batrix.rs | 81 ++ src/pdf417/encoder/barcode_row.rs | 84 ++ .../{Compaction.java => compaction.rs} | 11 +- .../{Dimensions.java => dimensions.rs} | 63 +- src/pdf417/encoder/mod.rs | 14 + ...ction.java => pdf_417_error_correction.rs} | 208 ++--- .../encoder/pdf_417_high_level_encoder.rs | 761 ++++++++++++++++++ 13 files changed, 1093 insertions(+), 1004 deletions(-) delete mode 100644 src/pdf417/encoder/BarcodeMatrix.java delete mode 100644 src/pdf417/encoder/BarcodeRow.java delete mode 100644 src/pdf417/encoder/PDF417HighLevelEncoder.java create mode 100644 src/pdf417/encoder/barcode_batrix.rs create mode 100644 src/pdf417/encoder/barcode_row.rs rename src/pdf417/encoder/{Compaction.java => compaction.rs} (86%) rename src/pdf417/encoder/{Dimensions.java => dimensions.rs} (56%) rename src/pdf417/encoder/{PDF417ErrorCorrection.java => pdf_417_error_correction.rs} (61%) create mode 100644 src/pdf417/encoder/pdf_417_high_level_encoder.rs diff --git a/src/common/eci_input.rs b/src/common/eci_input.rs index c4287cf..297ba0f 100644 --- a/src/common/eci_input.rs +++ b/src/common/eci_input.rs @@ -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; + fn getECIValue(&self, index: usize) -> Result; fn haveNCharacters(&self, index: usize, n: usize) -> bool; } diff --git a/src/common/minimal_eci_input.rs b/src/common/minimal_eci_input.rs index f52dd14..bfa48e1 100644 --- a/src/common/minimal_eci_input.rs +++ b/src/common/minimal_eci_input.rs @@ -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 { + fn getECIValue(&self, index: usize) -> Result { 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 { diff --git a/src/datamatrix/encoder/minimal_encoder.rs b/src/datamatrix/encoder/minimal_encoder.rs index 5828b60..05c8e86 100755 --- a/src/datamatrix/encoder/minimal_encoder.rs +++ b/src/datamatrix/encoder/minimal_encoder.rs @@ -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 { self.internal.isFNC1(index) } - fn getECIValue(&self, index: usize) -> Result { + fn getECIValue(&self, index: usize) -> Result { self.internal.getECIValue(index) } } diff --git a/src/pdf417/encoder/BarcodeMatrix.java b/src/pdf417/encoder/BarcodeMatrix.java deleted file mode 100644 index b4f6980..0000000 --- a/src/pdf417/encoder/BarcodeMatrix.java +++ /dev/null @@ -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; - } -} diff --git a/src/pdf417/encoder/BarcodeRow.java b/src/pdf417/encoder/BarcodeRow.java deleted file mode 100644 index ad93d27..0000000 --- a/src/pdf417/encoder/BarcodeRow.java +++ /dev/null @@ -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; - } -} diff --git a/src/pdf417/encoder/PDF417HighLevelEncoder.java b/src/pdf417/encoder/PDF417HighLevelEncoder.java deleted file mode 100644 index 282cc73..0000000 --- a/src/pdf417/encoder/PDF417HighLevelEncoder.java +++ /dev/null @@ -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; - } - } -} diff --git a/src/pdf417/encoder/barcode_batrix.rs b/src/pdf417/encoder/barcode_batrix.rs new file mode 100644 index 0000000..358eef5 --- /dev/null +++ b/src/pdf417/encoder/barcode_batrix.rs @@ -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, + 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> { + self.getScaledMatrix(1, 1) + } + + pub fn getScaledMatrix(&self, xScale: usize, yScale: usize) -> Vec> { + 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 + } +} diff --git a/src/pdf417/encoder/barcode_row.rs b/src/pdf417/encoder/barcode_row.rs new file mode 100644 index 0000000..76cee96 --- /dev/null +++ b/src/pdf417/encoder/barcode_row.rs @@ -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, + //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 { + 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 + } +} diff --git a/src/pdf417/encoder/Compaction.java b/src/pdf417/encoder/compaction.rs similarity index 86% rename from src/pdf417/encoder/Compaction.java rename to src/pdf417/encoder/compaction.rs index 67cf4b8..9c4bece 100644 --- a/src/pdf417/encoder/Compaction.java +++ b/src/pdf417/encoder/compaction.rs @@ -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 } diff --git a/src/pdf417/encoder/Dimensions.java b/src/pdf417/encoder/dimensions.rs similarity index 56% rename from src/pdf417/encoder/Dimensions.java rename to src/pdf417/encoder/dimensions.rs index 83a736b..29a7f1f 100644 --- a/src/pdf417/encoder/Dimensions.java +++ b/src/pdf417/encoder/dimensions.rs @@ -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 + } } diff --git a/src/pdf417/encoder/mod.rs b/src/pdf417/encoder/mod.rs index 8b13789..aef8bbf 100644 --- a/src/pdf417/encoder/mod.rs +++ b/src/pdf417/encoder/mod.rs @@ -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; \ No newline at end of file diff --git a/src/pdf417/encoder/PDF417ErrorCorrection.java b/src/pdf417/encoder/pdf_417_error_correction.rs similarity index 61% rename from src/pdf417/encoder/PDF417ErrorCorrection.java rename to src/pdf417/encoder/pdf_417_error_correction.rs index 6528172..eb36213 100644 --- a/src/pdf417/encoder/PDF417ErrorCorrection.java +++ b/src/pdf417/encoder/pdf_417_error_correction.rs @@ -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;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 { + 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 { + 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 { + 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) } diff --git a/src/pdf417/encoder/pdf_417_high_level_encoder.rs b/src/pdf417/encoder/pdf_417_high_level_encoder.rs new file mode 100644 index 0000000..8c9f736 --- /dev/null +++ b/src/pdf417/encoder/pdf_417_high_level_encoder.rs @@ -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, autoECI:bool) + -> Result { +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; + 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::(); + 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( input:&Box, + startpos:u32, + count:u32, + sb:&mut String, + initialSubmode:u32) -> Result { + 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( input:&Box, + 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( input:&Box, start:u32, end:u32) -> Vec{ + 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( input:&Box, 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::()); + 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( input:&Box, 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( input:&Box, 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( input:&Box, startpos:u32, encoding:Option) + -> Result { + // 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::() == TypeId::of::()); + // 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 { + 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, Exceptions> { + let res: Vec = self.0.chars().skip(start).take(end-start).collect(); + Ok(res) + } + + fn isECI(&self, _index: u32) -> Result { + Ok(false) + } + + fn getECIValue(&self, _index: usize) -> Result { + 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) + } +}