/*
|
* Copyright 2012 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.
|
*/
|
|
#import "ZXCharacterSetECI.h"
|
#import "ZXDecoderResult.h"
|
#import "ZXErrors.h"
|
#import "ZXIntArray.h"
|
#import "ZXPDF417DecodedBitStreamParser.h"
|
#import "ZXPDF417ResultMetadata.h"
|
|
typedef enum {
|
ZXPDF417ModeAlpha = 0,
|
ZXPDF417ModeLower,
|
ZXPDF417ModeMixed,
|
ZXPDF417ModePunct,
|
ZXPDF417ModeAlphaShift,
|
ZXPDF417ModePunctShift
|
} ZXPDF417Mode;
|
|
const int ZX_PDF417_TEXT_COMPACTION_MODE_LATCH = 900;
|
const int ZX_PDF417_BYTE_COMPACTION_MODE_LATCH = 901;
|
const int ZX_PDF417_NUMERIC_COMPACTION_MODE_LATCH = 902;
|
const int ZX_PDF417_BYTE_COMPACTION_MODE_LATCH_6 = 924;
|
const int ZX_PDF417_ECI_USER_DEFINED = 925;
|
const int ZX_PDF417_ECI_GENERAL_PURPOSE = 926;
|
const int ZX_PDF417_ECI_CHARSET = 927;
|
const int ZX_PDF417_BEGIN_MACRO_PDF417_CONTROL_BLOCK = 928;
|
const int ZX_PDF417_BEGIN_MACRO_PDF417_OPTIONAL_FIELD = 923;
|
const int ZX_PDF417_MACRO_PDF417_TERMINATOR = 922;
|
const int ZX_PDF417_MODE_SHIFT_TO_BYTE_COMPACTION_MODE = 913;
|
const int ZX_PDF417_MAX_NUMERIC_CODEWORDS = 15;
|
|
const int ZX_PDF417_PL = 25;
|
const int ZX_PDF417_LL = 27;
|
const int ZX_PDF417_AS = 27;
|
const int ZX_PDF417_ML = 28;
|
const int ZX_PDF417_AL = 28;
|
const int ZX_PDF417_PS = 29;
|
const int ZX_PDF417_PAL = 29;
|
|
const unichar ZX_PDF417_PUNCT_CHARS[] = {
|
';', '<', '>', '@', '[', '\\', ']', '_', '`', '~', '!',
|
'\r', '\t', ',', ':', '\n', '-', '.', '$', '/', '"', '|', '*',
|
'(', ')', '?', '{', '}', '\''};
|
|
const unichar ZX_PDF417_MIXED_CHARS[] = {
|
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '&',
|
'\r', '\t', ',', ':', '#', '-', '.', '$', '/', '+', '%', '*',
|
'=', '^'};
|
|
const int ZX_PDF417_NUMBER_OF_SEQUENCE_CODEWORDS = 2;
|
|
const NSStringEncoding ZX_PDF417_DECODING_DEFAULT_ENCODING = NSISOLatin1StringEncoding;
|
|
/**
|
* Table containing values for the exponent of 900.
|
* This is used in the numeric compaction decode algorithm.
|
*/
|
static NSArray *ZX_PDF417_EXP900 = nil;
|
|
@implementation ZXPDF417DecodedBitStreamParser
|
|
+ (void)initialize {
|
if ([self class] != [ZXPDF417DecodedBitStreamParser class]) return;
|
|
NSMutableArray *exponents = [NSMutableArray arrayWithCapacity:16];
|
[exponents addObject:[NSDecimalNumber one]];
|
NSDecimalNumber *nineHundred = [NSDecimalNumber decimalNumberWithString:@"900"];
|
[exponents addObject:nineHundred];
|
for (int i = 2; i < 16; i++) {
|
[exponents addObject:[exponents[i - 1] decimalNumberByMultiplyingBy:nineHundred]];
|
}
|
ZX_PDF417_EXP900 = [[NSArray alloc] initWithArray:exponents];
|
}
|
|
+ (ZXDecoderResult *)decode:(ZXIntArray *)codewords ecLevel:(NSString *)ecLevel error:(NSError **)error {
|
NSMutableString *result = [NSMutableString stringWithCapacity:codewords.length * 2];
|
NSStringEncoding encoding = ZX_PDF417_DECODING_DEFAULT_ENCODING;
|
// Get compaction mode
|
int codeIndex = 1;
|
int code = codewords.array[codeIndex++];
|
ZXPDF417ResultMetadata *resultMetadata = [[ZXPDF417ResultMetadata alloc] init];
|
while (codeIndex < codewords.array[0]) {
|
switch (code) {
|
case ZX_PDF417_TEXT_COMPACTION_MODE_LATCH:
|
codeIndex = [self textCompaction:codewords codeIndex:codeIndex result:result];
|
break;
|
case ZX_PDF417_BYTE_COMPACTION_MODE_LATCH:
|
case ZX_PDF417_BYTE_COMPACTION_MODE_LATCH_6:
|
codeIndex = [self byteCompaction:code codewords:codewords encoding:encoding codeIndex:codeIndex result:result];
|
break;
|
case ZX_PDF417_MODE_SHIFT_TO_BYTE_COMPACTION_MODE:
|
[result appendFormat:@"%C", (unichar)codewords.array[codeIndex++]];
|
break;
|
case ZX_PDF417_NUMERIC_COMPACTION_MODE_LATCH:
|
codeIndex = [self numericCompaction:codewords codeIndex:codeIndex result:result];
|
if (codeIndex < 0) {
|
if (error) *error = ZXFormatErrorInstance();
|
return nil;
|
}
|
break;
|
case ZX_PDF417_ECI_CHARSET: {
|
ZXCharacterSetECI *charsetECI =
|
[ZXCharacterSetECI characterSetECIByValue:codewords.array[codeIndex++]];
|
encoding = charsetECI.encoding;
|
break;
|
}
|
case ZX_PDF417_ECI_GENERAL_PURPOSE:
|
// Can't do anything with generic ECI; skip its 2 characters
|
codeIndex += 2;
|
break;
|
case ZX_PDF417_ECI_USER_DEFINED:
|
// Can't do anything with user ECI; skip its 1 character
|
codeIndex ++;
|
break;
|
case ZX_PDF417_BEGIN_MACRO_PDF417_CONTROL_BLOCK:
|
codeIndex = [self decodeMacroBlock:codewords codeIndex:codeIndex resultMetadata:resultMetadata];
|
if (codeIndex < 0) {
|
if (error) *error = ZXFormatErrorInstance();
|
return nil;
|
}
|
break;
|
case ZX_PDF417_BEGIN_MACRO_PDF417_OPTIONAL_FIELD:
|
case ZX_PDF417_MACRO_PDF417_TERMINATOR:
|
// Should not see these outside a macro block
|
if (error) *error = ZXFormatErrorInstance();
|
return nil;
|
default:
|
// Default to text compaction. During testing numerous barcodes
|
// appeared to be missing the starting mode. In these cases defaulting
|
// to text compaction seems to work.
|
codeIndex--;
|
codeIndex = [self textCompaction:codewords codeIndex:codeIndex result:result];
|
break;
|
}
|
if (codeIndex < codewords.length) {
|
code = codewords.array[codeIndex++];
|
} else {
|
if (error) *error = ZXFormatErrorInstance();
|
return nil;
|
}
|
}
|
if ([result length] == 0) {
|
if (error) *error = ZXFormatErrorInstance();
|
return nil;
|
}
|
ZXDecoderResult *decoderResult = [[ZXDecoderResult alloc] initWithRawBytes:nil text:result byteSegments:nil ecLevel:ecLevel];
|
decoderResult.other = resultMetadata;
|
return decoderResult;
|
}
|
|
+ (int)decodeMacroBlock:(ZXIntArray *)codewords codeIndex:(int)codeIndex resultMetadata:(ZXPDF417ResultMetadata *)resultMetadata {
|
if (codeIndex + ZX_PDF417_NUMBER_OF_SEQUENCE_CODEWORDS > codewords.array[0]) {
|
// we must have at least two bytes left for the segment index
|
return -1;
|
}
|
ZXIntArray *segmentIndexArray = [[ZXIntArray alloc] initWithLength:ZX_PDF417_NUMBER_OF_SEQUENCE_CODEWORDS];
|
for (int i = 0; i < ZX_PDF417_NUMBER_OF_SEQUENCE_CODEWORDS; i++, codeIndex++) {
|
segmentIndexArray.array[i] = codewords.array[codeIndex];
|
}
|
resultMetadata.segmentIndex = [[self decodeBase900toBase10:segmentIndexArray count:ZX_PDF417_NUMBER_OF_SEQUENCE_CODEWORDS] intValue];
|
|
NSMutableString *fileId = [NSMutableString string];
|
codeIndex = [self textCompaction:codewords codeIndex:codeIndex result:fileId];
|
resultMetadata.fileId = [NSString stringWithString:fileId];
|
|
if (codewords.array[codeIndex] == ZX_PDF417_BEGIN_MACRO_PDF417_OPTIONAL_FIELD) {
|
codeIndex++;
|
NSMutableArray *additionalOptionCodeWords = [NSMutableArray array];
|
|
BOOL end = NO;
|
while ((codeIndex < codewords.array[0]) && !end) {
|
int code = codewords.array[codeIndex++];
|
if (code < ZX_PDF417_TEXT_COMPACTION_MODE_LATCH) {
|
[additionalOptionCodeWords addObject:@(code)];
|
} else {
|
switch (code) {
|
case ZX_PDF417_MACRO_PDF417_TERMINATOR:
|
resultMetadata.lastSegment = YES;
|
codeIndex++;
|
end = YES;
|
break;
|
default:
|
return -1;
|
}
|
}
|
}
|
|
resultMetadata.optionalData = additionalOptionCodeWords;
|
} else if (codewords.array[codeIndex] == ZX_PDF417_MACRO_PDF417_TERMINATOR) {
|
resultMetadata.lastSegment = YES;
|
codeIndex++;
|
}
|
|
return codeIndex;
|
}
|
|
/**
|
* Text Compaction mode (see 5.4.1.5) permits all printable ASCII characters to be
|
* encoded, i.e. values 32 - 126 inclusive in accordance with ISO/IEC 646 (IRV), as
|
* well as selected control characters.
|
*
|
* @param codewords The array of codewords (data + error)
|
* @param codeIndex The current index into the codeword array.
|
* @param result The decoded data is appended to the result.
|
* @return The next index into the codeword array.
|
*/
|
+ (int)textCompaction:(ZXIntArray *)codewords codeIndex:(int)codeIndex result:(NSMutableString *)result {
|
// 2 character per codeword
|
ZXIntArray *textCompactionData = [[ZXIntArray alloc] initWithLength:(codewords.array[0] - codeIndex) * 2];
|
// Used to hold the byte compaction value if there is a mode shift
|
ZXIntArray *byteCompactionData = [[ZXIntArray alloc] initWithLength:(codewords.array[0] - codeIndex) * 2];
|
|
int index = 0;
|
BOOL end = NO;
|
while ((codeIndex < codewords.array[0]) && !end) {
|
int code = codewords.array[codeIndex++];
|
if (code < ZX_PDF417_TEXT_COMPACTION_MODE_LATCH) {
|
textCompactionData.array[index] = code / 30;
|
textCompactionData.array[index + 1] = code % 30;
|
index += 2;
|
} else {
|
switch (code) {
|
case ZX_PDF417_TEXT_COMPACTION_MODE_LATCH:
|
// reinitialize text compaction mode to alpha sub mode
|
textCompactionData.array[index++] = ZX_PDF417_TEXT_COMPACTION_MODE_LATCH;
|
break;
|
case ZX_PDF417_BYTE_COMPACTION_MODE_LATCH:
|
case ZX_PDF417_BYTE_COMPACTION_MODE_LATCH_6:
|
case ZX_PDF417_NUMERIC_COMPACTION_MODE_LATCH:
|
case ZX_PDF417_BEGIN_MACRO_PDF417_CONTROL_BLOCK:
|
case ZX_PDF417_BEGIN_MACRO_PDF417_OPTIONAL_FIELD:
|
case ZX_PDF417_MACRO_PDF417_TERMINATOR:
|
codeIndex--;
|
end = YES;
|
break;
|
case ZX_PDF417_MODE_SHIFT_TO_BYTE_COMPACTION_MODE:
|
// The Mode Shift codeword 913 shall cause a temporary
|
// switch from Text Compaction mode to Byte Compaction mode.
|
// This switch shall be in effect for only the next codeword,
|
// after which the mode shall revert to the prevailing sub-mode
|
// of the Text Compaction mode. Codeword 913 is only available
|
// in Text Compaction mode; its use is described in 5.4.2.4.
|
textCompactionData.array[index] = ZX_PDF417_MODE_SHIFT_TO_BYTE_COMPACTION_MODE;
|
code = codewords.array[codeIndex++];
|
byteCompactionData.array[index] = code;
|
index++;
|
break;
|
}
|
}
|
}
|
|
[self decodeTextCompaction:textCompactionData byteCompactionData:byteCompactionData length:index result:result];
|
return codeIndex;
|
}
|
|
/**
|
* 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
|
* (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 latch
|
* and shift characters which are used exclusively within the mode. The Text
|
* Compaction mode encodes up to 2 characters per codeword. The compaction rules
|
* for converting data into PDF417 codewords are defined in 5.4.2.2. The sub-mode
|
* switches are defined in 5.4.2.3.
|
*
|
* @param textCompactionData The text compaction data.
|
* @param byteCompactionData The byte compaction data if there
|
* was a mode shift.
|
* @param length The size of the text compaction and byte compaction data.
|
* @param result The decoded data is appended to the result.
|
*/
|
+ (void)decodeTextCompaction:(ZXIntArray *)textCompactionData byteCompactionData:(ZXIntArray *)byteCompactionData length:(unsigned int)length result:(NSMutableString *)result {
|
// Beginning from an initial state of the Alpha sub-mode
|
// The default compaction mode for PDF417 in effect at the start of each symbol shall always be Text
|
// Compaction mode Alpha sub-mode (uppercase alphabetic). A latch codeword from another mode to the Text
|
// Compaction mode shall always switch to the Text Compaction Alpha sub-mode.
|
ZXPDF417Mode subMode = ZXPDF417ModeAlpha;
|
ZXPDF417Mode priorToShiftMode = ZXPDF417ModeAlpha;
|
int i = 0;
|
while (i < length) {
|
int subModeCh = textCompactionData.array[i];
|
unichar ch = 0;
|
switch (subMode) {
|
case ZXPDF417ModeAlpha:
|
// Alpha (uppercase alphabetic)
|
if (subModeCh < 26) {
|
// Upper case Alpha Character
|
ch = (unichar)('A' + subModeCh);
|
} else {
|
if (subModeCh == 26) {
|
ch = ' ';
|
} else if (subModeCh == ZX_PDF417_LL) {
|
subMode = ZXPDF417ModeLower;
|
} else if (subModeCh == ZX_PDF417_ML) {
|
subMode = ZXPDF417ModeMixed;
|
} else if (subModeCh == ZX_PDF417_PS) {
|
// Shift to punctuation
|
priorToShiftMode = subMode;
|
subMode = ZXPDF417ModePunctShift;
|
} else if (subModeCh == ZX_PDF417_MODE_SHIFT_TO_BYTE_COMPACTION_MODE) {
|
// TODO Does this need to use the current character encoding? See other occurrences below
|
[result appendFormat:@"%C", (unichar)byteCompactionData.array[i]];
|
} else if (subModeCh == ZX_PDF417_TEXT_COMPACTION_MODE_LATCH) {
|
subMode = ZXPDF417ModeAlpha;
|
}
|
}
|
break;
|
|
case ZXPDF417ModeLower:
|
// Lower (lowercase alphabetic)
|
if (subModeCh < 26) {
|
ch = (unichar)('a' + subModeCh);
|
} else {
|
if (subModeCh == 26) {
|
ch = ' ';
|
} else if (subModeCh == ZX_PDF417_AS) {
|
// Shift to alpha
|
priorToShiftMode = subMode;
|
subMode = ZXPDF417ModeAlphaShift;
|
} else if (subModeCh == ZX_PDF417_ML) {
|
subMode = ZXPDF417ModeMixed;
|
} else if (subModeCh == ZX_PDF417_PS) {
|
// Shift to punctuation
|
priorToShiftMode = subMode;
|
subMode = ZXPDF417ModePunctShift;
|
} else if (subModeCh == ZX_PDF417_MODE_SHIFT_TO_BYTE_COMPACTION_MODE) {
|
[result appendFormat:@"%C", (unichar)byteCompactionData.array[i]];
|
} else if (subModeCh == ZX_PDF417_TEXT_COMPACTION_MODE_LATCH) {
|
subMode = ZXPDF417ModeAlpha;
|
}
|
}
|
break;
|
|
case ZXPDF417ModeMixed:
|
// Mixed (numeric and some punctuation)
|
if (subModeCh < ZX_PDF417_PL) {
|
ch = ZX_PDF417_MIXED_CHARS[subModeCh];
|
} else {
|
if (subModeCh == ZX_PDF417_PL) {
|
subMode = ZXPDF417ModePunct;
|
} else if (subModeCh == 26) {
|
ch = ' ';
|
} else if (subModeCh == ZX_PDF417_LL) {
|
subMode = ZXPDF417ModeLower;
|
} else if (subModeCh == ZX_PDF417_AL) {
|
subMode = ZXPDF417ModeAlpha;
|
} else if (subModeCh == ZX_PDF417_PS) {
|
// Shift to punctuation
|
priorToShiftMode = subMode;
|
subMode = ZXPDF417ModePunctShift;
|
} else if (subModeCh == ZX_PDF417_MODE_SHIFT_TO_BYTE_COMPACTION_MODE) {
|
[result appendFormat:@"%C", (unichar)byteCompactionData.array[i]];
|
} else if (subModeCh == ZX_PDF417_TEXT_COMPACTION_MODE_LATCH) {
|
subMode = ZXPDF417ModeAlpha;
|
}
|
}
|
break;
|
|
case ZXPDF417ModePunct:
|
// Punctuation
|
if (subModeCh < ZX_PDF417_PAL) {
|
ch = ZX_PDF417_PUNCT_CHARS[subModeCh];
|
} else {
|
if (subModeCh == ZX_PDF417_PAL) {
|
subMode = ZXPDF417ModeAlpha;
|
} else if (subModeCh == ZX_PDF417_MODE_SHIFT_TO_BYTE_COMPACTION_MODE) {
|
[result appendFormat:@"%C", (unichar)byteCompactionData.array[i]];
|
} else if (ZX_PDF417_TEXT_COMPACTION_MODE_LATCH) {
|
subMode = ZXPDF417ModeAlpha;
|
}
|
}
|
break;
|
|
case ZXPDF417ModeAlphaShift:
|
// Restore sub-mode
|
subMode = priorToShiftMode;
|
if (subModeCh < 26) {
|
ch = (unichar)('A' + subModeCh);
|
} else {
|
if (subModeCh == 26) {
|
ch = ' ';
|
} else if (subModeCh == ZX_PDF417_TEXT_COMPACTION_MODE_LATCH) {
|
subMode = ZXPDF417ModeAlpha;
|
}
|
}
|
break;
|
|
case ZXPDF417ModePunctShift:
|
// Restore sub-mode
|
subMode = priorToShiftMode;
|
if (subModeCh < ZX_PDF417_PAL) {
|
ch = ZX_PDF417_PUNCT_CHARS[subModeCh];
|
} else {
|
if (subModeCh == ZX_PDF417_PAL) {
|
subMode = ZXPDF417ModeAlpha;
|
} else if (subModeCh == ZX_PDF417_MODE_SHIFT_TO_BYTE_COMPACTION_MODE) {
|
// PS before Shift-to-Byte is used as a padding character,
|
// see 5.4.2.4 of the specification
|
[result appendFormat:@"%C", (unichar)byteCompactionData.array[i]];
|
} else if (subModeCh == ZX_PDF417_TEXT_COMPACTION_MODE_LATCH) {
|
subMode = ZXPDF417ModeAlpha;
|
}
|
}
|
break;
|
}
|
if (ch != 0) {
|
// Append decoded character to result
|
[result appendFormat:@"%C", ch];
|
}
|
i++;
|
}
|
}
|
|
/**
|
* 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 for international
|
* character set support.
|
*
|
* @param mode The byte compaction mode i.e. 901 or 924
|
* @param codewords The array of codewords (data + error)
|
* @param encoding Currently active character encoding
|
* @param codeIndex The current index into the codeword array.
|
* @param result The decoded data is appended to the result.
|
* @return The next index into the codeword array.
|
*/
|
+ (int)byteCompaction:(int)mode
|
codewords:(ZXIntArray *)codewords
|
encoding:(NSStringEncoding)encoding
|
codeIndex:(int)codeIndex
|
result:(NSMutableString *)result {
|
NSMutableData *decodedBytes = [NSMutableData data];
|
if (mode == ZX_PDF417_BYTE_COMPACTION_MODE_LATCH) {
|
// Total number of Byte Compaction characters to be encoded
|
// is not a multiple of 6
|
int count = 0;
|
long long value = 0;
|
ZXIntArray *byteCompactedCodewords = [[ZXIntArray alloc] initWithLength:6];
|
BOOL end = NO;
|
int nextCode = codewords.array[codeIndex++];
|
while ((codeIndex < codewords.array[0]) && !end) {
|
byteCompactedCodewords.array[count++] = nextCode;
|
// Base 900
|
value = 900 * value + nextCode;
|
nextCode = codewords.array[codeIndex++];
|
// perhaps it should be ok to check only nextCode >= TEXT_COMPACTION_MODE_LATCH
|
if (nextCode == ZX_PDF417_TEXT_COMPACTION_MODE_LATCH ||
|
nextCode == ZX_PDF417_BYTE_COMPACTION_MODE_LATCH ||
|
nextCode == ZX_PDF417_NUMERIC_COMPACTION_MODE_LATCH ||
|
nextCode == ZX_PDF417_BYTE_COMPACTION_MODE_LATCH_6 ||
|
nextCode == ZX_PDF417_BEGIN_MACRO_PDF417_CONTROL_BLOCK ||
|
nextCode == ZX_PDF417_BEGIN_MACRO_PDF417_OPTIONAL_FIELD ||
|
nextCode == ZX_PDF417_MACRO_PDF417_TERMINATOR) {
|
codeIndex--;
|
end = YES;
|
} else {
|
if ((count % 5 == 0) && (count > 0)) {
|
// Decode every 5 codewords
|
// Convert to Base 256
|
for (int j = 0; j < 6; ++j) {
|
int8_t byte = (int8_t) (value >> (8 * (5 - j)));
|
[decodedBytes appendBytes:&byte length:1];
|
}
|
value = 0;
|
count = 0;
|
}
|
}
|
}
|
|
// if the end of all codewords is reached the last codeword needs to be added
|
if (codeIndex == codewords.array[0] && nextCode < ZX_PDF417_TEXT_COMPACTION_MODE_LATCH) {
|
byteCompactedCodewords.array[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++) {
|
int8_t byte = (int8_t)byteCompactedCodewords.array[i];
|
[decodedBytes appendBytes:&byte length:1];
|
}
|
} else if (mode == ZX_PDF417_BYTE_COMPACTION_MODE_LATCH_6) {
|
// Total number of Byte Compaction characters to be encoded
|
// is an integer multiple of 6
|
int count = 0;
|
long long value = 0;
|
BOOL end = NO;
|
while (codeIndex < codewords.array[0] && !end) {
|
int code = codewords.array[codeIndex++];
|
if (code < ZX_PDF417_TEXT_COMPACTION_MODE_LATCH) {
|
count++;
|
// Base 900
|
value = 900 * value + code;
|
} else {
|
if (code == ZX_PDF417_TEXT_COMPACTION_MODE_LATCH ||
|
code == ZX_PDF417_BYTE_COMPACTION_MODE_LATCH ||
|
code == ZX_PDF417_NUMERIC_COMPACTION_MODE_LATCH ||
|
code == ZX_PDF417_BYTE_COMPACTION_MODE_LATCH_6 ||
|
code == ZX_PDF417_BEGIN_MACRO_PDF417_CONTROL_BLOCK ||
|
code == ZX_PDF417_BEGIN_MACRO_PDF417_OPTIONAL_FIELD ||
|
code == ZX_PDF417_MACRO_PDF417_TERMINATOR) {
|
codeIndex--;
|
end = YES;
|
}
|
}
|
if ((count % 5 == 0) && (count > 0)) {
|
// Decode every 5 codewords
|
// Convert to Base 256
|
for (int j = 0; j < 6; ++j) {
|
int8_t byte = (int8_t) (value >> (8 * (5 - j)));
|
[decodedBytes appendBytes:&byte length:1];
|
}
|
value = 0;
|
count = 0;
|
}
|
}
|
}
|
[result appendString:[[NSString alloc] initWithData:decodedBytes encoding:encoding]];
|
return codeIndex;
|
}
|
|
/**
|
* Numeric Compaction mode (see 5.4.4) permits efficient encoding of numeric data strings.
|
*
|
* @param codewords The array of codewords (data + error)
|
* @param codeIndex The current index into the codeword array.
|
* @param result The decoded data is appended to the result.
|
* @return The next index into the codeword array.
|
*/
|
+ (int)numericCompaction:(ZXIntArray *)codewords codeIndex:(int)codeIndex result:(NSMutableString *)result {
|
int count = 0;
|
BOOL end = NO;
|
|
ZXIntArray *numericCodewords = [[ZXIntArray alloc] initWithLength:ZX_PDF417_MAX_NUMERIC_CODEWORDS];
|
|
while (codeIndex < codewords.array[0] && !end) {
|
int code = codewords.array[codeIndex++];
|
if (codeIndex == codewords.array[0]) {
|
end = YES;
|
}
|
if (code < ZX_PDF417_TEXT_COMPACTION_MODE_LATCH) {
|
numericCodewords.array[count] = code;
|
count++;
|
} else {
|
if (code == ZX_PDF417_TEXT_COMPACTION_MODE_LATCH ||
|
code == ZX_PDF417_BYTE_COMPACTION_MODE_LATCH ||
|
code == ZX_PDF417_BYTE_COMPACTION_MODE_LATCH_6 ||
|
code == ZX_PDF417_BEGIN_MACRO_PDF417_CONTROL_BLOCK ||
|
code == ZX_PDF417_BEGIN_MACRO_PDF417_OPTIONAL_FIELD ||
|
code == ZX_PDF417_MACRO_PDF417_TERMINATOR) {
|
codeIndex--;
|
end = YES;
|
}
|
}
|
if (count % ZX_PDF417_MAX_NUMERIC_CODEWORDS == 0 ||
|
code == ZX_PDF417_NUMERIC_COMPACTION_MODE_LATCH ||
|
end) {
|
// Re-invoking Numeric Compaction mode (by using codeword 902
|
// while in Numeric Compaction mode) serves to terminate the
|
// current Numeric Compaction mode grouping as described in 5.4.4.2,
|
// and then to start a new one grouping.
|
if (count > 0) {
|
NSString *s = [self decodeBase900toBase10:numericCodewords count:count];
|
if (s == nil) {
|
return -1;
|
}
|
[result appendString:s];
|
count = 0;
|
}
|
}
|
}
|
return codeIndex;
|
}
|
|
/**
|
* Convert a list of Numeric Compacted codewords from Base 900 to Base 10.
|
*
|
* @param codewords The array of codewords
|
* @param count The number of codewords
|
* @return The decoded string representing the Numeric data.
|
*/
|
/*
|
EXAMPLE
|
Encode the fifteen digit numeric string 000213298174000
|
Prefix the numeric string with a 1 and set the initial value of
|
t = 1 000 213 298 174 000
|
Calculate codeword 0
|
d0 = 1 000 213 298 174 000 mod 900 = 200
|
|
t = 1 000 213 298 174 000 div 900 = 1 111 348 109 082
|
Calculate codeword 1
|
d1 = 1 111 348 109 082 mod 900 = 282
|
|
t = 1 111 348 109 082 div 900 = 1 234 831 232
|
Calculate codeword 2
|
d2 = 1 234 831 232 mod 900 = 632
|
|
t = 1 234 831 232 div 900 = 1 372 034
|
Calculate codeword 3
|
d3 = 1 372 034 mod 900 = 434
|
|
t = 1 372 034 div 900 = 1 524
|
Calculate codeword 4u
|
d4 = 1 524 mod 900 = 624
|
|
t = 1 524 div 900 = 1
|
Calculate codeword 5
|
d5 = 1 mod 900 = 1
|
t = 1 div 900 = 0
|
Codeword sequence is: 1, 624, 434, 632, 282, 200
|
|
Decode the above codewords involves
|
1 x 900 power of 5 + 624 x 900 power of 4 + 434 x 900 power of 3 +
|
632 x 900 power of 2 + 282 x 900 power of 1 + 200 x 900 power of 0 = 1000213298174000
|
|
Remove leading 1 => Result is 000213298174000
|
*/
|
+ (NSString *)decodeBase900toBase10:(ZXIntArray *)codewords count:(int)count {
|
NSDecimalNumber *result = [NSDecimalNumber zero];
|
for (int i = 0; i < count; i++) {
|
result = [result decimalNumberByAdding:[ZX_PDF417_EXP900[count - i - 1] decimalNumberByMultiplyingBy:[NSDecimalNumber decimalNumberWithDecimal:[@(codewords.array[i]) decimalValue]]]];
|
}
|
NSString *resultString = [result stringValue];
|
if (![resultString hasPrefix:@"1"]) {
|
return nil;
|
}
|
return [resultString substringFromIndex:1];
|
}
|
|
@end
|