/* * 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 "ZXIntArray.h" #import "ZXUPCEReader.h" // For an UPC-E barcode, the final digit is represented by the parities used // to encode the middle six digits, according to the table below. // // Parity of next 6 digits // Digit 0 1 2 3 4 5 // 0 Even Even Even Odd Odd Odd // 1 Even Even Odd Even Odd Odd // 2 Even Even Odd Odd Even Odd // 3 Even Even Odd Odd Odd Even // 4 Even Odd Even Even Odd Odd // 5 Even Odd Odd Even Even Odd // 6 Even Odd Odd Odd Even Even // 7 Even Odd Even Odd Even Odd // 8 Even Odd Even Odd Odd Even // 9 Even Odd Odd Even Odd Even // // The encoding is represented by the following array, which is a bit pattern // using Odd = 0 and Even = 1. For example, 5 is represented by: // // Odd Even Even Odd Odd Even // in binary: // 0 1 1 0 0 1 == 0x19 // const int CHECK_DIGIT_ENCODINGS[] = { 0x38, 0x34, 0x32, 0x31, 0x2C, 0x26, 0x23, 0x2A, 0x29, 0x25 }; const int ZX_UPCE_MIDDLE_END_PATTERN_LEN = 6; /** * The pattern that marks the middle, and end, of a UPC-E pattern. * There is no "second half" to a UPC-E barcode. */ const int ZX_UPCE_MIDDLE_END_PATTERN[] = {1, 1, 1, 1, 1, 1}; /** * See ZX_UCPE_L_AND_G_PATTERNS; these values similarly represent patterns of * even-odd parity encodings of digits that imply both the number system (0 or 1) * used, and the check digit. */ const int ZX_UCPE_NUMSYS_AND_CHECK_DIGIT_PATTERNS[][10] = { {0x38, 0x34, 0x32, 0x31, 0x2C, 0x26, 0x23, 0x2A, 0x29, 0x25}, {0x07, 0x0B, 0x0D, 0x0E, 0x13, 0x19, 0x1C, 0x15, 0x16, 0x1A} }; @interface ZXUPCEReader () @property (nonatomic, strong, readonly) ZXIntArray *decodeMiddleCounters; @end @implementation ZXUPCEReader - (id)init { if (self = [super init]) { _decodeMiddleCounters = [[ZXIntArray alloc] initWithLength:4]; } return self; } - (int)decodeMiddle:(ZXBitArray *)row startRange:(NSRange)startRange result:(NSMutableString *)result error:(NSError **)error { ZXIntArray *counters = self.decodeMiddleCounters; [counters clear]; int end = [row size]; int rowOffset = (int)NSMaxRange(startRange); int lgPatternFound = 0; for (int x = 0; x < 6 && rowOffset < end; x++) { int bestMatch = [ZXUPCEANReader decodeDigit:row counters:counters rowOffset:rowOffset patternType:ZX_UPC_EAN_PATTERNS_L_AND_G_PATTERNS error:error]; if (bestMatch == -1) { return -1; } [result appendFormat:@"%C", (unichar)('0' + bestMatch % 10)]; rowOffset += [counters sum]; if (bestMatch >= 10) { lgPatternFound |= 1 << (5 - x); } } if (![self determineNumSysAndCheckDigit:result lgPatternFound:lgPatternFound]) { if (error) *error = ZXNotFoundErrorInstance(); return -1; } return rowOffset; } - (NSRange)decodeEnd:(ZXBitArray *)row endStart:(int)endStart error:(NSError **)error { return [ZXUPCEANReader findGuardPattern:row rowOffset:endStart whiteFirst:YES pattern:ZX_UPCE_MIDDLE_END_PATTERN patternLen:sizeof(ZX_UPCE_MIDDLE_END_PATTERN) / sizeof(int) error:error]; } - (BOOL)checkChecksum:(NSString *)s error:(NSError **)error { return [super checkChecksum:[ZXUPCEReader convertUPCEtoUPCA:s] error:error]; } - (BOOL)determineNumSysAndCheckDigit:(NSMutableString *)resultString lgPatternFound:(int)lgPatternFound { for (int numSys = 0; numSys <= 1; numSys++) { for (int d = 0; d < 10; d++) { if (lgPatternFound == ZX_UCPE_NUMSYS_AND_CHECK_DIGIT_PATTERNS[numSys][d]) { [resultString insertString:[NSString stringWithFormat:@"%C", (unichar)('0' + numSys)] atIndex:0]; [resultString appendFormat:@"%C", (unichar)('0' + d)]; return YES; } } } return NO; } - (ZXBarcodeFormat)barcodeFormat { return kBarcodeFormatUPCE; } /** * Expands a UPC-E value back into its full, equivalent UPC-A code value. * * @param upce UPC-E code as string of digits * @return equivalent UPC-A code as string of digits */ + (NSString *)convertUPCEtoUPCA:(NSString *)upce { NSString *upceChars = [upce substringWithRange:NSMakeRange(1, 6)]; NSMutableString *result = [NSMutableString stringWithCapacity:12]; [result appendFormat:@"%C", [upce characterAtIndex:0]]; unichar lastChar = [upceChars characterAtIndex:5]; switch (lastChar) { case '0': case '1': case '2': [result appendString:[upceChars substringToIndex:2]]; [result appendFormat:@"%C", lastChar]; [result appendString:@"0000"]; [result appendString:[upceChars substringWithRange:NSMakeRange(2, 3)]]; break; case '3': [result appendString:[upceChars substringToIndex:3]]; [result appendString:@"00000"]; [result appendString:[upceChars substringWithRange:NSMakeRange(3, 2)]]; break; case '4': [result appendString:[upceChars substringToIndex:4]]; [result appendString:@"00000"]; [result appendString:[upceChars substringWithRange:NSMakeRange(4, 1)]]; break; default: [result appendString:[upceChars substringToIndex:5]]; [result appendString:@"0000"]; [result appendFormat:@"%C", lastChar]; break; } [result appendFormat:@"%C", [upce characterAtIndex:7]]; return result; } @end