mirror of
https://github.com/zxing/zxing.git
synced 2025-03-05 20:48:51 -08:00
Improve support for Macro PDF417 (#973)
* Improve support for Macro PDF417 * cleanup and move license to it's proper place * add .editorconfig + proper indentation / add optionalFields array * rename variables and use Arrays instead of System * replaced length with to * restore spaces & switch to package private
This commit is contained in:
parent
a3bbebccba
commit
dbfd5520e9
9
.editorconfig
Normal file
9
.editorconfig
Normal file
|
@ -0,0 +1,9 @@
|
|||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
|
@ -23,9 +23,21 @@ public final class PDF417ResultMetadata {
|
|||
|
||||
private int segmentIndex;
|
||||
private String fileId;
|
||||
private int[] optionalData;
|
||||
private boolean lastSegment;
|
||||
private int segmentCount = -1;
|
||||
private String sender;
|
||||
private String addressee;
|
||||
private String fileName;
|
||||
private long fileSize = -1;
|
||||
private long timestamp = -1;
|
||||
private int checksum = -1;
|
||||
private int[] optionalData;
|
||||
|
||||
/**
|
||||
* The Segment ID represents the segment of the whole file distributed over different symbols.
|
||||
*
|
||||
* @return File segment index
|
||||
*/
|
||||
public int getSegmentIndex() {
|
||||
return segmentIndex;
|
||||
}
|
||||
|
@ -34,6 +46,11 @@ public final class PDF417ResultMetadata {
|
|||
this.segmentIndex = segmentIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the same for each related PDF417 symbol
|
||||
*
|
||||
* @return File ID
|
||||
*/
|
||||
public String getFileId() {
|
||||
return fileId;
|
||||
}
|
||||
|
@ -42,14 +59,27 @@ public final class PDF417ResultMetadata {
|
|||
this.fileId = fileId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return always null
|
||||
* @deprecated use dedicated already parsed fields
|
||||
*/
|
||||
@Deprecated
|
||||
public int[] getOptionalData() {
|
||||
return optionalData;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated parse and use new fields
|
||||
*/
|
||||
@Deprecated
|
||||
public void setOptionalData(int[] optionalData) {
|
||||
this.optionalData = optionalData;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return true if it is the last segment
|
||||
*/
|
||||
public boolean isLastSegment() {
|
||||
return lastSegment;
|
||||
}
|
||||
|
@ -58,4 +88,83 @@ public final class PDF417ResultMetadata {
|
|||
this.lastSegment = lastSegment;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return count of segments, -1 if not set
|
||||
*/
|
||||
public int getSegmentCount() {
|
||||
return segmentCount;
|
||||
}
|
||||
|
||||
public void setSegmentCount(int segmentCount) {
|
||||
this.segmentCount = segmentCount;
|
||||
}
|
||||
|
||||
public String getSender() {
|
||||
return sender;
|
||||
}
|
||||
|
||||
public void setSender(String sender) {
|
||||
this.sender = sender;
|
||||
}
|
||||
|
||||
public String getAddressee() {
|
||||
return addressee;
|
||||
}
|
||||
|
||||
public void setAddressee(String addressee) {
|
||||
this.addressee = addressee;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filename of the encoded file
|
||||
*
|
||||
* @return filename
|
||||
*/
|
||||
public String getFileName() {
|
||||
return fileName;
|
||||
}
|
||||
|
||||
public void setFileName(String fileName) {
|
||||
this.fileName = fileName;
|
||||
}
|
||||
|
||||
/**
|
||||
* filesize in bytes of the encoded file
|
||||
*
|
||||
* @return filesize in bytes, -1 if not set
|
||||
*/
|
||||
public long getFileSize() {
|
||||
return fileSize;
|
||||
}
|
||||
|
||||
public void setFileSize(long fileSize) {
|
||||
this.fileSize = fileSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* 16-bit CRC checksum using CCITT-16
|
||||
*
|
||||
* @return crc checksum, -1 if not set
|
||||
*/
|
||||
public int getChecksum() {
|
||||
return checksum;
|
||||
}
|
||||
|
||||
public void setChecksum(int checksum) {
|
||||
this.checksum = checksum;
|
||||
}
|
||||
|
||||
/**
|
||||
* unix epock timestamp, elapsed seconds since 1970-01-01
|
||||
*
|
||||
* @return elapsed seconds, -1 if not set
|
||||
*/
|
||||
public long getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
public void setTimestamp(long timestamp) {
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -57,6 +57,14 @@ final class DecodedBitStreamParser {
|
|||
private static final int MODE_SHIFT_TO_BYTE_COMPACTION_MODE = 913;
|
||||
private static final int MAX_NUMERIC_CODEWORDS = 15;
|
||||
|
||||
private static final int MACRO_PDF417_OPTIONAL_FIELD_FILE_NAME = 0;
|
||||
private static final int MACRO_PDF417_OPTIONAL_FIELD_SEGMENT_COUNT = 1;
|
||||
private static final int MACRO_PDF417_OPTIONAL_FIELD_TIME_STAMP = 2;
|
||||
private static final int MACRO_PDF417_OPTIONAL_FIELD_SENDER = 3;
|
||||
private static final int MACRO_PDF417_OPTIONAL_FIELD_ADDRESSEE = 4;
|
||||
private static final int MACRO_PDF417_OPTIONAL_FIELD_FILE_SIZE = 5;
|
||||
private static final int MACRO_PDF417_OPTIONAL_FIELD_CHECKSUM = 6;
|
||||
|
||||
private static final int PL = 25;
|
||||
private static final int LL = 27;
|
||||
private static final int AS = 27;
|
||||
|
@ -76,6 +84,7 @@ final class DecodedBitStreamParser {
|
|||
* This is used in the numeric compaction decode algorithm.
|
||||
*/
|
||||
private static final BigInteger[] EXP900;
|
||||
|
||||
static {
|
||||
EXP900 = new BigInteger[16];
|
||||
EXP900[0] = BigInteger.ONE;
|
||||
|
@ -124,7 +133,7 @@ final class DecodedBitStreamParser {
|
|||
break;
|
||||
case ECI_USER_DEFINED:
|
||||
// Can't do anything with user ECI; skip its 1 character
|
||||
codeIndex ++;
|
||||
codeIndex++;
|
||||
break;
|
||||
case BEGIN_MACRO_PDF417_CONTROL_BLOCK:
|
||||
codeIndex = decodeMacroBlock(codewords, codeIndex, resultMetadata);
|
||||
|
@ -155,7 +164,7 @@ final class DecodedBitStreamParser {
|
|||
return decoderResult;
|
||||
}
|
||||
|
||||
private static int decodeMacroBlock(int[] codewords, int codeIndex, PDF417ResultMetadata resultMetadata)
|
||||
static int decodeMacroBlock(int[] codewords, int codeIndex, PDF417ResultMetadata resultMetadata)
|
||||
throws FormatException {
|
||||
if (codeIndex + NUMBER_OF_SEQUENCE_CODEWORDS > codewords[0]) {
|
||||
// we must have at least two bytes left for the segment index
|
||||
|
@ -172,35 +181,72 @@ final class DecodedBitStreamParser {
|
|||
codeIndex = textCompaction(codewords, codeIndex, fileId);
|
||||
resultMetadata.setFileId(fileId.toString());
|
||||
|
||||
switch (codewords[codeIndex]) {
|
||||
case BEGIN_MACRO_PDF417_OPTIONAL_FIELD:
|
||||
codeIndex++;
|
||||
int[] additionalOptionCodeWords = new int[codewords[0] - codeIndex];
|
||||
int additionalOptionCodeWordsIndex = 0;
|
||||
int optionalFieldsStart = -1;
|
||||
if (codewords[codeIndex] == BEGIN_MACRO_PDF417_OPTIONAL_FIELD) {
|
||||
optionalFieldsStart = codeIndex + 1;
|
||||
}
|
||||
|
||||
boolean end = false;
|
||||
while ((codeIndex < codewords[0]) && !end) {
|
||||
int code = codewords[codeIndex++];
|
||||
if (code < TEXT_COMPACTION_MODE_LATCH) {
|
||||
additionalOptionCodeWords[additionalOptionCodeWordsIndex++] = code;
|
||||
} else {
|
||||
switch (code) {
|
||||
case MACRO_PDF417_TERMINATOR:
|
||||
resultMetadata.setLastSegment(true);
|
||||
codeIndex++;
|
||||
end = true;
|
||||
break;
|
||||
default:
|
||||
throw FormatException.getFormatInstance();
|
||||
}
|
||||
while (codeIndex < codewords[0]) {
|
||||
switch (codewords[codeIndex]) {
|
||||
case BEGIN_MACRO_PDF417_OPTIONAL_FIELD:
|
||||
codeIndex++;
|
||||
switch (codewords[codeIndex]) {
|
||||
case MACRO_PDF417_OPTIONAL_FIELD_FILE_NAME:
|
||||
StringBuilder fileName = new StringBuilder();
|
||||
codeIndex = textCompaction(codewords, codeIndex + 1, fileName);
|
||||
resultMetadata.setFileName(fileName.toString());
|
||||
break;
|
||||
case MACRO_PDF417_OPTIONAL_FIELD_SENDER:
|
||||
StringBuilder sender = new StringBuilder();
|
||||
codeIndex = textCompaction(codewords, codeIndex + 1, sender);
|
||||
resultMetadata.setSender(sender.toString());
|
||||
break;
|
||||
case MACRO_PDF417_OPTIONAL_FIELD_ADDRESSEE:
|
||||
StringBuilder addressee = new StringBuilder();
|
||||
codeIndex = textCompaction(codewords, codeIndex + 1, addressee);
|
||||
resultMetadata.setAddressee(addressee.toString());
|
||||
break;
|
||||
case MACRO_PDF417_OPTIONAL_FIELD_SEGMENT_COUNT:
|
||||
StringBuilder segmentCount = new StringBuilder();
|
||||
codeIndex = numericCompaction(codewords, codeIndex + 1, segmentCount);
|
||||
resultMetadata.setSegmentCount(Integer.parseInt(segmentCount.toString()));
|
||||
break;
|
||||
case MACRO_PDF417_OPTIONAL_FIELD_TIME_STAMP:
|
||||
StringBuilder timestamp = new StringBuilder();
|
||||
codeIndex = numericCompaction(codewords, codeIndex + 1, timestamp);
|
||||
resultMetadata.setTimestamp(Long.parseLong(timestamp.toString()));
|
||||
break;
|
||||
case MACRO_PDF417_OPTIONAL_FIELD_CHECKSUM:
|
||||
StringBuilder checksum = new StringBuilder();
|
||||
codeIndex = numericCompaction(codewords, codeIndex + 1, checksum);
|
||||
resultMetadata.setChecksum(Integer.parseInt(checksum.toString()));
|
||||
break;
|
||||
case MACRO_PDF417_OPTIONAL_FIELD_FILE_SIZE:
|
||||
StringBuilder fileSize = new StringBuilder();
|
||||
codeIndex = numericCompaction(codewords, codeIndex + 1, fileSize);
|
||||
resultMetadata.setFileSize(Long.parseLong(fileSize.toString()));
|
||||
break;
|
||||
default:
|
||||
throw FormatException.getFormatInstance();
|
||||
}
|
||||
}
|
||||
resultMetadata.setOptionalData(Arrays.copyOf(additionalOptionCodeWords, additionalOptionCodeWordsIndex));
|
||||
break;
|
||||
case MACRO_PDF417_TERMINATOR:
|
||||
resultMetadata.setLastSegment(true);
|
||||
codeIndex++;
|
||||
break;
|
||||
break;
|
||||
case MACRO_PDF417_TERMINATOR:
|
||||
codeIndex++;
|
||||
resultMetadata.setLastSegment(true);
|
||||
break;
|
||||
default:
|
||||
throw FormatException.getFormatInstance();
|
||||
}
|
||||
}
|
||||
|
||||
// copy optional fields to additional options
|
||||
if (optionalFieldsStart != -1) {
|
||||
int optionalFieldsLength = codeIndex - optionalFieldsStart;
|
||||
if (resultMetadata.isLastSegment()) {
|
||||
// do not include terminator
|
||||
optionalFieldsLength--;
|
||||
}
|
||||
resultMetadata.setOptionalData(Arrays.copyOfRange(codewords, optionalFieldsStart, optionalFieldsStart + optionalFieldsLength));
|
||||
}
|
||||
|
||||
return codeIndex;
|
||||
|
|
|
@ -24,8 +24,8 @@ public final class PDF417BlackBox3TestCase extends AbstractBlackBoxTestCase {
|
|||
|
||||
public PDF417BlackBox3TestCase() {
|
||||
super("src/test/resources/blackbox/pdf417-3", new MultiFormatReader(), BarcodeFormat.PDF_417);
|
||||
addTest(18, 18, 0, 0, 0.0f);
|
||||
addTest(18, 18, 0, 0, 180.0f);
|
||||
addTest(19, 19, 0, 0, 0.0f);
|
||||
addTest(19, 19, 0, 0, 180.0f);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -64,7 +64,7 @@ public final class PDF417BlackBox4TestCase extends AbstractBlackBoxTestCase {
|
|||
|
||||
public PDF417BlackBox4TestCase() {
|
||||
super("src/test/resources/blackbox/pdf417-4", null, BarcodeFormat.PDF_417);
|
||||
testResults.add(new TestResult(2, 2, 0, 0, 0.0f));
|
||||
testResults.add(new TestResult(3, 3, 0, 0, 0.0f));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
* Copyright 2009 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.pdf417.decoder;
|
||||
|
||||
import com.google.zxing.FormatException;
|
||||
import com.google.zxing.pdf417.PDF417ResultMetadata;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class PDF471DecoderTestCase {
|
||||
|
||||
/**
|
||||
* Tests the first sample given in ISO/IEC 15438:2015(E) - Annex H.4
|
||||
*/
|
||||
@Test
|
||||
public void testStandardSample1() throws FormatException {
|
||||
PDF417ResultMetadata resultMetadata = new PDF417ResultMetadata();
|
||||
int[] sampleCodes = {20, 928, 111, 100, 17, 53, 923, 1, 111, 104, 923, 3, 64, 416, 34, 923, 4, 258, 446, 67,
|
||||
// we should never reach these
|
||||
1000, 1000, 1000};
|
||||
|
||||
DecodedBitStreamParser.decodeMacroBlock(sampleCodes, 2, resultMetadata);
|
||||
|
||||
assertEquals(0, resultMetadata.getSegmentIndex());
|
||||
assertEquals("ARBX", resultMetadata.getFileId());
|
||||
assertFalse(resultMetadata.isLastSegment());
|
||||
assertEquals(4, resultMetadata.getSegmentCount());
|
||||
assertEquals("CEN BE", resultMetadata.getSender());
|
||||
assertEquals("ISO CH", resultMetadata.getAddressee());
|
||||
|
||||
int[] optionalData = resultMetadata.getOptionalData();
|
||||
assertEquals("first element of optional array should be the first field identifier", 1, optionalData[0]);
|
||||
assertEquals("last element of optional array should be the last codeword of the last field", 67, optionalData[optionalData.length - 1]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Tests the second given in ISO/IEC 15438:2015(E) - Annex H.4
|
||||
*/
|
||||
@Test
|
||||
public void testStandardSample2() throws FormatException {
|
||||
PDF417ResultMetadata resultMetadata = new PDF417ResultMetadata();
|
||||
int[] sampleCodes = {11, 928, 111, 103, 17, 53, 923, 1, 111, 104, 922,
|
||||
// we should never reach these
|
||||
1000, 1000, 1000};
|
||||
|
||||
DecodedBitStreamParser.decodeMacroBlock(sampleCodes, 2, resultMetadata);
|
||||
|
||||
assertEquals(3, resultMetadata.getSegmentIndex());
|
||||
assertEquals("ARBX", resultMetadata.getFileId());
|
||||
assertTrue(resultMetadata.isLastSegment());
|
||||
assertEquals(4, resultMetadata.getSegmentCount());
|
||||
assertNull(resultMetadata.getAddressee());
|
||||
assertNull(resultMetadata.getSender());
|
||||
|
||||
|
||||
int[] optionalData = resultMetadata.getOptionalData();
|
||||
assertEquals("first element of optional array should be the first field identifier", 1, optionalData[0]);
|
||||
assertEquals("last element of optional array should be the last codeword of the last field", 104, optionalData[optionalData.length - 1]);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSampleWithFilename() throws FormatException {
|
||||
int[] sampleCodes = {23, 477, 928, 111, 100, 0, 252, 21, 86, 923, 0, 815, 251, 133, 12, 148, 537, 593, 599, 923, 1, 111, 102, 98, 311, 355, 522, 920, 779, 40, 628, 33, 749, 267, 506, 213, 928, 465, 248, 493, 72, 780, 699, 780, 493, 755, 84, 198, 628, 368, 156, 198, 809, 19, 113};
|
||||
PDF417ResultMetadata resultMetadata = new PDF417ResultMetadata();
|
||||
|
||||
DecodedBitStreamParser.decodeMacroBlock(sampleCodes, 3, resultMetadata);
|
||||
|
||||
assertEquals(0, resultMetadata.getSegmentIndex());
|
||||
assertEquals("AAIMAVC ", resultMetadata.getFileId());
|
||||
assertFalse(resultMetadata.isLastSegment());
|
||||
assertEquals(2, resultMetadata.getSegmentCount());
|
||||
assertNull(resultMetadata.getAddressee());
|
||||
assertNull(resultMetadata.getSender());
|
||||
assertEquals("filename.txt", resultMetadata.getFileName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSampleWithNumericValues() throws FormatException {
|
||||
int[] sampleCodes = {25, 477, 928, 111, 100, 0, 252, 21, 86, 923, 2, 2, 0, 1, 0, 0, 0, 923, 5, 130, 923, 6, 1, 500, 13, 0};
|
||||
PDF417ResultMetadata resultMetadata = new PDF417ResultMetadata();
|
||||
|
||||
DecodedBitStreamParser.decodeMacroBlock(sampleCodes, 3, resultMetadata);
|
||||
|
||||
assertEquals(0, resultMetadata.getSegmentIndex());
|
||||
assertEquals("AAIMAVC ", resultMetadata.getFileId());
|
||||
assertFalse(resultMetadata.isLastSegment());
|
||||
|
||||
assertEquals(180980729000000L, resultMetadata.getTimestamp());
|
||||
assertEquals(30, resultMetadata.getFileSize());
|
||||
assertEquals(260013, resultMetadata.getChecksum());
|
||||
|
||||
}
|
||||
|
||||
}
|
BIN
core/src/test/resources/blackbox/pdf417-3/19.png
Normal file
BIN
core/src/test/resources/blackbox/pdf417-3/19.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 931 B |
1
core/src/test/resources/blackbox/pdf417-3/19.txt
Normal file
1
core/src/test/resources/blackbox/pdf417-3/19.txt
Normal file
|
@ -0,0 +1 @@
|
|||
0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123
|
BIN
core/src/test/resources/blackbox/pdf417-4/04-01.png
Normal file
BIN
core/src/test/resources/blackbox/pdf417-4/04-01.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 82 KiB |
1
core/src/test/resources/blackbox/pdf417-4/04.txt
Normal file
1
core/src/test/resources/blackbox/pdf417-4/04.txt
Normal file
|
@ -0,0 +1 @@
|
|||
01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
|
Loading…
Reference in a new issue