mirror of
https://github.com/zxing/zxing.git
synced 2025-01-12 19:57:27 -08:00
Issue 1821: structured append support in QR code from mike06j
git-svn-id: https://zxing.googlecode.com/svn/trunk@2994 59b500cc-1b3d-0410-9834-0bbf25fbcc57
This commit is contained in:
parent
21d22400b9
commit
9a5391b653
|
@ -82,4 +82,16 @@ public enum ResultMetadataType {
|
|||
*/
|
||||
PDF417_EXTRA_METADATA,
|
||||
|
||||
/**
|
||||
* If the code format supports structured append and the current scanned code is part of one then the
|
||||
* sequence number is given with it.
|
||||
*/
|
||||
STRUCTURED_APPEND_SEQUENCE,
|
||||
|
||||
/**
|
||||
* If the code format supports structured append and the current scanned code is part of one then the
|
||||
* parity is given with it.
|
||||
*/
|
||||
STRUCTURED_APPEND_PARITY,
|
||||
|
||||
}
|
||||
|
|
|
@ -34,15 +34,28 @@ public final class DecoderResult {
|
|||
private Integer errorsCorrected;
|
||||
private Integer erasures;
|
||||
private Object other;
|
||||
private final int structuredAppendParity;
|
||||
private final int structuredAppendSequenceNumber;
|
||||
|
||||
public DecoderResult(byte[] rawBytes,
|
||||
String text,
|
||||
List<byte[]> byteSegments,
|
||||
String ecLevel) {
|
||||
this(rawBytes, text, byteSegments, ecLevel, -1, -1);
|
||||
}
|
||||
|
||||
public DecoderResult(byte[] rawBytes,
|
||||
String text,
|
||||
List<byte[]> byteSegments,
|
||||
String ecLevel,
|
||||
int saSequence,
|
||||
int saParity) {
|
||||
this.rawBytes = rawBytes;
|
||||
this.text = text;
|
||||
this.byteSegments = byteSegments;
|
||||
this.ecLevel = ecLevel;
|
||||
this.structuredAppendParity = saParity;
|
||||
this.structuredAppendSequenceNumber = saSequence;
|
||||
}
|
||||
|
||||
public byte[] getRawBytes() {
|
||||
|
@ -85,4 +98,16 @@ public final class DecoderResult {
|
|||
this.other = other;
|
||||
}
|
||||
|
||||
public boolean hasStructuredAppend() {
|
||||
return structuredAppendParity >= 0 && structuredAppendSequenceNumber >= 0;
|
||||
}
|
||||
|
||||
public int getStructuredAppendParity() {
|
||||
return structuredAppendParity;
|
||||
}
|
||||
|
||||
public int getStructuredAppendSequenceNumber() {
|
||||
return structuredAppendSequenceNumber;
|
||||
}
|
||||
|
||||
}
|
|
@ -32,8 +32,11 @@ import com.google.zxing.qrcode.QRCodeReader;
|
|||
import com.google.zxing.qrcode.decoder.QRCodeDecoderMetaData;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
|
||||
/**
|
||||
* This implementation can detect and decode multiple QR Codes in an image.
|
||||
|
@ -44,6 +47,7 @@ import java.util.Map;
|
|||
public final class QRCodeMultiReader extends QRCodeReader implements MultipleBarcodeReader {
|
||||
|
||||
private static final Result[] EMPTY_RESULT_ARRAY = new Result[0];
|
||||
private static final ResultPoint[] NO_POINTS = new ResultPoint[0];
|
||||
|
||||
@Override
|
||||
public Result[] decodeMultiple(BinaryBitmap image) throws NotFoundException {
|
||||
|
@ -72,6 +76,12 @@ public final class QRCodeMultiReader extends QRCodeReader implements MultipleBar
|
|||
if (ecLevel != null) {
|
||||
result.putMetadata(ResultMetadataType.ERROR_CORRECTION_LEVEL, ecLevel);
|
||||
}
|
||||
if (decoderResult.hasStructuredAppend()) {
|
||||
result.putMetadata(ResultMetadataType.STRUCTURED_APPEND_SEQUENCE,
|
||||
decoderResult.getStructuredAppendSequenceNumber());
|
||||
result.putMetadata(ResultMetadataType.STRUCTURED_APPEND_PARITY,
|
||||
decoderResult.getStructuredAppendParity());
|
||||
}
|
||||
results.add(result);
|
||||
} catch (ReaderException re) {
|
||||
// ignore and continue
|
||||
|
@ -80,8 +90,85 @@ public final class QRCodeMultiReader extends QRCodeReader implements MultipleBar
|
|||
if (results.isEmpty()) {
|
||||
return EMPTY_RESULT_ARRAY;
|
||||
} else {
|
||||
results = processStructuredAppend(results);
|
||||
return results.toArray(new Result[results.size()]);
|
||||
}
|
||||
}
|
||||
|
||||
private static List<Result> processStructuredAppend(List<Result> results) {
|
||||
boolean hasSA = false;
|
||||
|
||||
// first, check, if there is at least on SA result in the list
|
||||
for (Result result : results) {
|
||||
if (result.getResultMetadata().containsKey(ResultMetadataType.STRUCTURED_APPEND_SEQUENCE)) {
|
||||
hasSA = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!hasSA) {
|
||||
return results;
|
||||
}
|
||||
|
||||
// it is, second, split the lists and built a new result list
|
||||
List<Result> newResults = new ArrayList<>();
|
||||
List<Result> saResults = new ArrayList<>();
|
||||
for (Result result : results) {
|
||||
newResults.add(result);
|
||||
if (result.getResultMetadata().containsKey(ResultMetadataType.STRUCTURED_APPEND_SEQUENCE)) {
|
||||
saResults.add(result);
|
||||
}
|
||||
}
|
||||
// sort and concatenate the SA list items
|
||||
Collections.sort(saResults, new SAComparator());
|
||||
StringBuilder concatedText = new StringBuilder();
|
||||
int rawBytesLen = 0;
|
||||
int byteSegmentLength = 0;
|
||||
for (Result saResult : saResults) {
|
||||
concatedText.append(saResult.getText());
|
||||
rawBytesLen += saResult.getRawBytes().length;
|
||||
if (saResult.getResultMetadata().containsKey(ResultMetadataType.BYTE_SEGMENTS)) {
|
||||
for (byte[] segment : (Iterable<byte[]>) saResult.getResultMetadata().get(ResultMetadataType.BYTE_SEGMENTS)) {
|
||||
byteSegmentLength += segment.length;
|
||||
}
|
||||
}
|
||||
}
|
||||
byte[] newRawBytes = new byte[rawBytesLen];
|
||||
byte[] newByteSegment = new byte[byteSegmentLength];
|
||||
int newRawBytesIndex = 0;
|
||||
int byteSegmentIndex = 0;
|
||||
for (Result saResult : saResults) {
|
||||
System.arraycopy(saResult.getRawBytes(), 0, newRawBytes, newRawBytesIndex, saResult.getRawBytes().length);
|
||||
newRawBytesIndex += saResult.getRawBytes().length;
|
||||
if (saResult.getResultMetadata().containsKey(ResultMetadataType.BYTE_SEGMENTS)) {
|
||||
for (byte[] segment : (Iterable<byte[]>) saResult.getResultMetadata().get(ResultMetadataType.BYTE_SEGMENTS)) {
|
||||
System.arraycopy(segment, 0, newByteSegment, byteSegmentIndex, segment.length);
|
||||
byteSegmentIndex += segment.length;
|
||||
}
|
||||
}
|
||||
}
|
||||
Result newResult = new Result(concatedText.toString(), newRawBytes, NO_POINTS, BarcodeFormat.QR_CODE);
|
||||
if (byteSegmentLength > 0) {
|
||||
Collection<byte[]> byteSegmentList = new ArrayList<>();
|
||||
byteSegmentList.add(newByteSegment);
|
||||
newResult.putMetadata(ResultMetadataType.BYTE_SEGMENTS, byteSegmentList);
|
||||
}
|
||||
newResults.add(newResult);
|
||||
return newResults;
|
||||
}
|
||||
|
||||
private static final class SAComparator implements Comparator<Result> {
|
||||
@Override
|
||||
public int compare(Result a, Result b) {
|
||||
int aNumber = (int) (a.getResultMetadata().get(ResultMetadataType.STRUCTURED_APPEND_SEQUENCE));
|
||||
int bNumber = (int) (b.getResultMetadata().get(ResultMetadataType.STRUCTURED_APPEND_SEQUENCE));
|
||||
if (aNumber < bNumber) {
|
||||
return -1;
|
||||
}
|
||||
if (aNumber > bNumber) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -93,6 +93,12 @@ public class QRCodeReader implements Reader {
|
|||
if (ecLevel != null) {
|
||||
result.putMetadata(ResultMetadataType.ERROR_CORRECTION_LEVEL, ecLevel);
|
||||
}
|
||||
if (decoderResult.hasStructuredAppend()) {
|
||||
result.putMetadata(ResultMetadataType.STRUCTURED_APPEND_SEQUENCE,
|
||||
decoderResult.getStructuredAppendSequenceNumber());
|
||||
result.putMetadata(ResultMetadataType.STRUCTURED_APPEND_PARITY,
|
||||
decoderResult.getStructuredAppendParity());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -60,6 +60,9 @@ final class DecodedBitStreamParser {
|
|||
BitSource bits = new BitSource(bytes);
|
||||
StringBuilder result = new StringBuilder(50);
|
||||
List<byte[]> byteSegments = new ArrayList<>(1);
|
||||
int symbolSequence = -1;
|
||||
int parityData = -1;
|
||||
|
||||
try {
|
||||
CharacterSetECI currentCharacterSetECI = null;
|
||||
boolean fc1InEffect = false;
|
||||
|
@ -80,9 +83,10 @@ final class DecodedBitStreamParser {
|
|||
if (bits.available() < 16) {
|
||||
throw FormatException.getFormatInstance();
|
||||
}
|
||||
// not really supported; all we do is ignore it
|
||||
// sequence number and parity is added later to the result metadata
|
||||
// Read next 8 bits (symbol sequence #) and 8 bits (parity data), then continue
|
||||
bits.readBits(16);
|
||||
symbolSequence = bits.readBits(8);
|
||||
parityData = bits.readBits(8);
|
||||
} else if (mode == Mode.ECI) {
|
||||
// Count doesn't apply to ECI
|
||||
int value = parseECIValue(bits);
|
||||
|
@ -126,7 +130,9 @@ final class DecodedBitStreamParser {
|
|||
return new DecoderResult(bytes,
|
||||
result.toString(),
|
||||
byteSegments.isEmpty() ? null : byteSegments,
|
||||
ecLevel == null ? null : ecLevel.toString());
|
||||
ecLevel == null ? null : ecLevel.toString(),
|
||||
symbolSequence,
|
||||
parityData);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in a new issue