From 8265242784be6f5493f1ede4b1fd1432b1666a54 Mon Sep 17 00:00:00 2001 From: AlexGeller1 <87009702+AlexGeller1@users.noreply.github.com> Date: Sun, 13 Mar 2022 15:40:09 +0100 Subject: [PATCH] Share the ECI string builder of the data matrix decoder with the PDF417 decoder (#1508) * Shared the ECI string builder of the datamatrix decoder with the PDF417 decoder --- .../google/zxing/common/ECIStringBuilder.java | 124 ++++++++++++++++++ .../decoder/DecodedBitStreamParser.java | 71 +--------- .../decoder/DecodedBitStreamParser.java | 91 ++----------- 3 files changed, 139 insertions(+), 147 deletions(-) create mode 100644 core/src/main/java/com/google/zxing/common/ECIStringBuilder.java diff --git a/core/src/main/java/com/google/zxing/common/ECIStringBuilder.java b/core/src/main/java/com/google/zxing/common/ECIStringBuilder.java new file mode 100644 index 000000000..c755ae345 --- /dev/null +++ b/core/src/main/java/com/google/zxing/common/ECIStringBuilder.java @@ -0,0 +1,124 @@ +/* + * Copyright 2022 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.common; + +import com.google.zxing.FormatException; + +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; + +/** + * Class that converts a sequence of ECIs and bytes into a string + * + * @author Alex Geller + */ +public final class ECIStringBuilder { + private StringBuilder currentBytes; + private StringBuilder result; + private Charset currentCharset = StandardCharsets.ISO_8859_1; + + public ECIStringBuilder() { + currentBytes = new StringBuilder(); + } + public ECIStringBuilder(int initialCapacity) { + currentBytes = new StringBuilder(initialCapacity); + } + + /** + * Appends {@code value} as a byte value + */ + public void append(char value) { + currentBytes.append((char) (value & 0xff)); + } + + /** + * Appends {@code value} as a byte value (not its string representation) + */ + public void append(byte value) { + currentBytes.append((char) (value & 0xff)); + } + + /** + * Appends the characters in {@code value} as bytes values + */ + public void append(String value) { + currentBytes.append(value); + } + + /** + * Append the string repesentation of {@code value} (short for {@code append(String.valueOf(value))}) + */ + public void append(int value) { + append(String.valueOf(value)); + } + + public void appendECI(int value) throws FormatException { + encodeCurrentBytesIfAny(); + CharacterSetECI characterSetECI = CharacterSetECI.getCharacterSetECIByValue(value); + if (characterSetECI == null) { + throw FormatException.getFormatInstance(); + } + currentCharset = characterSetECI.getCharset(); + } + + private void encodeCurrentBytesIfAny() { + if (currentCharset.equals(StandardCharsets.ISO_8859_1)) { + if (currentBytes.length() > 0) { + if (result == null) { + result = currentBytes; + currentBytes = new StringBuilder(); + } else { + result.append(currentBytes); + currentBytes = new StringBuilder(); + } + } + } else if (currentBytes.length() > 0) { + byte[] bytes = currentBytes.toString().getBytes(StandardCharsets.ISO_8859_1); + currentBytes = new StringBuilder(); + if (result == null) { + result = new StringBuilder(new String(bytes, currentCharset)); + } else { + result.append(new String(bytes, currentCharset)); + } + } + } + + /** + * Appends the characters from {@code value} (unlike all other append methods of this class who append bytes) + */ + public void appendCharacters(StringBuilder value) { + encodeCurrentBytesIfAny(); + result.append(value); + } + + /** + * Short for {@code toString().length()} (if possible, use {@link #isEmpty()} instead) + */ + public int length() { + return toString().length(); + } + + public boolean isEmpty() { + return currentBytes.length() == 0 && (result == null || result.length() == 0); + } + + @Override + public String toString() { + encodeCurrentBytesIfAny(); + return result == null ? "" : result.toString(); + } +} diff --git a/core/src/main/java/com/google/zxing/datamatrix/decoder/DecodedBitStreamParser.java b/core/src/main/java/com/google/zxing/datamatrix/decoder/DecodedBitStreamParser.java index 586c37712..497b8c38a 100644 --- a/core/src/main/java/com/google/zxing/datamatrix/decoder/DecodedBitStreamParser.java +++ b/core/src/main/java/com/google/zxing/datamatrix/decoder/DecodedBitStreamParser.java @@ -19,10 +19,9 @@ package com.google.zxing.datamatrix.decoder; import com.google.zxing.FormatException; import com.google.zxing.common.BitSource; import com.google.zxing.common.DecoderResult; -import com.google.zxing.common.CharacterSetECI; +import com.google.zxing.common.ECIStringBuilder; import java.nio.charset.StandardCharsets; -import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; @@ -128,7 +127,7 @@ final class DecodedBitStreamParser { } } while (mode != Mode.PAD_ENCODE && bits.available() > 0); if (resultTrailer.length() > 0) { - result.append(resultTrailer); + result.appendCharacters(resultTrailer); } if (isECIencoded) { // Examples for this numbers can be found in this documentation of a hardware barcode scanner: @@ -588,70 +587,4 @@ final class DecodedBitStreamParser { return tempVariable >= 0 ? tempVariable : tempVariable + 256; } - private static final class ECIStringBuilder { - private StringBuilder currentBytes; - private StringBuilder currentChars; - private Charset currentCharset = StandardCharsets.ISO_8859_1; - private String result = null; - private boolean hadECI = false; - - private ECIStringBuilder(int initialCapacity) { - currentBytes = new StringBuilder(initialCapacity); - } - - private void append(char value) { - currentBytes.append(value); - } - - private void append(String value) { - currentBytes.append(value); - } - - private void append(int value) { - currentBytes.append(value); - } - - private void appendECI(int value) throws FormatException { - encodeCurrentBytesIfAny(); - CharacterSetECI characterSetECI = CharacterSetECI.getCharacterSetECIByValue(value); - if (characterSetECI == null) { - throw FormatException.getFormatInstance(new RuntimeException("Unsupported ECI value " + value)); - } - currentCharset = characterSetECI.getCharset(); - } - - private void encodeCurrentBytesIfAny() { - if (!hadECI) { - currentChars = currentBytes; - currentBytes = new StringBuilder(); - hadECI = true; - } else if (currentBytes.length() > 0) { - byte[] bytes = new byte[currentBytes.length()]; - for (int i = 0; i < bytes.length; i++) { - bytes[i] = (byte) (currentBytes.charAt(i) & 0xff); - } - currentChars.append(new String(bytes, currentCharset)); - currentBytes.setLength(0); - } - } - - private void append(StringBuilder value) { - encodeCurrentBytesIfAny(); - currentChars.append(value); - } - - /** - * returns the length of toString() - */ - public int length() { - return toString().length(); - } - - public String toString() { - encodeCurrentBytesIfAny(); - result = result == null ? currentChars.toString() : result + currentChars.toString(); - currentChars.setLength(0); - return result; - } - } } diff --git a/core/src/main/java/com/google/zxing/pdf417/decoder/DecodedBitStreamParser.java b/core/src/main/java/com/google/zxing/pdf417/decoder/DecodedBitStreamParser.java index 67ecc0851..56a7e2e5f 100644 --- a/core/src/main/java/com/google/zxing/pdf417/decoder/DecodedBitStreamParser.java +++ b/core/src/main/java/com/google/zxing/pdf417/decoder/DecodedBitStreamParser.java @@ -17,14 +17,12 @@ package com.google.zxing.pdf417.decoder; import com.google.zxing.FormatException; -import com.google.zxing.common.CharacterSetECI; +import com.google.zxing.common.ECIStringBuilder; import com.google.zxing.common.DecoderResult; import com.google.zxing.pdf417.PDF417ResultMetadata; -import java.io.ByteArrayOutputStream; import java.math.BigInteger; import java.util.Arrays; -import java.io.UnsupportedEncodingException; /** *

This class contains the methods for decoding the PDF417 codewords.

@@ -100,7 +98,7 @@ final class DecodedBitStreamParser { } static DecoderResult decode(int[] codewords, String ecLevel) throws FormatException { - ECIOutput result = new ECIOutput(codewords.length * 2); + ECIStringBuilder result = new ECIStringBuilder(codewords.length * 2); int codeIndex = textCompaction(codewords, 1, result); PDF417ResultMetadata resultMetadata = new PDF417ResultMetadata(); while (codeIndex < codewords[0]) { @@ -205,37 +203,37 @@ final class DecodedBitStreamParser { codeIndex++; switch (codewords[codeIndex]) { case MACRO_PDF417_OPTIONAL_FIELD_FILE_NAME: - ECIOutput fileName = new ECIOutput(); + ECIStringBuilder fileName = new ECIStringBuilder(); codeIndex = textCompaction(codewords, codeIndex + 1, fileName); resultMetadata.setFileName(fileName.toString()); break; case MACRO_PDF417_OPTIONAL_FIELD_SENDER: - ECIOutput sender = new ECIOutput(); + ECIStringBuilder sender = new ECIStringBuilder(); codeIndex = textCompaction(codewords, codeIndex + 1, sender); resultMetadata.setSender(sender.toString()); break; case MACRO_PDF417_OPTIONAL_FIELD_ADDRESSEE: - ECIOutput addressee = new ECIOutput(); + ECIStringBuilder addressee = new ECIStringBuilder(); codeIndex = textCompaction(codewords, codeIndex + 1, addressee); resultMetadata.setAddressee(addressee.toString()); break; case MACRO_PDF417_OPTIONAL_FIELD_SEGMENT_COUNT: - ECIOutput segmentCount = new ECIOutput(); + ECIStringBuilder segmentCount = new ECIStringBuilder(); codeIndex = numericCompaction(codewords, codeIndex + 1, segmentCount); resultMetadata.setSegmentCount(Integer.parseInt(segmentCount.toString())); break; case MACRO_PDF417_OPTIONAL_FIELD_TIME_STAMP: - ECIOutput timestamp = new ECIOutput(); + ECIStringBuilder timestamp = new ECIStringBuilder(); codeIndex = numericCompaction(codewords, codeIndex + 1, timestamp); resultMetadata.setTimestamp(Long.parseLong(timestamp.toString())); break; case MACRO_PDF417_OPTIONAL_FIELD_CHECKSUM: - ECIOutput checksum = new ECIOutput(); + ECIStringBuilder checksum = new ECIStringBuilder(); codeIndex = numericCompaction(codewords, codeIndex + 1, checksum); resultMetadata.setChecksum(Integer.parseInt(checksum.toString())); break; case MACRO_PDF417_OPTIONAL_FIELD_FILE_SIZE: - ECIOutput fileSize = new ECIOutput(); + ECIStringBuilder fileSize = new ECIStringBuilder(); codeIndex = numericCompaction(codewords, codeIndex + 1, fileSize); resultMetadata.setFileSize(Long.parseLong(fileSize.toString())); break; @@ -276,7 +274,7 @@ final class DecodedBitStreamParser { * @param result The decoded data is appended to the result. * @return The next index into the codeword array. */ - private static int textCompaction(int[] codewords, int codeIndex, ECIOutput result) throws FormatException { + private static int textCompaction(int[] codewords, int codeIndex, ECIStringBuilder result) throws FormatException { // 2 character per codeword int[] textCompactionData = new int[(codewords[0] - codeIndex) * 2]; // Used to hold the byte compaction value if there is a mode shift @@ -353,7 +351,7 @@ final class DecodedBitStreamParser { private static Mode decodeTextCompaction(int[] textCompactionData, int[] byteCompactionData, int length, - ECIOutput result, + ECIStringBuilder result, Mode startMode) { // Beginning from an initial state // The default compaction mode for PDF417 in effect at the start of each symbol shall always be Text @@ -547,7 +545,7 @@ final class DecodedBitStreamParser { private static int byteCompaction(int mode, int[] codewords, int codeIndex, - ECIOutput result) throws FormatException { + ECIStringBuilder result) throws FormatException { boolean end = false; while (codeIndex < codewords[0] && !end) { @@ -602,7 +600,7 @@ final class DecodedBitStreamParser { * @param result The decoded data is appended to the result. * @return The next index into the codeword array. */ - private static int numericCompaction(int[] codewords, int codeIndex, ECIOutput result) throws FormatException { + private static int numericCompaction(int[] codewords, int codeIndex, ECIStringBuilder result) throws FormatException { int count = 0; boolean end = false; @@ -697,67 +695,4 @@ final class DecodedBitStreamParser { return resultString.substring(1); } - private static final class ECIOutput { - private boolean needFlush = false; - private String encodingName = "ISO-8859-1"; - private ByteArrayOutputStream bytes = new ByteArrayOutputStream(); - private StringBuilder result; - - private ECIOutput() { - result = new StringBuilder(); - } - - private ECIOutput(int size) { - result = new StringBuilder(size); - } - - private void append(byte value) { - bytes.write(value); - needFlush = true; - } - - private void append(char value) { - bytes.write(value & 0xff); - needFlush = true; - } - - private void append(String s) { - for (int i = 0; i < s.length(); i++) { - append(s.charAt(i)); - } - } - - private void appendECI(int value) throws FormatException { - flush(); - bytes = new ByteArrayOutputStream(); - CharacterSetECI charsetECI = CharacterSetECI.getCharacterSetECIByValue(value); - if (charsetECI == null) { - throw FormatException.getFormatInstance(); - } - encodingName = charsetECI.name(); - } - - private void flush() { - if (needFlush) { - needFlush = false; - try { - result.append(bytes.toString(encodingName)); - } catch (UnsupportedEncodingException uee) { - // can't happen - throw new IllegalStateException(uee); - } - } - } - - private boolean isEmpty() { - return !needFlush && result.length() == 0; - } - - @Override - public String toString() { - flush(); - return result.toString(); - } - } - }