123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367 |
- /*
- * 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 "ZXCodaBarReader.h"
- #import "ZXDecodeHints.h"
- #import "ZXErrors.h"
- #import "ZXIntArray.h"
- #import "ZXResult.h"
- #import "ZXResultPoint.h"
-
- // These values are critical for determining how permissive the decoding
- // will be. All stripe sizes must be within the window these define, as
- // compared to the average stripe size.
- 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);
-
- /**
- * These represent the encodings of characters, as patterns of wide and narrow bars. The 7 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_CODA_CHARACTER_ENCODINGS[] = {
- 0x003, 0x006, 0x009, 0x060, 0x012, 0x042, 0x021, 0x024, 0x030, 0x048, // 0-9
- 0x00c, 0x018, 0x045, 0x051, 0x054, 0x015, 0x01A, 0x029, 0x00B, 0x00E, // -$:/.+ABCD
- };
-
- // minimal number of characters that should be present (inclusing start and stop characters)
- // under normal circumstances this should be set to 3, but can be set higher
- // as a last-ditch attempt to reduce false positives.
- const int ZX_CODA_MIN_CHARACTER_LENGTH = 3;
-
- // official start and end patterns
- const unichar ZX_CODA_STARTEND_ENCODING[] = {'A', 'B', 'C', 'D'};
-
- // some codabar generator allow the codabar string to be closed by every
- // character. This will cause lots of false positives!
-
- // some industries use a checksum standard but this is not part of the original codabar standard
- // for more information see : http://www.mecsw.com/specs/codabar.html
-
- @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;
- }
- // Hack: We store the position in the alphabet table into a
- // NSMutableString, so that we can access the decoded patterns in
- // validatePattern. We'll translate to the actual characters later.
- [self.decodeRowResult appendFormat:@"%C", (unichar)charOffset];
- nextStart += 8;
- // Stop as soon as we see the end character.
- 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); // no fixed end pattern so keep on reading while data is available
-
- // Look for whitespace after pattern:
- int trailingWhitespace = self.counters.array[nextStart - 1];
- int lastPatternSize = 0;
- for (int i = -8; i < -1; i++) {
- lastPatternSize += self.counters.array[nextStart + i];
- }
-
- // We need to see whitespace equal to 50% of the last pattern size,
- // otherwise this is probably a false positive. The exception is if we are
- // at the end of the row. (I.e. the barcode barely fits.)
- if (nextStart < self.counterLength && trailingWhitespace < lastPatternSize / 2) {
- if (error) *error = ZXNotFoundErrorInstance();
- return nil;
- }
-
- if (![self validatePattern:startOffset]) {
- if (error) *error = ZXNotFoundErrorInstance();
- return nil;
- }
-
- // Translate character table offsets to actual characters.
- 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]]]];
- }
- // Ensure a valid start and end character
- 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;
- }
-
- // remove stop/start characters character and check if a long enough string is contained
- 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 {
- // First, sum up the total size of our four categories of stripe sizes;
- int sizes[4] = {0, 0, 0, 0};
- int counts[4] = {0, 0, 0, 0};
- int end = (int)self.decodeRowResult.length - 1;
-
- // We break out of this loop in the middle, in order to handle
- // inter-character spaces properly.
- 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--) {
- // Even j = bars, while odd j = spaces. Categories 2 and 3 are for
- // long stripes, while 0 and 1 are for short stripes.
- int category = (j & 1) + (pattern & 1) * 2;
- sizes[category] += self.counters.array[pos + j];
- counts[category]++;
- pattern >>= 1;
- }
- if (i >= end) {
- break;
- }
- // We ignore the inter-character space - it could be of any size.
- pos += 8;
- }
-
- // Calculate our allowable size thresholds using fixed-point math.
- float maxes[4] = {0.0f, 0.0f, 0.0f, 0.0f};
- float mins[4] = {0.0f, 0.0f, 0.0f, 0.0f};
- // Define the threshold of acceptability to be the midpoint between the
- // average small stripe and the average large stripe. No stripe lengths
- // should be on the "wrong" side of that line.
- for (int i = 0; i < 2; i++) {
- mins[i] = 0.0f; // Accept arbitrarily small "short" stripes.
- 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];
- }
-
- // Now verify that all of the stripes are within the thresholds.
- 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--) {
- // Even j = bars, while odd j = spaces. Categories 2 and 3 are for
- // long stripes, while 0 and 1 are for short stripes.
- 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;
- }
-
- /**
- * Records the size of all runs of white and black pixels, starting with white.
- * This is just like recordPattern, except it records all the counters, and
- * uses our builtin "counters" member for storage.
- */
- - (BOOL)setCountersWithRow:(ZXBitArray *)row {
- self.counterLength = 0;
- // Start from the first white bit.
- 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) { // that is, exactly one is true
- 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]]) {
- // Look for whitespace before start pattern, >= 50% of width of start pattern
- // We make an exception if the whitespace is the first element.
- 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;
- }
-
- // Assumes that counters[position] is a bar.
- - (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
|