123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310 |
- /*
- * 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 "ZXCode39Reader.h"
- #import "ZXErrors.h"
- #import "ZXIntArray.h"
- #import "ZXResult.h"
- #import "ZXResultPoint.h"
-
- unichar ZX_CODE39_ALPHABET[] = {'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', '-', '.', ' ', '*', '$', '/', '+', '%'};
- NSString *ZX_CODE39_ALPHABET_STRING = nil;
-
-
- /**
- * These represent the encodings of characters, as patterns of wide and narrow bars.
- * The 9 least-significant bits of each int correspond to the pattern of wide and narrow,
- * with 1s representing "wide" and 0s representing narrow.
- */
- const int ZX_CODE39_CHARACTER_ENCODINGS[] = {
- 0x034, 0x121, 0x061, 0x160, 0x031, 0x130, 0x070, 0x025, 0x124, 0x064, // 0-9
- 0x109, 0x049, 0x148, 0x019, 0x118, 0x058, 0x00D, 0x10C, 0x04C, 0x01C, // A-J
- 0x103, 0x043, 0x142, 0x013, 0x112, 0x052, 0x007, 0x106, 0x046, 0x016, // K-T
- 0x181, 0x0C1, 0x1C0, 0x091, 0x190, 0x0D0, 0x085, 0x184, 0x0C4, 0x094, // U-*
- 0x0A8, 0x0A2, 0x08A, 0x02A // $-%
- };
-
- const int ZX_CODE39_ASTERISK_ENCODING = 0x094;
-
- @interface ZXCode39Reader ()
-
- @property (nonatomic, assign, readonly) BOOL extendedMode;
- @property (nonatomic, assign, readonly) BOOL usingCheckDigit;
- @property (nonatomic, strong, readonly) ZXIntArray *counters;
-
- @end
-
- @implementation ZXCode39Reader
-
- + (void)load {
- ZX_CODE39_ALPHABET_STRING = [[NSString alloc] initWithCharacters:ZX_CODE39_ALPHABET
- length:sizeof(ZX_CODE39_ALPHABET) / sizeof(unichar)];
- }
-
- - (id)init {
- return [self initUsingCheckDigit:NO extendedMode:NO];
- }
-
- - (id)initUsingCheckDigit:(BOOL)isUsingCheckDigit {
- return [self initUsingCheckDigit:isUsingCheckDigit extendedMode:NO];
- }
-
- - (id)initUsingCheckDigit:(BOOL)usingCheckDigit extendedMode:(BOOL)extendedMode {
- if (self = [super init]) {
- _usingCheckDigit = usingCheckDigit;
- _extendedMode = extendedMode;
- _counters = [[ZXIntArray alloc] initWithLength:9];
- }
-
- return self;
- }
-
- - (ZXResult *)decodeRow:(int)rowNumber row:(ZXBitArray *)row hints:(ZXDecodeHints *)hints error:(NSError **)error {
- ZXIntArray *theCounters = self.counters;
- [theCounters clear];
- NSMutableString *result = [NSMutableString stringWithCapacity:20];
-
- ZXIntArray *start = [self findAsteriskPattern:row counters:theCounters];
- if (!start) {
- if (error) *error = ZXNotFoundErrorInstance();
- return nil;
- }
- // Read off white space
- int nextStart = [row nextSet:start.array[1]];
- int end = [row size];
-
- unichar decodedChar;
- int lastStart;
- do {
- if (![ZXOneDReader recordPattern:row start:nextStart counters:theCounters]) {
- if (error) *error = ZXNotFoundErrorInstance();
- return nil;
- }
- int pattern = [self toNarrowWidePattern:theCounters];
- if (pattern < 0) {
- if (error) *error = ZXNotFoundErrorInstance();
- return nil;
- }
- decodedChar = [self patternToChar:pattern];
- if (decodedChar == 0) {
- if (error) *error = ZXNotFoundErrorInstance();
- return nil;
- }
- [result appendFormat:@"%C", decodedChar];
- lastStart = nextStart;
- for (int i = 0; i < theCounters.length; i++) {
- nextStart += theCounters.array[i];
- }
- // Read off white space
- nextStart = [row nextSet:nextStart];
- } while (decodedChar != '*');
- [result deleteCharactersInRange:NSMakeRange([result length] - 1, 1)];
-
- int lastPatternSize = 0;
- for (int i = 0; i < theCounters.length; i++) {
- lastPatternSize += theCounters.array[i];
- }
- int whiteSpaceAfterEnd = nextStart - lastStart - lastPatternSize;
- if (nextStart != end && (whiteSpaceAfterEnd * 2) < lastPatternSize) {
- if (error) *error = ZXNotFoundErrorInstance();
- return nil;
- }
-
- if (self.usingCheckDigit) {
- int max = (int)[result length] - 1;
- int total = 0;
- for (int i = 0; i < max; i++) {
- total += [ZX_CODE39_ALPHABET_STRING rangeOfString:[result substringWithRange:NSMakeRange(i, 1)]].location;
- }
- if ([result characterAtIndex:max] != ZX_CODE39_ALPHABET[total % 43]) {
- if (error) *error = ZXChecksumErrorInstance();
- return nil;
- }
- [result deleteCharactersInRange:NSMakeRange(max, 1)];
- }
-
- if ([result length] == 0) {
- // false positive
- if (error) *error = ZXNotFoundErrorInstance();
- return nil;
- }
-
- NSString *resultString;
- if (self.extendedMode) {
- resultString = [self decodeExtended:result];
- if (!resultString) {
- if (error) *error = ZXFormatErrorInstance();
- return nil;
- }
- } else {
- resultString = result;
- }
-
- float left = (float) (start.array[1] + start.array[0]) / 2.0f;
- float right = (lastStart + lastPatternSize) / 2.0f;
-
- return [ZXResult resultWithText:resultString
- rawBytes:nil
- resultPoints:@[[[ZXResultPoint alloc] initWithX:left y:(float)rowNumber],
- [[ZXResultPoint alloc] initWithX:right y:(float)rowNumber]]
- format:kBarcodeFormatCode39];
- }
-
- - (ZXIntArray *)findAsteriskPattern:(ZXBitArray *)row counters:(ZXIntArray *)counters {
- int width = row.size;
- int rowOffset = [row nextSet:0];
-
- int counterPosition = 0;
- int patternStart = rowOffset;
- BOOL isWhite = NO;
- int patternLength = counters.length;
- int32_t *array = counters.array;
-
- for (int i = rowOffset; i < width; i++) {
- if ([row get:i] ^ isWhite) {
- array[counterPosition]++;
- } else {
- if (counterPosition == patternLength - 1) {
- if ([self toNarrowWidePattern:counters] == ZX_CODE39_ASTERISK_ENCODING &&
- [row isRange:MAX(0, patternStart - ((i - patternStart) / 2)) end:patternStart value:NO]) {
- return [[ZXIntArray alloc] initWithInts:patternLength, i, -1];
- }
- patternStart += array[0] + array[1];
- for (int y = 2; y < counters.length; y++) {
- array[y - 2] = array[y];
- }
- array[patternLength - 2] = 0;
- array[patternLength - 1] = 0;
- counterPosition--;
- } else {
- counterPosition++;
- }
- array[counterPosition] = 1;
- isWhite = !isWhite;
- }
- }
-
- return nil;
- }
-
- - (int)toNarrowWidePattern:(ZXIntArray *)counters {
- int numCounters = counters.length;
- int maxNarrowCounter = 0;
- int wideCounters;
- do {
- int minCounter = INT_MAX;
- int32_t *array = counters.array;
- for (int i = 0; i < numCounters; i++) {
- int counter = array[i];
- if (counter < minCounter && counter > maxNarrowCounter) {
- minCounter = counter;
- }
- }
- maxNarrowCounter = minCounter;
- wideCounters = 0;
- int totalWideCountersWidth = 0;
- int pattern = 0;
- for (int i = 0; i < numCounters; i++) {
- int counter = array[i];
- if (array[i] > maxNarrowCounter) {
- pattern |= 1 << (numCounters - 1 - i);
- wideCounters++;
- totalWideCountersWidth += counter;
- }
- }
- if (wideCounters == 3) {
- for (int i = 0; i < numCounters && wideCounters > 0; i++) {
- int counter = array[i];
- if (array[i] > maxNarrowCounter) {
- wideCounters--;
- if ((counter * 2) >= totalWideCountersWidth) {
- return -1;
- }
- }
- }
- return pattern;
- }
- } while (wideCounters > 3);
- return -1;
- }
-
- - (unichar)patternToChar:(int)pattern {
- for (int i = 0; i < sizeof(ZX_CODE39_CHARACTER_ENCODINGS) / sizeof(int); i++) {
- if (ZX_CODE39_CHARACTER_ENCODINGS[i] == pattern) {
- return ZX_CODE39_ALPHABET[i];
- }
- }
- return 0;
- }
-
- - (NSString *)decodeExtended:(NSMutableString *)encoded {
- NSUInteger length = [encoded length];
- NSMutableString *decoded = [NSMutableString stringWithCapacity:length];
-
- for (int i = 0; i < length; i++) {
- unichar c = [encoded characterAtIndex:i];
- if (c == '+' || c == '$' || c == '%' || c == '/') {
- unichar next = [encoded characterAtIndex:i + 1];
- unichar decodedChar = '\0';
-
- switch (c) {
- case '+':
- if (next >= 'A' && next <= 'Z') {
- decodedChar = (unichar)(next + 32);
- } else {
- return nil;
- }
- break;
- case '$':
- if (next >= 'A' && next <= 'Z') {
- decodedChar = (unichar)(next - 64);
- } else {
- return nil;
- }
- break;
- case '%':
- if (next >= 'A' && next <= 'E') {
- decodedChar = (unichar)(next - 38);
- } else if (next >= 'F' && next <= 'W') {
- decodedChar = (unichar)(next - 11);
- } else {
- return nil;
- }
- break;
- case '/':
- if (next >= 'A' && next <= 'O') {
- decodedChar = (unichar)(next - 32);
- } else if (next == 'Z') {
- decodedChar = ':';
- } else {
- return nil;
- }
- break;
- }
- [decoded appendFormat:@"%C", decodedChar];
- i++;
- } else {
- [decoded appendFormat:@"%C", c];
- }
- }
-
- return decoded;
- }
-
- @end
|