Add support for multiple character-ECIs in data matrix decoder (#1474)

* Added support for multiple character-ECIs

* - Changed from ArrayList<Byte> to ByteArrayOutputStream
- Added blank lines

* Improved performance for the general case (without ECI)
This commit is contained in:
AlexGeller1 2022-01-05 16:32:36 +01:00 committed by GitHub
parent 9b972bbb07
commit c4266bf09c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -19,8 +19,10 @@ package com.google.zxing.datamatrix.decoder;
import com.google.zxing.FormatException; import com.google.zxing.FormatException;
import com.google.zxing.common.BitSource; import com.google.zxing.common.BitSource;
import com.google.zxing.common.DecoderResult; import com.google.zxing.common.DecoderResult;
import com.google.zxing.common.CharacterSetECI;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.nio.charset.Charset;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.HashSet; import java.util.HashSet;
@ -87,7 +89,7 @@ final class DecodedBitStreamParser {
static DecoderResult decode(byte[] bytes) throws FormatException { static DecoderResult decode(byte[] bytes) throws FormatException {
BitSource bits = new BitSource(bytes); BitSource bits = new BitSource(bytes);
StringBuilder result = new StringBuilder(100); ECIStringBuilder result = new ECIStringBuilder(100);
StringBuilder resultTrailer = new StringBuilder(0); StringBuilder resultTrailer = new StringBuilder(0);
List<byte[]> byteSegments = new ArrayList<>(1); List<byte[]> byteSegments = new ArrayList<>(1);
Mode mode = Mode.ASCII_ENCODE; Mode mode = Mode.ASCII_ENCODE;
@ -116,6 +118,7 @@ final class DecodedBitStreamParser {
decodeBase256Segment(bits, result, byteSegments); decodeBase256Segment(bits, result, byteSegments);
break; break;
case ECI_ENCODE: case ECI_ENCODE:
decodeECISegment(bits, result);
isECIencoded = true; // ECI detection only, atm continue decoding as ASCII isECIencoded = true; // ECI detection only, atm continue decoding as ASCII
break; break;
default: default:
@ -158,7 +161,7 @@ final class DecodedBitStreamParser {
* See ISO 16022:2006, 5.2.3 and Annex C, Table C.2 * See ISO 16022:2006, 5.2.3 and Annex C, Table C.2
*/ */
private static Mode decodeAsciiSegment(BitSource bits, private static Mode decodeAsciiSegment(BitSource bits,
StringBuilder result, ECIStringBuilder result,
StringBuilder resultTrailer, StringBuilder resultTrailer,
Set<Integer> fnc1positions) throws FormatException { Set<Integer> fnc1positions) throws FormatException {
boolean upperShift = false; boolean upperShift = false;
@ -231,7 +234,7 @@ final class DecodedBitStreamParser {
/** /**
* See ISO 16022:2006, 5.2.5 and Annex C, Table C.1 * See ISO 16022:2006, 5.2.5 and Annex C, Table C.1
*/ */
private static void decodeC40Segment(BitSource bits, StringBuilder result, Set<Integer> fnc1positions) private static void decodeC40Segment(BitSource bits, ECIStringBuilder result, Set<Integer> fnc1positions)
throws FormatException { throws FormatException {
// Three C40 values are encoded in a 16-bit value as // Three C40 values are encoded in a 16-bit value as
// (1600 * C1) + (40 * C2) + C3 + 1 // (1600 * C1) + (40 * C2) + C3 + 1
@ -323,7 +326,7 @@ final class DecodedBitStreamParser {
/** /**
* See ISO 16022:2006, 5.2.6 and Annex C, Table C.2 * See ISO 16022:2006, 5.2.6 and Annex C, Table C.2
*/ */
private static void decodeTextSegment(BitSource bits, StringBuilder result, Set<Integer> fnc1positions) private static void decodeTextSegment(BitSource bits, ECIStringBuilder result, Set<Integer> fnc1positions)
throws FormatException { throws FormatException {
// Three Text values are encoded in a 16-bit value as // Three Text values are encoded in a 16-bit value as
// (1600 * C1) + (40 * C2) + C3 + 1 // (1600 * C1) + (40 * C2) + C3 + 1
@ -421,7 +424,7 @@ final class DecodedBitStreamParser {
* See ISO 16022:2006, 5.2.7 * See ISO 16022:2006, 5.2.7
*/ */
private static void decodeAnsiX12Segment(BitSource bits, private static void decodeAnsiX12Segment(BitSource bits,
StringBuilder result) throws FormatException { ECIStringBuilder result) throws FormatException {
// Three ANSI X12 values are encoded in a 16-bit value as // Three ANSI X12 values are encoded in a 16-bit value as
// (1600 * C1) + (40 * C2) + C3 + 1 // (1600 * C1) + (40 * C2) + C3 + 1
@ -480,7 +483,7 @@ final class DecodedBitStreamParser {
/** /**
* See ISO 16022:2006, 5.2.8 and Annex C Table C.3 * See ISO 16022:2006, 5.2.8 and Annex C Table C.3
*/ */
private static void decodeEdifactSegment(BitSource bits, StringBuilder result) { private static void decodeEdifactSegment(BitSource bits, ECIStringBuilder result) {
do { do {
// If there is only two or less bytes left then it will be encoded as ASCII // If there is only two or less bytes left then it will be encoded as ASCII
if (bits.available() <= 16) { if (bits.available() <= 16) {
@ -512,7 +515,7 @@ final class DecodedBitStreamParser {
* See ISO 16022:2006, 5.2.9 and Annex B, B.2 * See ISO 16022:2006, 5.2.9 and Annex B, B.2
*/ */
private static void decodeBase256Segment(BitSource bits, private static void decodeBase256Segment(BitSource bits,
StringBuilder result, ECIStringBuilder result,
Collection<byte[]> byteSegments) Collection<byte[]> byteSegments)
throws FormatException { throws FormatException {
// Figure out how long the Base 256 Segment is. // Figure out how long the Base 256 Segment is.
@ -545,6 +548,36 @@ final class DecodedBitStreamParser {
result.append(new String(bytes, StandardCharsets.ISO_8859_1)); result.append(new String(bytes, StandardCharsets.ISO_8859_1));
} }
/**
* See ISO 16022:2007, 5.4.1
*/
private static void decodeECISegment(BitSource bits,
ECIStringBuilder result)
throws FormatException {
if (bits.available() < 8) {
throw FormatException.getFormatInstance();
}
int c1 = bits.readBits(8);
if (c1 <= 127) {
result.appendECI(c1 - 1);
}
//currently we only support character set ECIs
/*} else {
if (bits.available() < 8) {
throw FormatException.getFormatInstance();
}
int c2 = bits.readBits(8);
if (c1 >= 128 && c1 <= 191) {
} else {
if (bits.available() < 8) {
throw FormatException.getFormatInstance();
}
int c3 = bits.readBits(8);
}
}*/
}
/** /**
* See ISO 16022:2006, Annex B, B.2 * See ISO 16022:2006, Annex B, B.2
*/ */
@ -555,4 +588,70 @@ final class DecodedBitStreamParser {
return tempVariable >= 0 ? tempVariable : tempVariable + 256; 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;
}
}
} }