blob: 153911f512b4978813c607ba9cd5cee2d276eeb7 [file] [log] [blame]
/*
* 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 "ZXCharacterSetECI.h"
#import "ZXDecoderResult.h"
#import "ZXErrors.h"
#import "ZXErrorCorrectionLevel.h"
#import "ZXMode.h"
#import "ZXQRCodeDecodedBitStreamParser.h"
#import "ZXQRCodeVersion.h"
#import "ZXStringUtils.h"
/**
* See ISO 18004:2006, 6.4.4 Table 5
*/
char const 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',
' ', '$', '%', '*', '+', '-', '.', '/', ':'
};
int const GB2312_SUBSET = 1;
@implementation ZXQRCodeDecodedBitStreamParser
+ (ZXDecoderResult *)decode:(int8_t *)bytes length:(unsigned int)length version:(ZXQRCodeVersion *)version
ecLevel:(ZXErrorCorrectionLevel *)ecLevel hints:(ZXDecodeHints *)hints error:(NSError **)error {
ZXBitSource *bits = [[ZXBitSource alloc] initWithBytes:bytes length:length];
NSMutableString *result = [NSMutableString stringWithCapacity:50];
ZXCharacterSetECI *currentCharacterSetECI = nil;
BOOL fc1InEffect = NO;
NSMutableArray *byteSegments = [NSMutableArray arrayWithCapacity:1];
ZXMode *mode;
do {
if ([bits available] < 4) {
mode = [ZXMode terminatorMode];
} else {
mode = [ZXMode forBits:[bits readBits:4]];
if (!mode) {
if (error) *error = FormatErrorInstance();
return nil;
}
}
if (![mode isEqual:[ZXMode terminatorMode]]) {
if ([mode isEqual:[ZXMode fnc1FirstPositionMode]] || [mode isEqual:[ZXMode fnc1SecondPositionMode]]) {
fc1InEffect = YES;
} else if ([mode isEqual:[ZXMode structuredAppendMode]]) {
if (bits.available < 16) {
if (error) *error = FormatErrorInstance();
return nil;
}
[bits readBits:16];
} else if ([mode isEqual:[ZXMode eciMode]]) {
int value = [self parseECIValue:bits];
currentCharacterSetECI = [ZXCharacterSetECI characterSetECIByValue:value];
if (currentCharacterSetECI == nil) {
if (error) *error = FormatErrorInstance();
return nil;
}
} else {
if ([mode isEqual:[ZXMode hanziMode]]) {
int subset = [bits readBits:4];
int countHanzi = [bits readBits:[mode characterCountBits:version]];
if (subset == GB2312_SUBSET) {
if (![self decodeHanziSegment:bits result:result count:countHanzi]) {
if (error) *error = FormatErrorInstance();
return nil;
}
}
} else {
int count = [bits readBits:[mode characterCountBits:version]];
if ([mode isEqual:[ZXMode numericMode]]) {
if (![self decodeNumericSegment:bits result:result count:count]) {
if (error) *error = FormatErrorInstance();
return nil;
}
} else if ([mode isEqual:[ZXMode alphanumericMode]]) {
if (![self decodeAlphanumericSegment:bits result:result count:count fc1InEffect:fc1InEffect]) {
if (error) *error = FormatErrorInstance();
return nil;
}
} else if ([mode isEqual:[ZXMode byteMode]]) {
if (![self decodeByteSegment:bits result:result count:count currentCharacterSetECI:currentCharacterSetECI byteSegments:byteSegments hints:hints]) {
if (error) *error = FormatErrorInstance();
return nil;
}
} else if ([mode isEqual:[ZXMode kanjiMode]]) {
if (![self decodeKanjiSegment:bits result:result count:count]) {
if (error) *error = FormatErrorInstance();
return nil;
}
} else {
if (error) *error = FormatErrorInstance();
return nil;
}
}
}
}
} while (![mode isEqual:[ZXMode terminatorMode]]);
return [[ZXDecoderResult alloc] initWithRawBytes:bytes
length:length
text:result.description
byteSegments:byteSegments.count == 0 ? nil : byteSegments
ecLevel:ecLevel == nil ? nil : ecLevel.description];
}
/**
* 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;
}
char bytes[2];
bytes[0] = (char)((assembledTwoBytes >> 8) & 0xFF);
bytes[1] = (char)(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;
}
char bytes[2];
bytes[0] = (char)(assembledTwoBytes >> 8);
bytes[1] = (char)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 (count << 3 > bits.available) {
return NO;
}
int8_t readBytes[count];
NSMutableArray *readBytesArray = [NSMutableArray arrayWithCapacity:count];
for (int i = 0; i < count; i++) {
readBytes[i] = (char)[bits readBits:8];
[readBytesArray addObject:[NSNumber numberWithChar:readBytes[i]]];
}
NSStringEncoding encoding;
if (currentCharacterSetECI == nil) {
encoding = [ZXStringUtils guessEncoding:readBytes length:count hints:hints];
} else {
encoding = [currentCharacterSetECI encoding];
}
NSString *string = [[NSString alloc] initWithBytes:readBytes length:count encoding:encoding];
if (string) {
[result appendString:string];
}
[byteSegments addObject:readBytesArray];
return YES;
}
+ (unichar)toAlphaNumericChar:(int)value {
if (value >= 45) {
return -1;
}
return 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