/* * 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 "ZXBitArray.h" #import "ZXErrors.h" #import "ZXRSSExpandedBlockParsedResult.h" #import "ZXRSSExpandedCurrentParsingState.h" #import "ZXRSSExpandedDecodedChar.h" #import "ZXRSSExpandedDecodedInformation.h" #import "ZXRSSExpandedDecodedNumeric.h" #import "ZXRSSExpandedFieldParser.h" #import "ZXRSSExpandedGeneralAppIdDecoder.h" @interface ZXRSSExpandedGeneralAppIdDecoder () @property (nonatomic, strong, readonly) ZXBitArray *information; @property (nonatomic, strong, readonly) ZXRSSExpandedCurrentParsingState *current; @property (nonatomic, strong, readonly) NSMutableString *buffer; @end @implementation ZXRSSExpandedGeneralAppIdDecoder - (id)initWithInformation:(ZXBitArray *)information { if (self = [super init]) { _current = [[ZXRSSExpandedCurrentParsingState alloc] init]; _buffer = [NSMutableString string]; _information = information; } return self; } - (NSString *)decodeAllCodes:(NSMutableString *)buff initialPosition:(int)initialPosition error:(NSError **)error { int currentPosition = initialPosition; NSString *remaining = nil; do { ZXRSSExpandedDecodedInformation *info = [self decodeGeneralPurposeField:currentPosition remaining:remaining]; if (!info) { if (error) *error = ZXFormatErrorInstance(); return nil; } NSString *parsedFields = [ZXRSSExpandedFieldParser parseFieldsInGeneralPurpose:[info theNewString] error:error]; if (!parsedFields) { return nil; } else if (parsedFields.length > 0) { [buff appendString:parsedFields]; } if ([info remaining]) { remaining = [@([info remainingValue]) stringValue]; } else { remaining = nil; } if (currentPosition == [info theNewPosition]) {// No step forward! break; } currentPosition = [info theNewPosition]; } while (YES); return buff; } - (BOOL)isStillNumeric:(int)pos { // It's numeric if it still has 7 positions // and one of the first 4 bits is "1". if (pos + 7 > self.information.size) { return pos + 4 <= self.information.size; } for (int i = pos; i < pos + 3; ++i) { if ([self.information get:i]) { return YES; } } return [self.information get:pos + 3]; } - (ZXRSSExpandedDecodedNumeric *)decodeNumeric:(int)pos { if (pos + 7 > self.information.size) { int numeric = [self extractNumericValueFromBitArray:pos bits:4]; if (numeric == 0) { return [[ZXRSSExpandedDecodedNumeric alloc] initWithNewPosition:self.information.size firstDigit:ZX_FNC1_INT secondDigit:ZX_FNC1_INT]; } return [[ZXRSSExpandedDecodedNumeric alloc] initWithNewPosition:self.information.size firstDigit:numeric - 1 secondDigit:ZX_FNC1_INT]; } int numeric = [self extractNumericValueFromBitArray:pos bits:7]; int digit1 = (numeric - 8) / 11; int digit2 = (numeric - 8) % 11; return [[ZXRSSExpandedDecodedNumeric alloc] initWithNewPosition:pos + 7 firstDigit:digit1 secondDigit:digit2]; } - (int)extractNumericValueFromBitArray:(int)pos bits:(int)bits { return [ZXRSSExpandedGeneralAppIdDecoder extractNumericValueFromBitArray:self.information pos:pos bits:bits]; } + (int)extractNumericValueFromBitArray:(ZXBitArray *)information pos:(int)pos bits:(int)bits { if (bits > 32) { [NSException raise:NSInvalidArgumentException format:@"extractNumberValueFromBitArray can't handle more than 32 bits"]; } int value = 0; for (int i = 0; i < bits; ++i) { if ([information get:pos + i]) { value |= 1 << (bits - i - 1); } } return value; } - (ZXRSSExpandedDecodedInformation *)decodeGeneralPurposeField:(int)pos remaining:(NSString *)remaining { [self.buffer setString:@""]; if (remaining != nil) { [self.buffer appendString:remaining]; } self.current.position = pos; NSError *error; ZXRSSExpandedDecodedInformation *lastDecoded = [self parseBlocksWithError:&error]; if (error) { return nil; } if (lastDecoded != nil && [lastDecoded remaining]) { return [[ZXRSSExpandedDecodedInformation alloc] initWithNewPosition:self.current.position newString:self.buffer remainingValue:lastDecoded.remainingValue]; } return [[ZXRSSExpandedDecodedInformation alloc] initWithNewPosition:self.current.position newString:self.buffer]; } - (ZXRSSExpandedDecodedInformation *)parseBlocksWithError:(NSError **)error { BOOL isFinished; ZXRSSExpandedBlockParsedResult *result; do { int initialPosition = self.current.position; NSError *localError; if (self.current.alpha) { result = [self parseAlphaBlock]; isFinished = result.finished; } else if (self.current.isoIec646) { result = [self parseIsoIec646BlockWithError:&localError]; isFinished = result.finished; } else { result = [self parseNumericBlockWithError:&localError]; isFinished = result.finished; } if (localError) { if (error) *error = localError; return nil; } BOOL positionChanged = initialPosition != self.current.position; if (!positionChanged && !isFinished) { break; } } while (!isFinished); return result.decodedInformation; } - (ZXRSSExpandedBlockParsedResult *)parseNumericBlockWithError:(NSError **)error { while ([self isStillNumeric:self.current.position]) { ZXRSSExpandedDecodedNumeric *numeric = [self decodeNumeric:self.current.position]; if (!numeric) { if (error) *error = ZXFormatErrorInstance(); return nil; } self.current.position = numeric.theNewPosition; if ([numeric firstDigitFNC1]) { ZXRSSExpandedDecodedInformation *information; if ([numeric secondDigitFNC1]) { information = [[ZXRSSExpandedDecodedInformation alloc] initWithNewPosition:self.current.position newString:self.buffer]; } else { information = [[ZXRSSExpandedDecodedInformation alloc] initWithNewPosition:self.current.position newString:self.buffer remainingValue:numeric.secondDigit]; } return [[ZXRSSExpandedBlockParsedResult alloc] initWithInformation:information finished:YES]; } [self.buffer appendFormat:@"%d", numeric.firstDigit]; if (numeric.secondDigitFNC1) { ZXRSSExpandedDecodedInformation *information = [[ZXRSSExpandedDecodedInformation alloc] initWithNewPosition:self.current.position newString:self.buffer]; return [[ZXRSSExpandedBlockParsedResult alloc] initWithInformation:information finished:YES]; } [self.buffer appendFormat:@"%d", numeric.secondDigit]; } if ([self isNumericToAlphaNumericLatch:self.current.position]) { [self.current setAlpha]; self.current.position += 4; } return [[ZXRSSExpandedBlockParsedResult alloc] initWithFinished:NO]; } - (ZXRSSExpandedBlockParsedResult *)parseIsoIec646BlockWithError:(NSError **)error { while ([self isStillIsoIec646:self.current.position]) { ZXRSSExpandedDecodedChar *iso = [self decodeIsoIec646:self.current.position]; if (!iso) { if (error) *error = ZXFormatErrorInstance(); return nil; } self.current.position = iso.theNewPosition; if (iso.fnc1) { ZXRSSExpandedDecodedInformation *information = [[ZXRSSExpandedDecodedInformation alloc] initWithNewPosition:self.current.position newString:self.buffer]; return [[ZXRSSExpandedBlockParsedResult alloc] initWithInformation:information finished:YES]; } [self.buffer appendFormat:@"%C", iso.value]; } if ([self isAlphaOr646ToNumericLatch:self.current.position]) { self.current.position += 3; [self.current setNumeric]; } else if ([self isAlphaTo646ToAlphaLatch:self.current.position]) { if (self.current.position + 5 < self.information.size) { self.current.position += 5; } else { self.current.position = self.information.size; } [self.current setAlpha]; } return [[ZXRSSExpandedBlockParsedResult alloc] initWithFinished:NO]; } - (ZXRSSExpandedBlockParsedResult *)parseAlphaBlock { while ([self isStillAlpha:self.current.position]) { ZXRSSExpandedDecodedChar *alpha = [self decodeAlphanumeric:self.current.position]; self.current.position = alpha.theNewPosition; if (alpha.fnc1) { ZXRSSExpandedDecodedInformation *information = [[ZXRSSExpandedDecodedInformation alloc] initWithNewPosition:self.current.position newString:self.buffer]; return [[ZXRSSExpandedBlockParsedResult alloc] initWithInformation:information finished:YES]; } [self.buffer appendFormat:@"%C", alpha.value]; } if ([self isAlphaOr646ToNumericLatch:self.current.position]) { self.current.position += 3; [self.current setNumeric]; } else if ([self isAlphaTo646ToAlphaLatch:self.current.position]) { if (self.current.position + 5 < self.information.size) { self.current.position += 5; } else { self.current.position = self.information.size; } [self.current setIsoIec646]; } return [[ZXRSSExpandedBlockParsedResult alloc] initWithFinished:NO]; } - (BOOL)isStillIsoIec646:(int)pos { if (pos + 5 > self.information.size) { return NO; } int fiveBitValue = [self extractNumericValueFromBitArray:pos bits:5]; if (fiveBitValue >= 5 && fiveBitValue < 16) { return YES; } if (pos + 7 > self.information.size) { return NO; } int sevenBitValue = [self extractNumericValueFromBitArray:pos bits:7]; if (sevenBitValue >= 64 && sevenBitValue < 116) { return YES; } if (pos + 8 > self.information.size) { return NO; } int eightBitValue = [self extractNumericValueFromBitArray:pos bits:8]; return eightBitValue >= 232 && eightBitValue < 253; } - (ZXRSSExpandedDecodedChar *)decodeIsoIec646:(int)pos { int fiveBitValue = [self extractNumericValueFromBitArray:pos bits:5]; if (fiveBitValue == 15) { return [[ZXRSSExpandedDecodedChar alloc] initWithNewPosition:pos + 5 value:ZX_FNC1_CHAR]; } if (fiveBitValue >= 5 && fiveBitValue < 15) { return [[ZXRSSExpandedDecodedChar alloc] initWithNewPosition:pos + 5 value:(unichar)('0' + fiveBitValue - 5)]; } int sevenBitValue = [self extractNumericValueFromBitArray:pos bits:7]; if (sevenBitValue >= 64 && sevenBitValue < 90) { return [[ZXRSSExpandedDecodedChar alloc] initWithNewPosition:pos + 7 value:(unichar)(sevenBitValue + 1)]; } if (sevenBitValue >= 90 && sevenBitValue < 116) { return [[ZXRSSExpandedDecodedChar alloc] initWithNewPosition:pos + 7 value:(unichar)(sevenBitValue + 7)]; } int eightBitValue = [self extractNumericValueFromBitArray:pos bits:8]; unichar c; switch (eightBitValue) { case 232: c = '!'; break; case 233: c = '"'; break; case 234: c ='%'; break; case 235: c = '&'; break; case 236: c = '\''; break; case 237: c = '('; break; case 238: c = ')'; break; case 239: c = '*'; break; case 240: c = '+'; break; case 241: c = ','; break; case 242: c = '-'; break; case 243: c = '.'; break; case 244: c = '/'; break; case 245: c = ':'; break; case 246: c = ';'; break; case 247: c = '<'; break; case 248: c = '='; break; case 249: c = '>'; break; case 250: c = '?'; break; case 251: c = '_'; break; case 252: c = ' '; break; default: return nil; } return [[ZXRSSExpandedDecodedChar alloc] initWithNewPosition:pos + 8 value:c]; } - (BOOL)isStillAlpha:(int)pos { if (pos + 5 > self.information.size) { return NO; } // We now check if it's a valid 5-bit value (0..9 and FNC1) int fiveBitValue = [self extractNumericValueFromBitArray:pos bits:5]; if (fiveBitValue >= 5 && fiveBitValue < 16) { return YES; } if (pos + 6 > self.information.size) { return NO; } int sixBitValue = [self extractNumericValueFromBitArray:pos bits:6]; return sixBitValue >= 16 && sixBitValue < 63; // 63 not included } - (ZXRSSExpandedDecodedChar *)decodeAlphanumeric:(int)pos { int fiveBitValue = [self extractNumericValueFromBitArray:pos bits:5]; if (fiveBitValue == 15) { return [[ZXRSSExpandedDecodedChar alloc] initWithNewPosition:pos + 5 value:ZX_FNC1_CHAR]; } if (fiveBitValue >= 5 && fiveBitValue < 15) { return [[ZXRSSExpandedDecodedChar alloc] initWithNewPosition:pos + 5 value:(unichar)('0' + fiveBitValue - 5)]; } int sixBitValue = [self extractNumericValueFromBitArray:pos bits:6]; if (sixBitValue >= 32 && sixBitValue < 58) { return [[ZXRSSExpandedDecodedChar alloc] initWithNewPosition:pos + 6 value:(unichar)(sixBitValue + 33)]; } unichar c; switch (sixBitValue){ case 58: c = '*'; break; case 59: c = ','; break; case 60: c = '-'; break; case 61: c = '.'; break; case 62: c = '/'; break; default: @throw [NSException exceptionWithName:@"RuntimeException" reason:[NSString stringWithFormat:@"Decoding invalid alphanumeric value: %d", sixBitValue] userInfo:nil]; } return [[ZXRSSExpandedDecodedChar alloc] initWithNewPosition:pos + 6 value:c]; } - (BOOL)isAlphaTo646ToAlphaLatch:(int)pos { if (pos + 1 > self.information.size) { return NO; } for (int i = 0; i < 5 && i + pos < self.information.size; ++i) { if (i == 2) { if (![self.information get:pos + 2]) { return NO; } } else if ([self.information get:pos + i]) { return NO; } } return YES; } - (BOOL)isAlphaOr646ToNumericLatch:(int)pos { // Next is alphanumeric if there are 3 positions and they are all zeros if (pos + 3 > self.information.size) { return NO; } for (int i = pos; i < pos + 3; ++i) { if ([self.information get:i]) { return NO; } } return YES; } - (BOOL)isNumericToAlphaNumericLatch:(int)pos { // Next is alphanumeric if there are 4 positions and they are all zeros, or // if there is a subset of this just before the end of the symbol if (pos + 1 > self.information.size) { return NO; } for (int i = 0; i < 4 && i + pos < self.information.size; ++i) { if ([self.information get:pos + i]) { return NO; } } return YES; } @end