123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643 |
- /*
- * 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
|