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
This commit is contained in:
AlexGeller1 2022-03-13 15:40:09 +01:00 committed by GitHub
parent ce1a1a53cf
commit 8265242784
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 139 additions and 147 deletions

View file

@ -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();
}
}

View file

@ -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;
}
}
}

View file

@ -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;
/**
* <p>This class contains the methods for decoding the PDF417 codewords.</p>
@ -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();
}
}
}