| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356 | 
							- /*
 -  * 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 "ZXDecodeHints.h"
 - #import "ZXErrors.h"
 - #import "ZXIntArray.h"
 - #import "ZXITFReader.h"
 - #import "ZXResult.h"
 - #import "ZXResultPoint.h"
 - 
 - static float ZX_ITF_MAX_AVG_VARIANCE = 0.38f;
 - static float ZX_ITF_MAX_INDIVIDUAL_VARIANCE = 0.78f;
 - 
 - static const int ZX_ITF_W = 3; // Pixel width of a wide line
 - static const int ZX_ITF_N = 1; // Pixel width of a narrow line
 - 
 - /** Valid ITF lengths. Anything longer than the largest value is also allowed. */
 - const int ZX_ITF_DEFAULT_ALLOWED_LENGTHS[] = { 6, 8, 10, 12, 14 };
 - 
 - /**
 -  * Start/end guard pattern.
 -  *
 -  * Note: The end pattern is reversed because the row is reversed before
 -  * searching for the END_PATTERN
 -  */
 - const int ZX_ITF_ITF_START_PATTERN[] = {ZX_ITF_N, ZX_ITF_N, ZX_ITF_N, ZX_ITF_N};
 - const int ZX_ITF_END_PATTERN_REVERSED[] = {ZX_ITF_N, ZX_ITF_N, ZX_ITF_W};
 - 
 - /**
 -  * Patterns of Wide / Narrow lines to indicate each digit
 -  */
 - const int ZX_ITF_PATTERNS_LEN = 10;
 - const int ZX_ITF_PATTERNS[ZX_ITF_PATTERNS_LEN][5] = {
 -   {ZX_ITF_N, ZX_ITF_N, ZX_ITF_W, ZX_ITF_W, ZX_ITF_N}, // 0
 -   {ZX_ITF_W, ZX_ITF_N, ZX_ITF_N, ZX_ITF_N, ZX_ITF_W}, // 1
 -   {ZX_ITF_N, ZX_ITF_W, ZX_ITF_N, ZX_ITF_N, ZX_ITF_W}, // 2
 -   {ZX_ITF_W, ZX_ITF_W, ZX_ITF_N, ZX_ITF_N, ZX_ITF_N}, // 3
 -   {ZX_ITF_N, ZX_ITF_N, ZX_ITF_W, ZX_ITF_N, ZX_ITF_W}, // 4
 -   {ZX_ITF_W, ZX_ITF_N, ZX_ITF_W, ZX_ITF_N, ZX_ITF_N}, // 5
 -   {ZX_ITF_N, ZX_ITF_W, ZX_ITF_W, ZX_ITF_N, ZX_ITF_N}, // 6
 -   {ZX_ITF_N, ZX_ITF_N, ZX_ITF_N, ZX_ITF_W, ZX_ITF_W}, // 7
 -   {ZX_ITF_W, ZX_ITF_N, ZX_ITF_N, ZX_ITF_W, ZX_ITF_N}, // 8
 -   {ZX_ITF_N, ZX_ITF_W, ZX_ITF_N, ZX_ITF_W, ZX_ITF_N}  // 9
 - };
 - 
 - @interface ZXITFReader ()
 - 
 - @property (nonatomic, assign) int narrowLineWidth;
 - 
 - @end
 - 
 - @implementation ZXITFReader
 - 
 - - (id)init {
 -   if (self = [super init]) {
 -     _narrowLineWidth = -1;
 -   }
 - 
 -   return self;
 - }
 - 
 - - (ZXResult *)decodeRow:(int)rowNumber row:(ZXBitArray *)row hints:(ZXDecodeHints *)hints error:(NSError **)error {
 -   // Find out where the Middle section (payload) starts & ends
 -   ZXIntArray *startRange = [self decodeStart:row];
 -   ZXIntArray *endRange = [self decodeEnd:row];
 -   if (!startRange || !endRange) {
 -     if (error) *error = ZXNotFoundErrorInstance();
 -     return nil;
 -   }
 - 
 -   NSMutableString *resultString = [NSMutableString stringWithCapacity:20];
 -   if (![self decodeMiddle:row payloadStart:startRange.array[1] payloadEnd:endRange.array[0] resultString:resultString]) {
 -     if (error) *error = ZXNotFoundErrorInstance();
 -     return nil;
 -   }
 - 
 -   NSArray *allowedLengths = nil;
 -   if (hints != nil) {
 -     allowedLengths = hints.allowedLengths;
 -   }
 -   if (allowedLengths == nil) {
 -     NSMutableArray *temp = [NSMutableArray array];
 -     for (int i = 0; i < sizeof(ZX_ITF_DEFAULT_ALLOWED_LENGTHS) / sizeof(int); i++) {
 -       [temp addObject:@(ZX_ITF_DEFAULT_ALLOWED_LENGTHS[i])];
 -     }
 -     allowedLengths = [NSArray arrayWithArray:temp];
 -   }
 - 
 -   // To avoid false positives with 2D barcodes (and other patterns), make
 -   // an assumption that the decoded string must be a 'standard' length if it's short
 -   NSUInteger length = [resultString length];
 -   BOOL lengthOK = NO;
 -   int maxAllowedLength = 0;
 -   for (NSNumber *i in allowedLengths) {
 -     int allowedLength = [i intValue];
 -     if (length == allowedLength) {
 -       lengthOK = YES;
 -       break;
 -     }
 -     if (allowedLength > maxAllowedLength) {
 -       maxAllowedLength = allowedLength;
 -     }
 -   }
 -   if (!lengthOK && length > maxAllowedLength) {
 -     lengthOK = YES;
 -   }
 -   if (!lengthOK) {
 -     if (error) *error = ZXFormatErrorInstance();
 -     return nil;
 -   }
 - 
 -   return [ZXResult resultWithText:resultString
 -                          rawBytes:nil
 -                      resultPoints:@[[[ZXResultPoint alloc] initWithX:startRange.array[1] y:(float)rowNumber],
 -                                     [[ZXResultPoint alloc] initWithX:endRange.array[0] y:(float)rowNumber]]
 -                            format:kBarcodeFormatITF];
 - }
 - 
 - /**
 -  * @param row          row of black/white values to search
 -  * @param payloadStart offset of start pattern
 -  * @param resultString NSMutableString to append decoded chars to
 -  * @return NO if decoding could not complete successfully
 -  */
 - - (BOOL)decodeMiddle:(ZXBitArray *)row payloadStart:(int)payloadStart payloadEnd:(int)payloadEnd resultString:(NSMutableString *)resultString {
 -   // Digits are interleaved in pairs - 5 black lines for one digit, and the
 -   // 5
 -   // interleaved white lines for the second digit.
 -   // Therefore, need to scan 10 lines and then
 -   // split these into two arrays
 -   ZXIntArray *counterDigitPair = [[ZXIntArray alloc] initWithLength:10];
 -   ZXIntArray *counterBlack = [[ZXIntArray alloc] initWithLength:5];
 -   ZXIntArray *counterWhite = [[ZXIntArray alloc] initWithLength:5];
 - 
 -   while (payloadStart < payloadEnd) {
 -     // Get 10 runs of black/white.
 -     if (![ZXOneDReader recordPattern:row start:payloadStart counters:counterDigitPair]) {
 -       return NO;
 -     }
 -     // Split them into each array
 -     for (int k = 0; k < 5; k++) {
 -       int twoK = 2 * k;
 -       counterBlack.array[k] = counterDigitPair.array[twoK];
 -       counterWhite.array[k] = counterDigitPair.array[twoK + 1];
 -     }
 - 
 -     int bestMatch = [self decodeDigit:counterBlack];
 -     if (bestMatch == -1) {
 -       return NO;
 -     }
 -     [resultString appendFormat:@"%C", (unichar)('0' + bestMatch)];
 -     bestMatch = [self decodeDigit:counterWhite];
 -     if (bestMatch == -1) {
 -       return NO;
 -     }
 -     [resultString appendFormat:@"%C", (unichar)('0' + bestMatch)];
 - 
 -     for (int i = 0; i < counterDigitPair.length; i++) {
 -       payloadStart += counterDigitPair.array[i];
 -     }
 -   }
 -   return YES;
 - }
 - 
 - /**
 -  * Identify where the start of the middle / payload section starts.
 -  *
 -  * @param row row of black/white values to search
 -  * @return Array, containing index of start of 'start block' and end of
 -  *         'start block'
 -  */
 - - (ZXIntArray *)decodeStart:(ZXBitArray *)row {
 -   int endStart = [self skipWhiteSpace:row];
 -   if (endStart == -1) {
 -     return nil;
 -   }
 -   ZXIntArray *startPattern = [self findGuardPattern:row rowOffset:endStart pattern:ZX_ITF_ITF_START_PATTERN patternLen:sizeof(ZX_ITF_ITF_START_PATTERN)/sizeof(int)];
 -   if (!startPattern) {
 -     return nil;
 -   }
 - 
 -   self.narrowLineWidth = (startPattern.array[1] - startPattern.array[0]) / 4;
 - 
 -   if (![self validateQuietZone:row startPattern:startPattern.array[0]]) {
 -     return nil;
 -   }
 - 
 -   return startPattern;
 - }
 - 
 - /**
 -  * The start & end patterns must be pre/post fixed by a quiet zone. This
 -  * zone must be at least 10 times the width of a narrow line.  Scan back until
 -  * we either get to the start of the barcode or match the necessary number of
 -  * quiet zone pixels.
 -  *
 -  * Note: Its assumed the row is reversed when using this method to find
 -  * quiet zone after the end pattern.
 -  *
 -  * ref: http://www.barcode-1.net/i25code.html
 -  *
 -  * @param row bit array representing the scanned barcode.
 -  * @param startPattern index into row of the start or end pattern.
 -  * @return NO if the quiet zone cannot be found, a ReaderException is thrown.
 -  */
 - - (BOOL)validateQuietZone:(ZXBitArray *)row startPattern:(int)startPattern {
 -   int quietCount = self.narrowLineWidth * 10;
 - 
 -   // if there are not so many pixel at all let's try as many as possible
 -   quietCount = quietCount < startPattern ? quietCount : startPattern;
 - 
 -   for (int i = startPattern - 1; quietCount > 0 && i >= 0; i--) {
 -     if ([row get:i]) {
 -       break;
 -     }
 -     quietCount--;
 -   }
 -   if (quietCount != 0) {
 -     return NO;
 -   }
 -   return YES;
 - }
 - 
 - /**
 -  * Skip all whitespace until we get to the first black line.
 -  *
 -  * @param row row of black/white values to search
 -  * @return index of the first black line or -1 if no black lines are found in the row
 -  */
 - - (int)skipWhiteSpace:(ZXBitArray *)row {
 -   int width = [row size];
 -   int endStart = [row nextSet:0];
 -   if (endStart == width) {
 -     return -1;
 -   }
 -   return endStart;
 - }
 - 
 - /**
 -  * Identify where the end of the middle / payload section ends.
 -  *
 -  * @param row row of black/white values to search
 -  * @return Array, containing index of start of 'end block' and end of 'end
 -  *         block'
 -  */
 - - (ZXIntArray *)decodeEnd:(ZXBitArray *)row {
 -   [row reverse];
 - 
 -   int endStart = [self skipWhiteSpace:row];
 -   if (endStart == -1) {
 -     [row reverse];
 -     return nil;
 -   }
 -   ZXIntArray *endPattern = [self findGuardPattern:row rowOffset:endStart pattern:ZX_ITF_END_PATTERN_REVERSED patternLen:sizeof(ZX_ITF_END_PATTERN_REVERSED)/sizeof(int)];
 -   if (!endPattern) {
 -     [row reverse];
 -     return nil;
 -   }
 -   if (![self validateQuietZone:row startPattern:endPattern.array[0]]) {
 -     [row reverse];
 -     return nil;
 -   }
 -   int temp = endPattern.array[0];
 -   endPattern.array[0] = [row size] - endPattern.array[1];
 -   endPattern.array[1] = [row size] - temp;
 -   [row reverse];
 -   return endPattern;
 - }
 - 
 - /**
 -  * @param row       row of black/white values to search
 -  * @param rowOffset position to start search
 -  * @param pattern   pattern of counts of number of black and white pixels that are
 -  *                  being searched for as a pattern
 -  * @return start/end horizontal offset of guard pattern, as an array of two
 -  *         ints or nil if pattern is not found
 -  */
 - - (ZXIntArray *)findGuardPattern:(ZXBitArray *)row rowOffset:(int)rowOffset pattern:(const int[])pattern patternLen:(int)patternLen {
 -   int patternLength = patternLen;
 -   ZXIntArray *counters = [[ZXIntArray alloc] initWithLength:patternLength];
 -   int32_t *array = counters.array;
 -   int width = row.size;
 -   BOOL isWhite = NO;
 - 
 -   int counterPosition = 0;
 -   int patternStart = rowOffset;
 -   for (int x = rowOffset; x < width; x++) {
 -     if ([row get:x] ^ isWhite) {
 -       array[counterPosition]++;
 -     } else {
 -       if (counterPosition == patternLength - 1) {
 -         if ([ZXOneDReader patternMatchVariance:counters pattern:pattern maxIndividualVariance:ZX_ITF_MAX_INDIVIDUAL_VARIANCE] < ZX_ITF_MAX_AVG_VARIANCE) {
 -           return [[ZXIntArray alloc] initWithInts:patternStart, x, -1];
 -         }
 -         patternStart += array[0] + array[1];
 -         for (int y = 2; y < patternLength; y++) {
 -           array[y - 2] = array[y];
 -         }
 -         array[patternLength - 2] = 0;
 -         array[patternLength - 1] = 0;
 -         counterPosition--;
 -       } else {
 -         counterPosition++;
 -       }
 -       array[counterPosition] = 1;
 -       isWhite = !isWhite;
 -     }
 -   }
 - 
 -   return nil;
 - }
 - 
 - /**
 -  * Attempts to decode a sequence of ITF black/white lines into single
 -  * digit.
 -  *
 -  * @param counters the counts of runs of observed black/white/black/... values
 -  * @return The decoded digit or -1 if digit cannot be decoded
 -  */
 - - (int)decodeDigit:(ZXIntArray *)counters {
 -   float bestVariance = ZX_ITF_MAX_AVG_VARIANCE; // worst variance we'll accept
 -   int bestMatch = -1;
 -   int max = ZX_ITF_PATTERNS_LEN;
 -   for (int i = 0; i < max; i++) {
 -     int pattern[counters.length];
 -     for (int ind = 0; ind < counters.length; ind++){
 -       pattern[ind] = ZX_ITF_PATTERNS[i][ind];
 -     }
 -     float variance = [ZXOneDReader patternMatchVariance:counters pattern:pattern maxIndividualVariance:ZX_ITF_MAX_INDIVIDUAL_VARIANCE];
 -     if (variance < bestVariance) {
 -       bestVariance = variance;
 -       bestMatch = i;
 -     }
 -   }
 -   if (bestMatch >= 0) {
 -     return bestMatch;
 -   } else {
 -     return -1;
 -   }
 - }
 - 
 - @end
 
 
  |