123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367 |
-
-
- #import "ZXBitArray.h"
- #import "ZXCodaBarReader.h"
- #import "ZXDecodeHints.h"
- #import "ZXErrors.h"
- #import "ZXIntArray.h"
- #import "ZXResult.h"
- #import "ZXResultPoint.h"
-
-
-
-
- static float ZX_CODA_MAX_ACCEPTABLE = 2.0f;
- static float ZX_CODA_PADDING = 1.5f;
-
- const unichar ZX_CODA_ALPHABET[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
- '-', '$', ':', '/', '.', '+', 'A', 'B', 'C', 'D', 'T', 'N'};
- const int ZX_CODA_ALPHABET_LEN = sizeof(ZX_CODA_ALPHABET) / sizeof(unichar);
-
-
- const int ZX_CODA_CHARACTER_ENCODINGS[] = {
- 0x003, 0x006, 0x009, 0x060, 0x012, 0x042, 0x021, 0x024, 0x030, 0x048,
- 0x00c, 0x018, 0x045, 0x051, 0x054, 0x015, 0x01A, 0x029, 0x00B, 0x00E,
- };
-
-
-
-
- const int ZX_CODA_MIN_CHARACTER_LENGTH = 3;
-
-
- const unichar ZX_CODA_STARTEND_ENCODING[] = {'A', 'B', 'C', 'D'};
-
-
-
-
-
-
-
- @interface ZXCodaBarReader ()
-
- @property (nonatomic, strong) NSMutableString *decodeRowResult;
- @property (nonatomic, strong) ZXIntArray *counters;
- @property (nonatomic, assign) int counterLength;
-
- @end
-
- @implementation ZXCodaBarReader
-
- - (id)init {
- if (self = [super init]) {
- _decodeRowResult = [NSMutableString stringWithCapacity:20];
- _counters = [[ZXIntArray alloc] initWithLength:80];
- _counterLength = 0;
- }
-
- return self;
- }
-
- - (ZXResult *)decodeRow:(int)rowNumber row:(ZXBitArray *)row hints:(ZXDecodeHints *)hints error:(NSError **)error {
- [self.counters clear];
-
- if (![self setCountersWithRow:row]) {
- if (error) *error = ZXNotFoundErrorInstance();
- return nil;
- }
-
- int startOffset = [self findStartPattern];
- if (startOffset == -1) {
- if (error) *error = ZXNotFoundErrorInstance();
- return nil;
- }
-
- int nextStart = startOffset;
-
- self.decodeRowResult = [NSMutableString string];
- do {
- int charOffset = [self toNarrowWidePattern:nextStart];
- if (charOffset == -1) {
- if (error) *error = ZXNotFoundErrorInstance();
- return nil;
- }
-
-
-
- [self.decodeRowResult appendFormat:@"%C", (unichar)charOffset];
- nextStart += 8;
-
- if (self.decodeRowResult.length > 1 &&
- [ZXCodaBarReader arrayContains:ZX_CODA_STARTEND_ENCODING
- length:sizeof(ZX_CODA_STARTEND_ENCODING) / sizeof(unichar)
- key:ZX_CODA_ALPHABET[charOffset]]) {
- break;
- }
- } while (nextStart < self.counterLength);
-
-
- int trailingWhitespace = self.counters.array[nextStart - 1];
- int lastPatternSize = 0;
- for (int i = -8; i < -1; i++) {
- lastPatternSize += self.counters.array[nextStart + i];
- }
-
-
-
-
- if (nextStart < self.counterLength && trailingWhitespace < lastPatternSize / 2) {
- if (error) *error = ZXNotFoundErrorInstance();
- return nil;
- }
-
- if (![self validatePattern:startOffset]) {
- if (error) *error = ZXNotFoundErrorInstance();
- return nil;
- }
-
-
- for (int i = 0; i < self.decodeRowResult.length; i++) {
- [self.decodeRowResult replaceCharactersInRange:NSMakeRange(i, 1) withString:[NSString stringWithFormat:@"%c", ZX_CODA_ALPHABET[[self.decodeRowResult characterAtIndex:i]]]];
- }
-
- unichar startchar = [self.decodeRowResult characterAtIndex:0];
- if (![ZXCodaBarReader arrayContains:ZX_CODA_STARTEND_ENCODING
- length:sizeof(ZX_CODA_STARTEND_ENCODING) / sizeof(unichar)
- key:startchar]) {
- if (error) *error = ZXNotFoundErrorInstance();
- return nil;
- }
- unichar endchar = [self.decodeRowResult characterAtIndex:self.decodeRowResult.length - 1];
- if (![ZXCodaBarReader arrayContains:ZX_CODA_STARTEND_ENCODING
- length:sizeof(ZX_CODA_STARTEND_ENCODING) / sizeof(unichar)
- key:endchar]) {
- if (error) *error = ZXNotFoundErrorInstance();
- return nil;
- }
-
-
- if (self.decodeRowResult.length <= ZX_CODA_MIN_CHARACTER_LENGTH) {
- if (error) *error = ZXNotFoundErrorInstance();
- return nil;
- }
-
- if (!hints.returnCodaBarStartEnd) {
- [self.decodeRowResult deleteCharactersInRange:NSMakeRange(self.decodeRowResult.length - 1, 1)];
- [self.decodeRowResult deleteCharactersInRange:NSMakeRange(0, 1)];
- }
-
- int runningCount = 0;
- for (int i = 0; i < startOffset; i++) {
- runningCount += self.counters.array[i];
- }
- float left = (float) runningCount;
- for (int i = startOffset; i < nextStart - 1; i++) {
- runningCount += self.counters.array[i];
- }
- float right = (float) runningCount;
- return [ZXResult resultWithText:self.decodeRowResult
- rawBytes:nil
- resultPoints:@[[[ZXResultPoint alloc] initWithX:left y:(float)rowNumber],
- [[ZXResultPoint alloc] initWithX:right y:(float)rowNumber]]
- format:kBarcodeFormatCodabar];
- }
-
- - (BOOL)validatePattern:(int)start {
-
- int sizes[4] = {0, 0, 0, 0};
- int counts[4] = {0, 0, 0, 0};
- int end = (int)self.decodeRowResult.length - 1;
-
-
-
- int pos = start;
- for (int i = 0; true; i++) {
- int pattern = ZX_CODA_CHARACTER_ENCODINGS[[self.decodeRowResult characterAtIndex:i]];
- for (int j = 6; j >= 0; j--) {
-
-
- int category = (j & 1) + (pattern & 1) * 2;
- sizes[category] += self.counters.array[pos + j];
- counts[category]++;
- pattern >>= 1;
- }
- if (i >= end) {
- break;
- }
-
- pos += 8;
- }
-
-
- float maxes[4] = {0.0f, 0.0f, 0.0f, 0.0f};
- float mins[4] = {0.0f, 0.0f, 0.0f, 0.0f};
-
-
-
- for (int i = 0; i < 2; i++) {
- mins[i] = 0.0f;
- mins[i + 2] = ((float) sizes[i] / counts[i] + (float) sizes[i + 2] / counts[i + 2]) / 2.0f;
- maxes[i] = mins[i + 2];
- maxes[i + 2] = (sizes[i + 2] * ZX_CODA_MAX_ACCEPTABLE + ZX_CODA_PADDING) / counts[i + 2];
- }
-
-
- pos = start;
- for (int i = 0; true; i++) {
- int pattern = ZX_CODA_CHARACTER_ENCODINGS[[self.decodeRowResult characterAtIndex:i]];
- for (int j = 6; j >= 0; j--) {
-
-
- int category = (j & 1) + (pattern & 1) * 2;
- int size = self.counters.array[pos + j];
- if (size < mins[category] || size > maxes[category]) {
- return NO;
- }
- pattern >>= 1;
- }
- if (i >= end) {
- break;
- }
- pos += 8;
- }
-
- return YES;
- }
-
-
- - (BOOL)setCountersWithRow:(ZXBitArray *)row {
- self.counterLength = 0;
-
- int i = [row nextUnset:0];
- int end = row.size;
- if (i >= end) {
- return NO;
- }
- BOOL isWhite = YES;
- int count = 0;
- while (i < end) {
- if ([row get:i] ^ isWhite) {
- count++;
- } else {
- [self counterAppend:count];
- count = 1;
- isWhite = !isWhite;
- }
- i++;
- }
- [self counterAppend:count];
- return YES;
- }
-
- - (void)counterAppend:(int)e {
- self.counters.array[self.counterLength] = e;
- self.counterLength++;
- if (self.counterLength >= self.counters.length) {
- ZXIntArray *temp = [[ZXIntArray alloc] initWithLength:self.counterLength * 2];
- memcpy(temp.array, self.counters.array, self.counters.length * sizeof(int32_t));
- self.counters = temp;
- }
- }
-
- - (int)findStartPattern {
- for (int i = 1; i < self.counterLength; i += 2) {
- int charOffset = [self toNarrowWidePattern:i];
- if (charOffset != -1 && [[self class] arrayContains:ZX_CODA_STARTEND_ENCODING
- length:sizeof(ZX_CODA_STARTEND_ENCODING) / sizeof(unichar)
- key:ZX_CODA_ALPHABET[charOffset]]) {
-
-
- int patternSize = 0;
- for (int j = i; j < i + 7; j++) {
- patternSize += self.counters.array[j];
- }
- if (i == 1 || self.counters.array[i-1] >= patternSize / 2) {
- return i;
- }
- }
- }
-
- return -1;
- }
-
- + (BOOL)arrayContains:(const unichar *)array length:(unsigned int)length key:(unichar)key {
- if (array != nil) {
- for (int i = 0; i < length; i++) {
- if (array[i] == key) {
- return YES;
- }
- }
- }
- return NO;
- }
-
-
- - (int)toNarrowWidePattern:(int)position {
- int32_t *array = self.counters.array;
- int end = position + 7;
- if (end >= self.counterLength) {
- return -1;
- }
-
- int maxBar = 0;
- int minBar = INT_MAX;
- for (int j = position; j < end; j += 2) {
- int currentCounter = array[j];
- if (currentCounter < minBar) {
- minBar = currentCounter;
- }
- if (currentCounter > maxBar) {
- maxBar = currentCounter;
- }
- }
- int thresholdBar = (minBar + maxBar) / 2;
-
- int maxSpace = 0;
- int minSpace = INT_MAX;
- for (int j = position + 1; j < end; j += 2) {
- int currentCounter = array[j];
- if (currentCounter < minSpace) {
- minSpace = currentCounter;
- }
- if (currentCounter > maxSpace) {
- maxSpace = currentCounter;
- }
- }
- int thresholdSpace = (minSpace + maxSpace) / 2;
-
- int bitmask = 1 << 7;
- int pattern = 0;
- for (int i = 0; i < 7; i++) {
- int threshold = (i & 1) == 0 ? thresholdBar : thresholdSpace;
- bitmask >>= 1;
- if (array[position + i] > threshold) {
- pattern |= bitmask;
- }
- }
-
- for (int i = 0; i < sizeof(ZX_CODA_CHARACTER_ENCODINGS) / sizeof(int); i++) {
- if (ZX_CODA_CHARACTER_ENCODINGS[i] == pattern) {
- return i;
- }
- }
- return -1;
- }
-
- @end
|