DecodedBitStreamParser.java (zxing-zxing-3.4.1) | : | DecodedBitStreamParser.java (zxing-zxing-3.5.0) | ||
---|---|---|---|---|
skipping to change at line 20 | skipping to change at line 20 | |||
* Unless required by applicable law or agreed to in writing, software | * Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | * distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
* See the License for the specific language governing permissions and | * See the License for the specific language governing permissions and | |||
* limitations under the License. | * limitations under the License. | |||
*/ | */ | |||
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.nio.charset.Charset; | ||||
import java.nio.charset.StandardCharsets; | ||||
import java.util.Arrays; | import java.util.Arrays; | |||
/** | /** | |||
* <p>This class contains the methods for decoding the PDF417 codewords.</p> | * <p>This class contains the methods for decoding the PDF417 codewords.</p> | |||
* | * | |||
* @author SITA Lab (kevin.osullivan@sita.aero) | * @author SITA Lab (kevin.osullivan@sita.aero) | |||
* @author Guenther Grau | * @author Guenther Grau | |||
*/ | */ | |||
final class DecodedBitStreamParser { | final class DecodedBitStreamParser { | |||
skipping to change at line 104 | skipping to change at line 101 | |||
EXP900[i] = EXP900[i - 1].multiply(nineHundred); | EXP900[i] = EXP900[i - 1].multiply(nineHundred); | |||
} | } | |||
} | } | |||
private static final int NUMBER_OF_SEQUENCE_CODEWORDS = 2; | private static final int NUMBER_OF_SEQUENCE_CODEWORDS = 2; | |||
private DecodedBitStreamParser() { | private DecodedBitStreamParser() { | |||
} | } | |||
static DecoderResult decode(int[] codewords, String ecLevel) throws FormatExce ption { | static DecoderResult decode(int[] codewords, String ecLevel) throws FormatExce ption { | |||
StringBuilder result = new StringBuilder(codewords.length * 2); | ECIStringBuilder result = new ECIStringBuilder(codewords.length * 2); | |||
Charset encoding = StandardCharsets.ISO_8859_1; | int codeIndex = textCompaction(codewords, 1, result); | |||
// Get compaction mode | ||||
int codeIndex = 1; | ||||
int code = codewords[codeIndex++]; | ||||
PDF417ResultMetadata resultMetadata = new PDF417ResultMetadata(); | PDF417ResultMetadata resultMetadata = new PDF417ResultMetadata(); | |||
while (codeIndex < codewords[0]) { | while (codeIndex < codewords[0]) { | |||
int code = codewords[codeIndex++]; | ||||
switch (code) { | switch (code) { | |||
case TEXT_COMPACTION_MODE_LATCH: | case TEXT_COMPACTION_MODE_LATCH: | |||
codeIndex = textCompaction(codewords, codeIndex, result); | codeIndex = textCompaction(codewords, codeIndex, result); | |||
break; | break; | |||
case BYTE_COMPACTION_MODE_LATCH: | case BYTE_COMPACTION_MODE_LATCH: | |||
case BYTE_COMPACTION_MODE_LATCH_6: | case BYTE_COMPACTION_MODE_LATCH_6: | |||
codeIndex = byteCompaction(code, codewords, encoding, codeIndex, resul t); | codeIndex = byteCompaction(code, codewords, codeIndex, result); | |||
break; | break; | |||
case MODE_SHIFT_TO_BYTE_COMPACTION_MODE: | case MODE_SHIFT_TO_BYTE_COMPACTION_MODE: | |||
result.append((char) codewords[codeIndex++]); | result.append((char) codewords[codeIndex++]); | |||
break; | break; | |||
case NUMERIC_COMPACTION_MODE_LATCH: | case NUMERIC_COMPACTION_MODE_LATCH: | |||
codeIndex = numericCompaction(codewords, codeIndex, result); | codeIndex = numericCompaction(codewords, codeIndex, result); | |||
break; | break; | |||
case ECI_CHARSET: | case ECI_CHARSET: | |||
CharacterSetECI charsetECI = | result.appendECI(codewords[codeIndex++]); | |||
CharacterSetECI.getCharacterSetECIByValue(codewords[codeIndex++]); | ||||
encoding = Charset.forName(charsetECI.name()); | ||||
break; | break; | |||
case ECI_GENERAL_PURPOSE: | case ECI_GENERAL_PURPOSE: | |||
// Can't do anything with generic ECI; skip its 2 characters | // Can't do anything with generic ECI; skip its 2 characters | |||
codeIndex += 2; | codeIndex += 2; | |||
break; | break; | |||
case ECI_USER_DEFINED: | case ECI_USER_DEFINED: | |||
// Can't do anything with user ECI; skip its 1 character | // Can't do anything with user ECI; skip its 1 character | |||
codeIndex++; | codeIndex++; | |||
break; | break; | |||
case BEGIN_MACRO_PDF417_CONTROL_BLOCK: | case BEGIN_MACRO_PDF417_CONTROL_BLOCK: | |||
skipping to change at line 153 | skipping to change at line 146 | |||
// Should not see these outside a macro block | // Should not see these outside a macro block | |||
throw FormatException.getFormatInstance(); | throw FormatException.getFormatInstance(); | |||
default: | default: | |||
// Default to text compaction. During testing numerous barcodes | // Default to text compaction. During testing numerous barcodes | |||
// appeared to be missing the starting mode. In these cases defaulting | // appeared to be missing the starting mode. In these cases defaulting | |||
// to text compaction seems to work. | // to text compaction seems to work. | |||
codeIndex--; | codeIndex--; | |||
codeIndex = textCompaction(codewords, codeIndex, result); | codeIndex = textCompaction(codewords, codeIndex, result); | |||
break; | break; | |||
} | } | |||
if (codeIndex < codewords.length) { | ||||
code = codewords[codeIndex++]; | ||||
} else { | ||||
throw FormatException.getFormatInstance(); | ||||
} | ||||
} | } | |||
if (result.length() == 0) { | if (result.isEmpty() && resultMetadata.getFileId() == null) { | |||
throw FormatException.getFormatInstance(); | throw FormatException.getFormatInstance(); | |||
} | } | |||
DecoderResult decoderResult = new DecoderResult(null, result.toString(), nul l, ecLevel); | DecoderResult decoderResult = new DecoderResult(null, result.toString(), nul l, ecLevel); | |||
decoderResult.setOther(resultMetadata); | decoderResult.setOther(resultMetadata); | |||
return decoderResult; | return decoderResult; | |||
} | } | |||
@SuppressWarnings("deprecation") | @SuppressWarnings("deprecation") | |||
static int decodeMacroBlock(int[] codewords, int codeIndex, PDF417ResultMetada ta resultMetadata) | static int decodeMacroBlock(int[] codewords, int codeIndex, PDF417ResultMetada ta resultMetadata) | |||
throws FormatException { | throws FormatException { | |||
if (codeIndex + NUMBER_OF_SEQUENCE_CODEWORDS > codewords[0]) { | if (codeIndex + NUMBER_OF_SEQUENCE_CODEWORDS > codewords[0]) { | |||
// we must have at least two bytes left for the segment index | // we must have at least two bytes left for the segment index | |||
throw FormatException.getFormatInstance(); | throw FormatException.getFormatInstance(); | |||
} | } | |||
int[] segmentIndexArray = new int[NUMBER_OF_SEQUENCE_CODEWORDS]; | int[] segmentIndexArray = new int[NUMBER_OF_SEQUENCE_CODEWORDS]; | |||
for (int i = 0; i < NUMBER_OF_SEQUENCE_CODEWORDS; i++, codeIndex++) { | for (int i = 0; i < NUMBER_OF_SEQUENCE_CODEWORDS; i++, codeIndex++) { | |||
segmentIndexArray[i] = codewords[codeIndex]; | segmentIndexArray[i] = codewords[codeIndex]; | |||
} | } | |||
resultMetadata.setSegmentIndex(Integer.parseInt(decodeBase900toBase10(segmen | String segmentIndexString = decodeBase900toBase10(segmentIndexArray, NUMBER_ | |||
tIndexArray, | OF_SEQUENCE_CODEWORDS); | |||
NUMBER_OF_SEQUENCE_CODEWORDS))); | if (segmentIndexString.isEmpty()) { | |||
resultMetadata.setSegmentIndex(0); | ||||
} else { | ||||
try { | ||||
resultMetadata.setSegmentIndex(Integer.parseInt(segmentIndexString)); | ||||
} catch (NumberFormatException nfe) { | ||||
// too large; bad input? | ||||
throw FormatException.getFormatInstance(); | ||||
} | ||||
} | ||||
// Decoding the fileId codewords as 0-899 numbers, each 0-filled to width 3. | ||||
This follows the spec | ||||
// (See ISO/IEC 15438:2015 Annex H.6) and preserves all info, but some gener | ||||
ators (e.g. TEC-IT) write | ||||
// the fileId using text compaction, so in those cases the fileId will appea | ||||
r mangled. | ||||
StringBuilder fileId = new StringBuilder(); | StringBuilder fileId = new StringBuilder(); | |||
codeIndex = textCompaction(codewords, codeIndex, fileId); | while (codeIndex < codewords[0] && | |||
codeIndex < codewords.length && | ||||
codewords[codeIndex] != MACRO_PDF417_TERMINATOR && | ||||
codewords[codeIndex] != BEGIN_MACRO_PDF417_OPTIONAL_FIELD) { | ||||
fileId.append(String.format("%03d", codewords[codeIndex])); | ||||
codeIndex++; | ||||
} | ||||
if (fileId.length() == 0) { | ||||
// at least one fileId codeword is required (Annex H.2) | ||||
throw FormatException.getFormatInstance(); | ||||
} | ||||
resultMetadata.setFileId(fileId.toString()); | resultMetadata.setFileId(fileId.toString()); | |||
int optionalFieldsStart = -1; | int optionalFieldsStart = -1; | |||
if (codewords[codeIndex] == BEGIN_MACRO_PDF417_OPTIONAL_FIELD) { | if (codewords[codeIndex] == BEGIN_MACRO_PDF417_OPTIONAL_FIELD) { | |||
optionalFieldsStart = codeIndex + 1; | optionalFieldsStart = codeIndex + 1; | |||
} | } | |||
while (codeIndex < codewords[0]) { | while (codeIndex < codewords[0]) { | |||
switch (codewords[codeIndex]) { | switch (codewords[codeIndex]) { | |||
case BEGIN_MACRO_PDF417_OPTIONAL_FIELD: | case BEGIN_MACRO_PDF417_OPTIONAL_FIELD: | |||
codeIndex++; | codeIndex++; | |||
switch (codewords[codeIndex]) { | switch (codewords[codeIndex]) { | |||
case MACRO_PDF417_OPTIONAL_FIELD_FILE_NAME: | case MACRO_PDF417_OPTIONAL_FIELD_FILE_NAME: | |||
StringBuilder fileName = new StringBuilder(); | 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: | |||
StringBuilder sender = new StringBuilder(); | 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: | |||
StringBuilder addressee = new StringBuilder(); | 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: | |||
StringBuilder segmentCount = new StringBuilder(); | ECIStringBuilder segmentCount = new ECIStringBuilder(); | |||
codeIndex = numericCompaction(codewords, codeIndex + 1, segmentCou nt); | codeIndex = numericCompaction(codewords, codeIndex + 1, segmentCou nt); | |||
resultMetadata.setSegmentCount(Integer.parseInt(segmentCount.toStr ing())); | resultMetadata.setSegmentCount(Integer.parseInt(segmentCount.toStr ing())); | |||
break; | break; | |||
case MACRO_PDF417_OPTIONAL_FIELD_TIME_STAMP: | case MACRO_PDF417_OPTIONAL_FIELD_TIME_STAMP: | |||
StringBuilder timestamp = new StringBuilder(); | 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: | |||
StringBuilder checksum = new StringBuilder(); | 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: | |||
StringBuilder fileSize = new StringBuilder(); | 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; | |||
default: | default: | |||
throw FormatException.getFormatInstance(); | throw FormatException.getFormatInstance(); | |||
} | } | |||
break; | break; | |||
case MACRO_PDF417_TERMINATOR: | case MACRO_PDF417_TERMINATOR: | |||
codeIndex++; | codeIndex++; | |||
resultMetadata.setLastSegment(true); | resultMetadata.setLastSegment(true); | |||
skipping to change at line 250 | skipping to change at line 260 | |||
} | } | |||
} | } | |||
// copy optional fields to additional options | // copy optional fields to additional options | |||
if (optionalFieldsStart != -1) { | if (optionalFieldsStart != -1) { | |||
int optionalFieldsLength = codeIndex - optionalFieldsStart; | int optionalFieldsLength = codeIndex - optionalFieldsStart; | |||
if (resultMetadata.isLastSegment()) { | if (resultMetadata.isLastSegment()) { | |||
// do not include terminator | // do not include terminator | |||
optionalFieldsLength--; | optionalFieldsLength--; | |||
} | } | |||
resultMetadata.setOptionalData(Arrays.copyOfRange(codewords, optionalField | resultMetadata.setOptionalData( | |||
sStart, optionalFieldsStart + optionalFieldsLength)); | Arrays.copyOfRange(codewords, optionalFieldsStart, optionalFieldsStart | |||
+ optionalFieldsLength)); | ||||
} | } | |||
return codeIndex; | return codeIndex; | |||
} | } | |||
/** | /** | |||
* Text Compaction mode (see 5.4.1.5) permits all printable ASCII characters t o be | * Text Compaction mode (see 5.4.1.5) permits all printable ASCII characters t o be | |||
* encoded, i.e. values 32 - 126 inclusive in accordance with ISO/IEC 646 (IRV ), as | * encoded, i.e. values 32 - 126 inclusive in accordance with ISO/IEC 646 (IRV ), as | |||
* well as selected control characters. | * well as selected control characters. | |||
* | * | |||
* @param codewords The array of codewords (data + error) | * @param codewords The array of codewords (data + error) | |||
* @param codeIndex The current index into the codeword array. | * @param codeIndex The current index into the codeword array. | |||
* @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, StringBuilde r result) { | private static int textCompaction(int[] codewords, int codeIndex, ECIStringBui lder 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 | |||
int[] byteCompactionData = new int[(codewords[0] - codeIndex) * 2]; | int[] byteCompactionData = new int[(codewords[0] - codeIndex) * 2]; | |||
int index = 0; | int index = 0; | |||
boolean end = false; | boolean end = false; | |||
Mode subMode = Mode.ALPHA; | ||||
while ((codeIndex < codewords[0]) && !end) { | while ((codeIndex < codewords[0]) && !end) { | |||
int code = codewords[codeIndex++]; | int code = codewords[codeIndex++]; | |||
if (code < TEXT_COMPACTION_MODE_LATCH) { | if (code < TEXT_COMPACTION_MODE_LATCH) { | |||
textCompactionData[index] = code / 30; | textCompactionData[index] = code / 30; | |||
textCompactionData[index + 1] = code % 30; | textCompactionData[index + 1] = code % 30; | |||
index += 2; | index += 2; | |||
} else { | } else { | |||
switch (code) { | switch (code) { | |||
case TEXT_COMPACTION_MODE_LATCH: | case TEXT_COMPACTION_MODE_LATCH: | |||
// reinitialize text compaction mode to alpha sub mode | // reinitialize text compaction mode to alpha sub mode | |||
skipping to change at line 307 | skipping to change at line 319 | |||
// switch from Text Compaction mode to Byte Compaction mode. | // switch from Text Compaction mode to Byte Compaction mode. | |||
// This switch shall be in effect for only the next codeword, | // This switch shall be in effect for only the next codeword, | |||
// after which the mode shall revert to the prevailing sub-mode | // after which the mode shall revert to the prevailing sub-mode | |||
// of the Text Compaction mode. Codeword 913 is only available | // of the Text Compaction mode. Codeword 913 is only available | |||
// in Text Compaction mode; its use is described in 5.4.2.4. | // in Text Compaction mode; its use is described in 5.4.2.4. | |||
textCompactionData[index] = MODE_SHIFT_TO_BYTE_COMPACTION_MODE; | textCompactionData[index] = MODE_SHIFT_TO_BYTE_COMPACTION_MODE; | |||
code = codewords[codeIndex++]; | code = codewords[codeIndex++]; | |||
byteCompactionData[index] = code; | byteCompactionData[index] = code; | |||
index++; | index++; | |||
break; | break; | |||
case ECI_CHARSET: | ||||
subMode = decodeTextCompaction(textCompactionData, byteCompactionDat | ||||
a, index, result, subMode); | ||||
result.appendECI(codewords[codeIndex++]); | ||||
textCompactionData = new int[(codewords[0] - codeIndex) * 2]; | ||||
byteCompactionData = new int[(codewords[0] - codeIndex) * 2]; | ||||
index = 0; | ||||
break; | ||||
} | } | |||
} | } | |||
} | } | |||
decodeTextCompaction(textCompactionData, byteCompactionData, index, result); | decodeTextCompaction(textCompactionData, byteCompactionData, index, result, subMode); | |||
return codeIndex; | return codeIndex; | |||
} | } | |||
/** | /** | |||
* The Text Compaction mode includes all the printable ASCII characters | * The Text Compaction mode includes all the printable ASCII characters | |||
* (i.e. values from 32 to 126) and three ASCII control characters: HT or tab | * (i.e. values from 32 to 126) and three ASCII control characters: HT or tab | |||
* (ASCII value 9), LF or line feed (ASCII value 10), and CR or carriage | * (ASCII value 9), LF or line feed (ASCII value 10), and CR or carriage | |||
* return (ASCII value 13). The Text Compaction mode also includes various lat ch | * return (ASCII value 13). The Text Compaction mode also includes various lat ch | |||
* and shift characters which are used exclusively within the mode. The Text | * and shift characters which are used exclusively within the mode. The Text | |||
* Compaction mode encodes up to 2 characters per codeword. The compaction rul es | * Compaction mode encodes up to 2 characters per codeword. The compaction rul es | |||
* for converting data into PDF417 codewords are defined in 5.4.2.2. The sub-m ode | * for converting data into PDF417 codewords are defined in 5.4.2.2. The sub-m ode | |||
* switches are defined in 5.4.2.3. | * switches are defined in 5.4.2.3. | |||
* | * | |||
* @param textCompactionData The text compaction data. | * @param textCompactionData The text compaction data. | |||
* @param byteCompactionData The byte compaction data if there | * @param byteCompactionData The byte compaction data if there | |||
* was a mode shift. | * was a mode shift. | |||
* @param length The size of the text compaction and byte compacti on data. | * @param length The size of the text compaction and byte compacti on data. | |||
* @param result The decoded data is appended to the result. | * @param result The decoded data is appended to the result. | |||
* @param startMode The mode in which decoding starts | ||||
* @return The mode in which decoding ended | ||||
*/ | */ | |||
private static void decodeTextCompaction(int[] textCompactionData, | private static Mode decodeTextCompaction(int[] textCompactionData, | |||
int[] byteCompactionData, | int[] byteCompactionData, | |||
int length, | int length, | |||
StringBuilder result) { | ECIStringBuilder result, | |||
// Beginning from an initial state of the Alpha sub-mode | Mode startMode) { | |||
// Beginning from an initial state | ||||
// The default compaction mode for PDF417 in effect at the start of each sym bol shall always be Text | // The default compaction mode for PDF417 in effect at the start of each sym bol shall always be Text | |||
// Compaction mode Alpha sub-mode (uppercase alphabetic). A latch codeword f rom another mode to the Text | // Compaction mode Alpha sub-mode (uppercase alphabetic). A latch codeword f rom another mode to the Text | |||
// Compaction mode shall always switch to the Text Compaction Alpha sub-mode . | // Compaction mode shall always switch to the Text Compaction Alpha sub-mode . | |||
Mode subMode = Mode.ALPHA; | Mode subMode = startMode; | |||
Mode priorToShiftMode = Mode.ALPHA; | Mode priorToShiftMode = startMode; | |||
Mode latchedMode = startMode; | ||||
int i = 0; | int i = 0; | |||
while (i < length) { | while (i < length) { | |||
int subModeCh = textCompactionData[i]; | int subModeCh = textCompactionData[i]; | |||
char ch = 0; | char ch = 0; | |||
switch (subMode) { | switch (subMode) { | |||
case ALPHA: | case ALPHA: | |||
// Alpha (uppercase alphabetic) | // Alpha (uppercase alphabetic) | |||
if (subModeCh < 26) { | if (subModeCh < 26) { | |||
// Upper case Alpha Character | // Upper case Alpha Character | |||
ch = (char) ('A' + subModeCh); | ch = (char) ('A' + subModeCh); | |||
} else { | } else { | |||
switch (subModeCh) { | switch (subModeCh) { | |||
case 26: | case 26: | |||
ch = ' '; | ch = ' '; | |||
break; | break; | |||
case LL: | case LL: | |||
subMode = Mode.LOWER; | subMode = Mode.LOWER; | |||
latchedMode = subMode; | ||||
break; | break; | |||
case ML: | case ML: | |||
subMode = Mode.MIXED; | subMode = Mode.MIXED; | |||
latchedMode = subMode; | ||||
break; | break; | |||
case PS: | case PS: | |||
// Shift to punctuation | // Shift to punctuation | |||
priorToShiftMode = subMode; | priorToShiftMode = subMode; | |||
subMode = Mode.PUNCT_SHIFT; | subMode = Mode.PUNCT_SHIFT; | |||
break; | break; | |||
case MODE_SHIFT_TO_BYTE_COMPACTION_MODE: | case MODE_SHIFT_TO_BYTE_COMPACTION_MODE: | |||
result.append((char) byteCompactionData[i]); | result.append((char) byteCompactionData[i]); | |||
break; | break; | |||
case TEXT_COMPACTION_MODE_LATCH: | case TEXT_COMPACTION_MODE_LATCH: | |||
subMode = Mode.ALPHA; | subMode = Mode.ALPHA; | |||
latchedMode = subMode; | ||||
break; | break; | |||
} | } | |||
} | } | |||
break; | break; | |||
case LOWER: | case LOWER: | |||
// Lower (lowercase alphabetic) | // Lower (lowercase alphabetic) | |||
if (subModeCh < 26) { | if (subModeCh < 26) { | |||
ch = (char) ('a' + subModeCh); | ch = (char) ('a' + subModeCh); | |||
} else { | } else { | |||
skipping to change at line 392 | skipping to change at line 418 | |||
case 26: | case 26: | |||
ch = ' '; | ch = ' '; | |||
break; | break; | |||
case AS: | case AS: | |||
// Shift to alpha | // Shift to alpha | |||
priorToShiftMode = subMode; | priorToShiftMode = subMode; | |||
subMode = Mode.ALPHA_SHIFT; | subMode = Mode.ALPHA_SHIFT; | |||
break; | break; | |||
case ML: | case ML: | |||
subMode = Mode.MIXED; | subMode = Mode.MIXED; | |||
latchedMode = subMode; | ||||
break; | break; | |||
case PS: | case PS: | |||
// Shift to punctuation | // Shift to punctuation | |||
priorToShiftMode = subMode; | priorToShiftMode = subMode; | |||
subMode = Mode.PUNCT_SHIFT; | subMode = Mode.PUNCT_SHIFT; | |||
break; | break; | |||
case MODE_SHIFT_TO_BYTE_COMPACTION_MODE: | case MODE_SHIFT_TO_BYTE_COMPACTION_MODE: | |||
// TODO Does this need to use the current character encoding? Se e other occurrences below | ||||
result.append((char) byteCompactionData[i]); | result.append((char) byteCompactionData[i]); | |||
break; | break; | |||
case TEXT_COMPACTION_MODE_LATCH: | case TEXT_COMPACTION_MODE_LATCH: | |||
subMode = Mode.ALPHA; | subMode = Mode.ALPHA; | |||
latchedMode = subMode; | ||||
break; | break; | |||
} | } | |||
} | } | |||
break; | break; | |||
case MIXED: | case MIXED: | |||
// Mixed (numeric and some punctuation) | // Mixed (numeric and some punctuation) | |||
if (subModeCh < PL) { | if (subModeCh < PL) { | |||
ch = MIXED_CHARS[subModeCh]; | ch = MIXED_CHARS[subModeCh]; | |||
} else { | } else { | |||
switch (subModeCh) { | switch (subModeCh) { | |||
case PL: | case PL: | |||
subMode = Mode.PUNCT; | subMode = Mode.PUNCT; | |||
latchedMode = subMode; | ||||
break; | break; | |||
case 26: | case 26: | |||
ch = ' '; | ch = ' '; | |||
break; | break; | |||
case LL: | case LL: | |||
subMode = Mode.LOWER; | subMode = Mode.LOWER; | |||
latchedMode = subMode; | ||||
break; | break; | |||
case AL: | case AL: | |||
case TEXT_COMPACTION_MODE_LATCH: | case TEXT_COMPACTION_MODE_LATCH: | |||
subMode = Mode.ALPHA; | subMode = Mode.ALPHA; | |||
latchedMode = subMode; | ||||
break; | break; | |||
case PS: | case PS: | |||
// Shift to punctuation | // Shift to punctuation | |||
priorToShiftMode = subMode; | priorToShiftMode = subMode; | |||
subMode = Mode.PUNCT_SHIFT; | subMode = Mode.PUNCT_SHIFT; | |||
break; | break; | |||
case MODE_SHIFT_TO_BYTE_COMPACTION_MODE: | case MODE_SHIFT_TO_BYTE_COMPACTION_MODE: | |||
result.append((char) byteCompactionData[i]); | result.append((char) byteCompactionData[i]); | |||
break; | break; | |||
} | } | |||
skipping to change at line 449 | skipping to change at line 479 | |||
case PUNCT: | case PUNCT: | |||
// Punctuation | // Punctuation | |||
if (subModeCh < PAL) { | if (subModeCh < PAL) { | |||
ch = PUNCT_CHARS[subModeCh]; | ch = PUNCT_CHARS[subModeCh]; | |||
} else { | } else { | |||
switch (subModeCh) { | switch (subModeCh) { | |||
case PAL: | case PAL: | |||
case TEXT_COMPACTION_MODE_LATCH: | case TEXT_COMPACTION_MODE_LATCH: | |||
subMode = Mode.ALPHA; | subMode = Mode.ALPHA; | |||
latchedMode = subMode; | ||||
break; | break; | |||
case MODE_SHIFT_TO_BYTE_COMPACTION_MODE: | case MODE_SHIFT_TO_BYTE_COMPACTION_MODE: | |||
result.append((char) byteCompactionData[i]); | result.append((char) byteCompactionData[i]); | |||
break; | break; | |||
} | } | |||
} | } | |||
break; | break; | |||
case ALPHA_SHIFT: | case ALPHA_SHIFT: | |||
// Restore sub-mode | // Restore sub-mode | |||
skipping to change at line 500 | skipping to change at line 531 | |||
} | } | |||
} | } | |||
break; | break; | |||
} | } | |||
if (ch != 0) { | if (ch != 0) { | |||
// Append decoded character to result | // Append decoded character to result | |||
result.append(ch); | result.append(ch); | |||
} | } | |||
i++; | i++; | |||
} | } | |||
return latchedMode; | ||||
} | } | |||
/** | /** | |||
* Byte Compaction mode (see 5.4.3) permits all 256 possible 8-bit byte values to be encoded. | * Byte Compaction mode (see 5.4.3) permits all 256 possible 8-bit byte values to be encoded. | |||
* This includes all ASCII characters value 0 to 127 inclusive and provides fo r international | * This includes all ASCII characters value 0 to 127 inclusive and provides fo r international | |||
* character set support. | * character set support. | |||
* | * | |||
* @param mode The byte compaction mode i.e. 901 or 924 | * @param mode The byte compaction mode i.e. 901 or 924 | |||
* @param codewords The array of codewords (data + error) | * @param codewords The array of codewords (data + error) | |||
* @param encoding Currently active character encoding | ||||
* @param codeIndex The current index into the codeword array. | * @param codeIndex The current index into the codeword array. | |||
* @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 byteCompaction(int mode, | private static int byteCompaction(int mode, | |||
int[] codewords, | int[] codewords, | |||
Charset encoding, | ||||
int codeIndex, | int codeIndex, | |||
StringBuilder result) { | ECIStringBuilder result) throws FormatExcept | |||
ByteArrayOutputStream decodedBytes = new ByteArrayOutputStream(); | ion { | |||
int count = 0; | ||||
long value = 0; | ||||
boolean end = false; | boolean end = false; | |||
switch (mode) { | while (codeIndex < codewords[0] && !end) { | |||
case BYTE_COMPACTION_MODE_LATCH: | //handle leading ECIs | |||
// Total number of Byte Compaction characters to be encoded | while (codeIndex < codewords[0] && codewords[codeIndex] == ECI_CHARSET) { | |||
// is not a multiple of 6 | result.appendECI(codewords[++codeIndex]); | |||
codeIndex++; | ||||
int[] byteCompactedCodewords = new int[6]; | } | |||
int nextCode = codewords[codeIndex++]; | ||||
while ((codeIndex < codewords[0]) && !end) { | ||||
byteCompactedCodewords[count++] = nextCode; | ||||
// Base 900 | ||||
value = 900 * value + nextCode; | ||||
nextCode = codewords[codeIndex++]; | ||||
// perhaps it should be ok to check only nextCode >= TEXT_COMPACTION_M | ||||
ODE_LATCH | ||||
switch (nextCode) { | ||||
case TEXT_COMPACTION_MODE_LATCH: | ||||
case BYTE_COMPACTION_MODE_LATCH: | ||||
case NUMERIC_COMPACTION_MODE_LATCH: | ||||
case BYTE_COMPACTION_MODE_LATCH_6: | ||||
case BEGIN_MACRO_PDF417_CONTROL_BLOCK: | ||||
case BEGIN_MACRO_PDF417_OPTIONAL_FIELD: | ||||
case MACRO_PDF417_TERMINATOR: | ||||
codeIndex--; | ||||
end = true; | ||||
break; | ||||
default: | ||||
if ((count % 5 == 0) && (count > 0)) { | ||||
// Decode every 5 codewords | ||||
// Convert to Base 256 | ||||
for (int j = 0; j < 6; ++j) { | ||||
decodedBytes.write((byte) (value >> (8 * (5 - j)))); | ||||
} | ||||
value = 0; | ||||
count = 0; | ||||
} | ||||
break; | ||||
} | ||||
} | ||||
// if the end of all codewords is reached the last codeword needs to be | ||||
added | ||||
if (codeIndex == codewords[0] && nextCode < TEXT_COMPACTION_MODE_LATCH) | ||||
{ | ||||
byteCompactedCodewords[count++] = nextCode; | ||||
} | ||||
// If Byte Compaction mode is invoked with codeword 901, | ||||
// the last group of codewords is interpreted directly | ||||
// as one byte per codeword, without compaction. | ||||
for (int i = 0; i < count; i++) { | ||||
decodedBytes.write((byte) byteCompactedCodewords[i]); | ||||
} | ||||
break; | ||||
case BYTE_COMPACTION_MODE_LATCH_6: | if (codeIndex >= codewords[0] || codewords[codeIndex] >= TEXT_COMPACTION_M | |||
// Total number of Byte Compaction characters to be encoded | ODE_LATCH) { | |||
// is an integer multiple of 6 | end = true; | |||
while (codeIndex < codewords[0] && !end) { | } else { | |||
int code = codewords[codeIndex++]; | //decode one block of 5 codewords to 6 bytes | |||
if (code < TEXT_COMPACTION_MODE_LATCH) { | long value = 0; | |||
count++; | int count = 0; | |||
// Base 900 | do { | |||
value = 900 * value + code; | value = 900 * value + codewords[codeIndex++]; | |||
} else { | count++; | |||
switch (code) { | } while (count < 5 && | |||
case TEXT_COMPACTION_MODE_LATCH: | codeIndex < codewords[0] && | |||
case BYTE_COMPACTION_MODE_LATCH: | codewords[codeIndex] < TEXT_COMPACTION_MODE_LATCH); | |||
case NUMERIC_COMPACTION_MODE_LATCH: | if (count == 5 && (mode == BYTE_COMPACTION_MODE_LATCH_6 || | |||
case BYTE_COMPACTION_MODE_LATCH_6: | codeIndex < codewords[0] && | |||
case BEGIN_MACRO_PDF417_CONTROL_BLOCK: | codewords[codeIndex] < TEXT_COMPACTION_MODE_LATCH)) { | |||
case BEGIN_MACRO_PDF417_OPTIONAL_FIELD: | for (int i = 0; i < 6; i++) { | |||
case MACRO_PDF417_TERMINATOR: | result.append((byte) (value >> (8 * (5 - i)))); | |||
codeIndex--; | ||||
end = true; | ||||
break; | ||||
} | ||||
} | } | |||
if ((count % 5 == 0) && (count > 0)) { | } else { | |||
// Decode every 5 codewords | codeIndex -= count; | |||
// Convert to Base 256 | while ((codeIndex < codewords[0]) && !end) { | |||
for (int j = 0; j < 6; ++j) { | int code = codewords[codeIndex++]; | |||
decodedBytes.write((byte) (value >> (8 * (5 - j)))); | if (code < TEXT_COMPACTION_MODE_LATCH) { | |||
result.append((byte) code); | ||||
} else if (code == ECI_CHARSET) { | ||||
result.appendECI(codewords[codeIndex++]); | ||||
} else { | ||||
codeIndex--; | ||||
end = true; | ||||
} | } | |||
value = 0; | ||||
count = 0; | ||||
} | } | |||
} | } | |||
break; | } | |||
} | } | |||
result.append(new String(decodedBytes.toByteArray(), encoding)); | ||||
return codeIndex; | return codeIndex; | |||
} | } | |||
/** | /** | |||
* Numeric Compaction mode (see 5.4.4) permits efficient encoding of numeric d ata strings. | * Numeric Compaction mode (see 5.4.4) permits efficient encoding of numeric d ata strings. | |||
* | * | |||
* @param codewords The array of codewords (data + error) | * @param codewords The array of codewords (data + error) | |||
* @param codeIndex The current index into the codeword array. | * @param codeIndex The current index into the codeword array. | |||
* @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, StringBui lder result) throws FormatException { | private static int numericCompaction(int[] codewords, int codeIndex, ECIString Builder result) throws FormatException { | |||
int count = 0; | int count = 0; | |||
boolean end = false; | boolean end = false; | |||
int[] numericCodewords = new int[MAX_NUMERIC_CODEWORDS]; | int[] numericCodewords = new int[MAX_NUMERIC_CODEWORDS]; | |||
while (codeIndex < codewords[0] && !end) { | while (codeIndex < codewords[0] && !end) { | |||
int code = codewords[codeIndex++]; | int code = codewords[codeIndex++]; | |||
if (codeIndex == codewords[0]) { | if (codeIndex == codewords[0]) { | |||
end = true; | end = true; | |||
} | } | |||
skipping to change at line 645 | skipping to change at line 625 | |||
numericCodewords[count] = code; | numericCodewords[count] = code; | |||
count++; | count++; | |||
} else { | } else { | |||
switch (code) { | switch (code) { | |||
case TEXT_COMPACTION_MODE_LATCH: | case TEXT_COMPACTION_MODE_LATCH: | |||
case BYTE_COMPACTION_MODE_LATCH: | case BYTE_COMPACTION_MODE_LATCH: | |||
case BYTE_COMPACTION_MODE_LATCH_6: | case BYTE_COMPACTION_MODE_LATCH_6: | |||
case BEGIN_MACRO_PDF417_CONTROL_BLOCK: | case BEGIN_MACRO_PDF417_CONTROL_BLOCK: | |||
case BEGIN_MACRO_PDF417_OPTIONAL_FIELD: | case BEGIN_MACRO_PDF417_OPTIONAL_FIELD: | |||
case MACRO_PDF417_TERMINATOR: | case MACRO_PDF417_TERMINATOR: | |||
case ECI_CHARSET: | ||||
codeIndex--; | codeIndex--; | |||
end = true; | end = true; | |||
break; | break; | |||
} | } | |||
} | } | |||
if ((count % MAX_NUMERIC_CODEWORDS == 0 || code == NUMERIC_COMPACTION_MODE _LATCH || end) && count > 0) { | if ((count % MAX_NUMERIC_CODEWORDS == 0 || code == NUMERIC_COMPACTION_MODE _LATCH || end) && count > 0) { | |||
// Re-invoking Numeric Compaction mode (by using codeword 902 | // Re-invoking Numeric Compaction mode (by using codeword 902 | |||
// while in Numeric Compaction mode) serves to terminate the | // while in Numeric Compaction mode) serves to terminate the | |||
// current Numeric Compaction mode grouping as described in 5.4.4.2, | // current Numeric Compaction mode grouping as described in 5.4.4.2, | |||
// and then to start a new one grouping. | // and then to start a new one grouping. | |||
End of changes. 50 change blocks. | ||||
132 lines changed or deleted | 116 lines changed or added |