/* * 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 "ZXBitSource.h" #import "ZXByteArray.h" #import "ZXCharacterSetECI.h" #import "ZXDecoderResult.h" #import "ZXErrors.h" #import "ZXQRCodeDecodedBitStreamParser.h" #import "ZXQRCodeErrorCorrectionLevel.h" #import "ZXQRCodeMode.h" #import "ZXQRCodeVersion.h" #import "ZXStringUtils.h" /** * See ISO 18004:2006, 6.4.4 Table 5 */ const unichar ZX_ALPHANUMERIC_CHARS[45] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', ' ', '$', '%', '*', '+', '-', '.', '/', ':' }; const int ZX_GB2312_SUBSET = 1; @implementation ZXQRCodeDecodedBitStreamParser + (ZXDecoderResult *)decode:(ZXByteArray *)bytes version:(ZXQRCodeVersion *)version ecLevel:(ZXQRCodeErrorCorrectionLevel *)ecLevel hints:(ZXDecodeHints *)hints error:(NSError **)error { ZXBitSource *bits = [[ZXBitSource alloc] initWithBytes:bytes]; NSMutableString *result = [NSMutableString stringWithCapacity:50]; NSMutableArray *byteSegments = [NSMutableArray arrayWithCapacity:1]; int symbolSequence = -1; int parityData = -1; ZXCharacterSetECI *currentCharacterSetECI = nil; ZXQRCodeMode *mode; BOOL fc1InEffect = NO; do { // While still another segment to read... if ([bits available] < 4) { // OK, assume we're done. Really, a TERMINATOR mode should have been recorded here mode = [ZXQRCodeMode terminatorMode]; } else { mode = [ZXQRCodeMode forBits:[bits readBits:4]]; // mode is encoded by 4 bits if (!mode) { if (error) *error = ZXFormatErrorInstance(); return nil; } } if (![mode isEqual:[ZXQRCodeMode terminatorMode]]) { if ([mode isEqual:[ZXQRCodeMode fnc1FirstPositionMode]] || [mode isEqual:[ZXQRCodeMode fnc1SecondPositionMode]]) { // We do little with FNC1 except alter the parsed result a bit according to the spec fc1InEffect = YES; } else if ([mode isEqual:[ZXQRCodeMode structuredAppendMode]]) { if (bits.available < 16) { if (error) *error = ZXFormatErrorInstance(); return nil; } // sequence number and parity is added later to the result metadata // Read next 8 bits (symbol sequence #) and 8 bits (parity data), then continue symbolSequence = [bits readBits:8]; parityData = [bits readBits:8]; } else if ([mode isEqual:[ZXQRCodeMode eciMode]]) { // Count doesn't apply to ECI int value = [self parseECIValue:bits]; currentCharacterSetECI = [ZXCharacterSetECI characterSetECIByValue:value]; if (currentCharacterSetECI == nil) { if (error) *error = ZXFormatErrorInstance(); return nil; } } else { // First handle Hanzi mode which does not start with character count if ([mode isEqual:[ZXQRCodeMode hanziMode]]) { //chinese mode contains a sub set indicator right after mode indicator int subset = [bits readBits:4]; int countHanzi = [bits readBits:[mode characterCountBits:version]]; if (subset == ZX_GB2312_SUBSET) { if (![self decodeHanziSegment:bits result:result count:countHanzi]) { if (error) *error = ZXFormatErrorInstance(); return nil; } } } else { // "Normal" QR code modes: // How many characters will follow, encoded in this mode? int count = [bits readBits:[mode characterCountBits:version]]; if ([mode isEqual:[ZXQRCodeMode numericMode]]) { if (![self decodeNumericSegment:bits result:result count:count]) { if (error) *error = ZXFormatErrorInstance(); return nil; } } else if ([mode isEqual:[ZXQRCodeMode alphanumericMode]]) { if (![self decodeAlphanumericSegment:bits result:result count:count fc1InEffect:fc1InEffect]) { if (error) *error = ZXFormatErrorInstance(); return nil; } } else if ([mode isEqual:[ZXQRCodeMode byteMode]]) { if (![self decodeByteSegment:bits result:result count:count currentCharacterSetECI:currentCharacterSetECI byteSegments:byteSegments hints:hints]) { if (error) *error = ZXFormatErrorInstance(); return nil; } } else if ([mode isEqual:[ZXQRCodeMode kanjiMode]]) { if (![self decodeKanjiSegment:bits result:result count:count]) { if (error) *error = ZXFormatErrorInstance(); return nil; } } else { if (error) *error = ZXFormatErrorInstance(); return nil; } } } } } while (![mode isEqual:[ZXQRCodeMode terminatorMode]]); return [[ZXDecoderResult alloc] initWithRawBytes:bytes text:result.description byteSegments:byteSegments.count == 0 ? nil : byteSegments ecLevel:ecLevel == nil ? nil : ecLevel.description saSequence:symbolSequence saParity:parityData]; } /** * See specification GBT 18284-2000 */ + (BOOL)decodeHanziSegment:(ZXBitSource *)bits result:(NSMutableString *)result count:(int)count { if (count * 13 > bits.available) { return NO; } NSMutableData *buffer = [NSMutableData dataWithCapacity:2 * count]; while (count > 0) { int twoBytes = [bits readBits:13]; int assembledTwoBytes = ((twoBytes / 0x060) << 8) | (twoBytes % 0x060); if (assembledTwoBytes < 0x003BF) { assembledTwoBytes += 0x0A1A1; } else { assembledTwoBytes += 0x0A6A1; } int8_t bytes[2]; bytes[0] = (int8_t)((assembledTwoBytes >> 8) & 0xFF); bytes[1] = (int8_t)(assembledTwoBytes & 0xFF); [buffer appendBytes:bytes length:2]; count--; } NSString *string = [[NSString alloc] initWithData:buffer encoding:CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingGB_18030_2000)]; if (string) { [result appendString:string]; } return YES; } + (BOOL)decodeKanjiSegment:(ZXBitSource *)bits result:(NSMutableString *)result count:(int)count { if (count * 13 > bits.available) { return NO; } NSMutableData *buffer = [NSMutableData dataWithCapacity:2 * count]; while (count > 0) { int twoBytes = [bits readBits:13]; int assembledTwoBytes = ((twoBytes / 0x0C0) << 8) | (twoBytes % 0x0C0); if (assembledTwoBytes < 0x01F00) { assembledTwoBytes += 0x08140; } else { assembledTwoBytes += 0x0C140; } int8_t bytes[2]; bytes[0] = (int8_t)(assembledTwoBytes >> 8); bytes[1] = (int8_t)assembledTwoBytes; [buffer appendBytes:bytes length:2]; count--; } NSString *string = [[NSString alloc] initWithData:buffer encoding:NSShiftJISStringEncoding]; if (string) { [result appendString:string]; } return YES; } + (BOOL)decodeByteSegment:(ZXBitSource *)bits result:(NSMutableString *)result count:(int)count currentCharacterSetECI:(ZXCharacterSetECI *)currentCharacterSetECI byteSegments:(NSMutableArray *)byteSegments hints:(ZXDecodeHints *)hints { if (8 * count > bits.available) { return NO; } ZXByteArray *readBytes = [[ZXByteArray alloc] initWithLength:count]; for (int i = 0; i < count; i++) { readBytes.array[i] = (int8_t)[bits readBits:8]; } NSStringEncoding encoding; if (currentCharacterSetECI == nil) { encoding = [ZXStringUtils guessEncoding:readBytes hints:hints]; } else { encoding = [currentCharacterSetECI encoding]; } NSString *string = [[NSString alloc] initWithBytes:readBytes.array length:readBytes.length encoding:encoding]; if (string) { [result appendString:string]; } [byteSegments addObject:readBytes]; return YES; } + (unichar)toAlphaNumericChar:(int)value { if (value >= 45) { return -1; } return ZX_ALPHANUMERIC_CHARS[value]; } + (BOOL)decodeAlphanumericSegment:(ZXBitSource *)bits result:(NSMutableString *)result count:(int)count fc1InEffect:(BOOL)fc1InEffect { int start = (int)result.length; while (count > 1) { if ([bits available] < 11) { return NO; } int nextTwoCharsBits = [bits readBits:11]; unichar next1 = [self toAlphaNumericChar:nextTwoCharsBits / 45]; unichar next2 = [self toAlphaNumericChar:nextTwoCharsBits % 45]; [result appendFormat:@"%C%C", next1, next2]; count -= 2; } if (count == 1) { if ([bits available] < 6) { return NO; } unichar next1 = [self toAlphaNumericChar:[bits readBits:6]]; [result appendFormat:@"%C", next1]; } if (fc1InEffect) { for (int i = start; i < [result length]; i++) { if ([result characterAtIndex:i] == '%') { if (i < [result length] - 1 && [result characterAtIndex:i + 1] == '%') { [result deleteCharactersInRange:NSMakeRange(i + 1, 1)]; } else { [result insertString:[NSString stringWithFormat:@"%C", (unichar)0x1D] atIndex:i]; } } } } return YES; } + (BOOL)decodeNumericSegment:(ZXBitSource *)bits result:(NSMutableString *)result count:(int)count { // Read three digits at a time while (count >= 3) { // Each 10 bits encodes three digits if (bits.available < 10) { return NO; } int threeDigitsBits = [bits readBits:10]; if (threeDigitsBits >= 1000) { return NO; } unichar next1 = [self toAlphaNumericChar:threeDigitsBits / 100]; unichar next2 = [self toAlphaNumericChar:(threeDigitsBits / 10) % 10]; unichar next3 = [self toAlphaNumericChar:threeDigitsBits % 10]; [result appendFormat:@"%C%C%C", next1, next2, next3]; count -= 3; } if (count == 2) { // Two digits left over to read, encoded in 7 bits if (bits.available < 7) { return NO; } int twoDigitsBits = [bits readBits:7]; if (twoDigitsBits >= 100) { return NO; } unichar next1 = [self toAlphaNumericChar:twoDigitsBits / 10]; unichar next2 = [self toAlphaNumericChar:twoDigitsBits % 10]; [result appendFormat:@"%C%C", next1, next2]; } else if (count == 1) { // One digit left over to read if (bits.available < 4) { return NO; } int digitBits = [bits readBits:4]; if (digitBits >= 10) { return NO; } unichar next1 = [self toAlphaNumericChar:digitBits]; [result appendFormat:@"%C", next1]; } return YES; } + (int)parseECIValue:(ZXBitSource *)bits { int firstByte = [bits readBits:8]; if ((firstByte & 0x80) == 0) { return firstByte & 0x7F; } if ((firstByte & 0xC0) == 0x80) { int secondByte = [bits readBits:8]; return ((firstByte & 0x3F) << 8) | secondByte; } if ((firstByte & 0xE0) == 0xC0) { int secondThirdBytes = [bits readBits:16]; return ((firstByte & 0x1F) << 16) | secondThirdBytes; } return -1; } @end