mirror of
https://github.com/zxing/zxing.git
synced 2025-03-05 20:48:51 -08:00
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:
parent
ce1a1a53cf
commit
8265242784
124
core/src/main/java/com/google/zxing/common/ECIStringBuilder.java
Normal file
124
core/src/main/java/com/google/zxing/common/ECIStringBuilder.java
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,10 +19,9 @@ 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 com.google.zxing.common.ECIStringBuilder;
|
||||||
|
|
||||||
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;
|
||||||
|
@ -128,7 +127,7 @@ final class DecodedBitStreamParser {
|
||||||
}
|
}
|
||||||
} while (mode != Mode.PAD_ENCODE && bits.available() > 0);
|
} while (mode != Mode.PAD_ENCODE && bits.available() > 0);
|
||||||
if (resultTrailer.length() > 0) {
|
if (resultTrailer.length() > 0) {
|
||||||
result.append(resultTrailer);
|
result.appendCharacters(resultTrailer);
|
||||||
}
|
}
|
||||||
if (isECIencoded) {
|
if (isECIencoded) {
|
||||||
// Examples for this numbers can be found in this documentation of a hardware barcode scanner:
|
// 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;
|
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,14 +17,12 @@
|
||||||
package com.google.zxing.pdf417.decoder;
|
package com.google.zxing.pdf417.decoder;
|
||||||
|
|
||||||
import com.google.zxing.FormatException;
|
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.common.DecoderResult;
|
||||||
import com.google.zxing.pdf417.PDF417ResultMetadata;
|
import com.google.zxing.pdf417.PDF417ResultMetadata;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.io.UnsupportedEncodingException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>This class contains the methods for decoding the PDF417 codewords.</p>
|
* <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 {
|
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);
|
int codeIndex = textCompaction(codewords, 1, result);
|
||||||
PDF417ResultMetadata resultMetadata = new PDF417ResultMetadata();
|
PDF417ResultMetadata resultMetadata = new PDF417ResultMetadata();
|
||||||
while (codeIndex < codewords[0]) {
|
while (codeIndex < codewords[0]) {
|
||||||
|
@ -205,37 +203,37 @@ final class DecodedBitStreamParser {
|
||||||
codeIndex++;
|
codeIndex++;
|
||||||
switch (codewords[codeIndex]) {
|
switch (codewords[codeIndex]) {
|
||||||
case MACRO_PDF417_OPTIONAL_FIELD_FILE_NAME:
|
case MACRO_PDF417_OPTIONAL_FIELD_FILE_NAME:
|
||||||
ECIOutput fileName = new ECIOutput();
|
ECIStringBuilder fileName = new ECIStringBuilder();
|
||||||
codeIndex = textCompaction(codewords, codeIndex + 1, fileName);
|
codeIndex = textCompaction(codewords, codeIndex + 1, fileName);
|
||||||
resultMetadata.setFileName(fileName.toString());
|
resultMetadata.setFileName(fileName.toString());
|
||||||
break;
|
break;
|
||||||
case MACRO_PDF417_OPTIONAL_FIELD_SENDER:
|
case MACRO_PDF417_OPTIONAL_FIELD_SENDER:
|
||||||
ECIOutput sender = new ECIOutput();
|
ECIStringBuilder sender = new ECIStringBuilder();
|
||||||
codeIndex = textCompaction(codewords, codeIndex + 1, sender);
|
codeIndex = textCompaction(codewords, codeIndex + 1, sender);
|
||||||
resultMetadata.setSender(sender.toString());
|
resultMetadata.setSender(sender.toString());
|
||||||
break;
|
break;
|
||||||
case MACRO_PDF417_OPTIONAL_FIELD_ADDRESSEE:
|
case MACRO_PDF417_OPTIONAL_FIELD_ADDRESSEE:
|
||||||
ECIOutput addressee = new ECIOutput();
|
ECIStringBuilder addressee = new ECIStringBuilder();
|
||||||
codeIndex = textCompaction(codewords, codeIndex + 1, addressee);
|
codeIndex = textCompaction(codewords, codeIndex + 1, addressee);
|
||||||
resultMetadata.setAddressee(addressee.toString());
|
resultMetadata.setAddressee(addressee.toString());
|
||||||
break;
|
break;
|
||||||
case MACRO_PDF417_OPTIONAL_FIELD_SEGMENT_COUNT:
|
case MACRO_PDF417_OPTIONAL_FIELD_SEGMENT_COUNT:
|
||||||
ECIOutput segmentCount = new ECIOutput();
|
ECIStringBuilder segmentCount = new ECIStringBuilder();
|
||||||
codeIndex = numericCompaction(codewords, codeIndex + 1, segmentCount);
|
codeIndex = numericCompaction(codewords, codeIndex + 1, segmentCount);
|
||||||
resultMetadata.setSegmentCount(Integer.parseInt(segmentCount.toString()));
|
resultMetadata.setSegmentCount(Integer.parseInt(segmentCount.toString()));
|
||||||
break;
|
break;
|
||||||
case MACRO_PDF417_OPTIONAL_FIELD_TIME_STAMP:
|
case MACRO_PDF417_OPTIONAL_FIELD_TIME_STAMP:
|
||||||
ECIOutput timestamp = new ECIOutput();
|
ECIStringBuilder timestamp = new ECIStringBuilder();
|
||||||
codeIndex = numericCompaction(codewords, codeIndex + 1, timestamp);
|
codeIndex = numericCompaction(codewords, codeIndex + 1, timestamp);
|
||||||
resultMetadata.setTimestamp(Long.parseLong(timestamp.toString()));
|
resultMetadata.setTimestamp(Long.parseLong(timestamp.toString()));
|
||||||
break;
|
break;
|
||||||
case MACRO_PDF417_OPTIONAL_FIELD_CHECKSUM:
|
case MACRO_PDF417_OPTIONAL_FIELD_CHECKSUM:
|
||||||
ECIOutput checksum = new ECIOutput();
|
ECIStringBuilder checksum = new ECIStringBuilder();
|
||||||
codeIndex = numericCompaction(codewords, codeIndex + 1, checksum);
|
codeIndex = numericCompaction(codewords, codeIndex + 1, checksum);
|
||||||
resultMetadata.setChecksum(Integer.parseInt(checksum.toString()));
|
resultMetadata.setChecksum(Integer.parseInt(checksum.toString()));
|
||||||
break;
|
break;
|
||||||
case MACRO_PDF417_OPTIONAL_FIELD_FILE_SIZE:
|
case MACRO_PDF417_OPTIONAL_FIELD_FILE_SIZE:
|
||||||
ECIOutput fileSize = new ECIOutput();
|
ECIStringBuilder fileSize = new ECIStringBuilder();
|
||||||
codeIndex = numericCompaction(codewords, codeIndex + 1, fileSize);
|
codeIndex = numericCompaction(codewords, codeIndex + 1, fileSize);
|
||||||
resultMetadata.setFileSize(Long.parseLong(fileSize.toString()));
|
resultMetadata.setFileSize(Long.parseLong(fileSize.toString()));
|
||||||
break;
|
break;
|
||||||
|
@ -276,7 +274,7 @@ final class DecodedBitStreamParser {
|
||||||
* @param result The decoded data is appended to the result.
|
* @param result The decoded data is appended to the result.
|
||||||
* @return The next index into the codeword array.
|
* @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
|
// 2 character per codeword
|
||||||
int[] textCompactionData = new int[(codewords[0] - codeIndex) * 2];
|
int[] textCompactionData = new int[(codewords[0] - codeIndex) * 2];
|
||||||
// Used to hold the byte compaction value if there is a mode shift
|
// 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,
|
private static Mode decodeTextCompaction(int[] textCompactionData,
|
||||||
int[] byteCompactionData,
|
int[] byteCompactionData,
|
||||||
int length,
|
int length,
|
||||||
ECIOutput result,
|
ECIStringBuilder result,
|
||||||
Mode startMode) {
|
Mode startMode) {
|
||||||
// Beginning from an initial state
|
// Beginning from an initial state
|
||||||
// The default compaction mode for PDF417 in effect at the start of each symbol shall always be Text
|
// 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,
|
private static int byteCompaction(int mode,
|
||||||
int[] codewords,
|
int[] codewords,
|
||||||
int codeIndex,
|
int codeIndex,
|
||||||
ECIOutput result) throws FormatException {
|
ECIStringBuilder result) throws FormatException {
|
||||||
boolean end = false;
|
boolean end = false;
|
||||||
|
|
||||||
while (codeIndex < codewords[0] && !end) {
|
while (codeIndex < codewords[0] && !end) {
|
||||||
|
@ -602,7 +600,7 @@ final class DecodedBitStreamParser {
|
||||||
* @param result The decoded data is appended to the result.
|
* @param result The decoded data is appended to the result.
|
||||||
* @return The next index into the codeword array.
|
* @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;
|
int count = 0;
|
||||||
boolean end = false;
|
boolean end = false;
|
||||||
|
|
||||||
|
@ -697,67 +695,4 @@ final class DecodedBitStreamParser {
|
||||||
return resultString.substring(1);
|
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue