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