Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

ZXITFReader.m 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356
  1. /*
  2. * Copyright 2012 ZXing authors
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #import "ZXBitArray.h"
  17. #import "ZXDecodeHints.h"
  18. #import "ZXErrors.h"
  19. #import "ZXIntArray.h"
  20. #import "ZXITFReader.h"
  21. #import "ZXResult.h"
  22. #import "ZXResultPoint.h"
  23. static float ZX_ITF_MAX_AVG_VARIANCE = 0.38f;
  24. static float ZX_ITF_MAX_INDIVIDUAL_VARIANCE = 0.78f;
  25. static const int ZX_ITF_W = 3; // Pixel width of a wide line
  26. static const int ZX_ITF_N = 1; // Pixel width of a narrow line
  27. /** Valid ITF lengths. Anything longer than the largest value is also allowed. */
  28. const int ZX_ITF_DEFAULT_ALLOWED_LENGTHS[] = { 6, 8, 10, 12, 14 };
  29. /**
  30. * Start/end guard pattern.
  31. *
  32. * Note: The end pattern is reversed because the row is reversed before
  33. * searching for the END_PATTERN
  34. */
  35. const int ZX_ITF_ITF_START_PATTERN[] = {ZX_ITF_N, ZX_ITF_N, ZX_ITF_N, ZX_ITF_N};
  36. const int ZX_ITF_END_PATTERN_REVERSED[] = {ZX_ITF_N, ZX_ITF_N, ZX_ITF_W};
  37. /**
  38. * Patterns of Wide / Narrow lines to indicate each digit
  39. */
  40. const int ZX_ITF_PATTERNS_LEN = 10;
  41. const int ZX_ITF_PATTERNS[ZX_ITF_PATTERNS_LEN][5] = {
  42. {ZX_ITF_N, ZX_ITF_N, ZX_ITF_W, ZX_ITF_W, ZX_ITF_N}, // 0
  43. {ZX_ITF_W, ZX_ITF_N, ZX_ITF_N, ZX_ITF_N, ZX_ITF_W}, // 1
  44. {ZX_ITF_N, ZX_ITF_W, ZX_ITF_N, ZX_ITF_N, ZX_ITF_W}, // 2
  45. {ZX_ITF_W, ZX_ITF_W, ZX_ITF_N, ZX_ITF_N, ZX_ITF_N}, // 3
  46. {ZX_ITF_N, ZX_ITF_N, ZX_ITF_W, ZX_ITF_N, ZX_ITF_W}, // 4
  47. {ZX_ITF_W, ZX_ITF_N, ZX_ITF_W, ZX_ITF_N, ZX_ITF_N}, // 5
  48. {ZX_ITF_N, ZX_ITF_W, ZX_ITF_W, ZX_ITF_N, ZX_ITF_N}, // 6
  49. {ZX_ITF_N, ZX_ITF_N, ZX_ITF_N, ZX_ITF_W, ZX_ITF_W}, // 7
  50. {ZX_ITF_W, ZX_ITF_N, ZX_ITF_N, ZX_ITF_W, ZX_ITF_N}, // 8
  51. {ZX_ITF_N, ZX_ITF_W, ZX_ITF_N, ZX_ITF_W, ZX_ITF_N} // 9
  52. };
  53. @interface ZXITFReader ()
  54. @property (nonatomic, assign) int narrowLineWidth;
  55. @end
  56. @implementation ZXITFReader
  57. - (id)init {
  58. if (self = [super init]) {
  59. _narrowLineWidth = -1;
  60. }
  61. return self;
  62. }
  63. - (ZXResult *)decodeRow:(int)rowNumber row:(ZXBitArray *)row hints:(ZXDecodeHints *)hints error:(NSError **)error {
  64. // Find out where the Middle section (payload) starts & ends
  65. ZXIntArray *startRange = [self decodeStart:row];
  66. ZXIntArray *endRange = [self decodeEnd:row];
  67. if (!startRange || !endRange) {
  68. if (error) *error = ZXNotFoundErrorInstance();
  69. return nil;
  70. }
  71. NSMutableString *resultString = [NSMutableString stringWithCapacity:20];
  72. if (![self decodeMiddle:row payloadStart:startRange.array[1] payloadEnd:endRange.array[0] resultString:resultString]) {
  73. if (error) *error = ZXNotFoundErrorInstance();
  74. return nil;
  75. }
  76. NSArray *allowedLengths = nil;
  77. if (hints != nil) {
  78. allowedLengths = hints.allowedLengths;
  79. }
  80. if (allowedLengths == nil) {
  81. NSMutableArray *temp = [NSMutableArray array];
  82. for (int i = 0; i < sizeof(ZX_ITF_DEFAULT_ALLOWED_LENGTHS) / sizeof(int); i++) {
  83. [temp addObject:@(ZX_ITF_DEFAULT_ALLOWED_LENGTHS[i])];
  84. }
  85. allowedLengths = [NSArray arrayWithArray:temp];
  86. }
  87. // To avoid false positives with 2D barcodes (and other patterns), make
  88. // an assumption that the decoded string must be a 'standard' length if it's short
  89. NSUInteger length = [resultString length];
  90. BOOL lengthOK = NO;
  91. int maxAllowedLength = 0;
  92. for (NSNumber *i in allowedLengths) {
  93. int allowedLength = [i intValue];
  94. if (length == allowedLength) {
  95. lengthOK = YES;
  96. break;
  97. }
  98. if (allowedLength > maxAllowedLength) {
  99. maxAllowedLength = allowedLength;
  100. }
  101. }
  102. if (!lengthOK && length > maxAllowedLength) {
  103. lengthOK = YES;
  104. }
  105. if (!lengthOK) {
  106. if (error) *error = ZXFormatErrorInstance();
  107. return nil;
  108. }
  109. return [ZXResult resultWithText:resultString
  110. rawBytes:nil
  111. resultPoints:@[[[ZXResultPoint alloc] initWithX:startRange.array[1] y:(float)rowNumber],
  112. [[ZXResultPoint alloc] initWithX:endRange.array[0] y:(float)rowNumber]]
  113. format:kBarcodeFormatITF];
  114. }
  115. /**
  116. * @param row row of black/white values to search
  117. * @param payloadStart offset of start pattern
  118. * @param resultString NSMutableString to append decoded chars to
  119. * @return NO if decoding could not complete successfully
  120. */
  121. - (BOOL)decodeMiddle:(ZXBitArray *)row payloadStart:(int)payloadStart payloadEnd:(int)payloadEnd resultString:(NSMutableString *)resultString {
  122. // Digits are interleaved in pairs - 5 black lines for one digit, and the
  123. // 5
  124. // interleaved white lines for the second digit.
  125. // Therefore, need to scan 10 lines and then
  126. // split these into two arrays
  127. ZXIntArray *counterDigitPair = [[ZXIntArray alloc] initWithLength:10];
  128. ZXIntArray *counterBlack = [[ZXIntArray alloc] initWithLength:5];
  129. ZXIntArray *counterWhite = [[ZXIntArray alloc] initWithLength:5];
  130. while (payloadStart < payloadEnd) {
  131. // Get 10 runs of black/white.
  132. if (![ZXOneDReader recordPattern:row start:payloadStart counters:counterDigitPair]) {
  133. return NO;
  134. }
  135. // Split them into each array
  136. for (int k = 0; k < 5; k++) {
  137. int twoK = 2 * k;
  138. counterBlack.array[k] = counterDigitPair.array[twoK];
  139. counterWhite.array[k] = counterDigitPair.array[twoK + 1];
  140. }
  141. int bestMatch = [self decodeDigit:counterBlack];
  142. if (bestMatch == -1) {
  143. return NO;
  144. }
  145. [resultString appendFormat:@"%C", (unichar)('0' + bestMatch)];
  146. bestMatch = [self decodeDigit:counterWhite];
  147. if (bestMatch == -1) {
  148. return NO;
  149. }
  150. [resultString appendFormat:@"%C", (unichar)('0' + bestMatch)];
  151. for (int i = 0; i < counterDigitPair.length; i++) {
  152. payloadStart += counterDigitPair.array[i];
  153. }
  154. }
  155. return YES;
  156. }
  157. /**
  158. * Identify where the start of the middle / payload section starts.
  159. *
  160. * @param row row of black/white values to search
  161. * @return Array, containing index of start of 'start block' and end of
  162. * 'start block'
  163. */
  164. - (ZXIntArray *)decodeStart:(ZXBitArray *)row {
  165. int endStart = [self skipWhiteSpace:row];
  166. if (endStart == -1) {
  167. return nil;
  168. }
  169. ZXIntArray *startPattern = [self findGuardPattern:row rowOffset:endStart pattern:ZX_ITF_ITF_START_PATTERN patternLen:sizeof(ZX_ITF_ITF_START_PATTERN)/sizeof(int)];
  170. if (!startPattern) {
  171. return nil;
  172. }
  173. self.narrowLineWidth = (startPattern.array[1] - startPattern.array[0]) / 4;
  174. if (![self validateQuietZone:row startPattern:startPattern.array[0]]) {
  175. return nil;
  176. }
  177. return startPattern;
  178. }
  179. /**
  180. * The start & end patterns must be pre/post fixed by a quiet zone. This
  181. * zone must be at least 10 times the width of a narrow line. Scan back until
  182. * we either get to the start of the barcode or match the necessary number of
  183. * quiet zone pixels.
  184. *
  185. * Note: Its assumed the row is reversed when using this method to find
  186. * quiet zone after the end pattern.
  187. *
  188. * ref: http://www.barcode-1.net/i25code.html
  189. *
  190. * @param row bit array representing the scanned barcode.
  191. * @param startPattern index into row of the start or end pattern.
  192. * @return NO if the quiet zone cannot be found, a ReaderException is thrown.
  193. */
  194. - (BOOL)validateQuietZone:(ZXBitArray *)row startPattern:(int)startPattern {
  195. int quietCount = self.narrowLineWidth * 10;
  196. // if there are not so many pixel at all let's try as many as possible
  197. quietCount = quietCount < startPattern ? quietCount : startPattern;
  198. for (int i = startPattern - 1; quietCount > 0 && i >= 0; i--) {
  199. if ([row get:i]) {
  200. break;
  201. }
  202. quietCount--;
  203. }
  204. if (quietCount != 0) {
  205. return NO;
  206. }
  207. return YES;
  208. }
  209. /**
  210. * Skip all whitespace until we get to the first black line.
  211. *
  212. * @param row row of black/white values to search
  213. * @return index of the first black line or -1 if no black lines are found in the row
  214. */
  215. - (int)skipWhiteSpace:(ZXBitArray *)row {
  216. int width = [row size];
  217. int endStart = [row nextSet:0];
  218. if (endStart == width) {
  219. return -1;
  220. }
  221. return endStart;
  222. }
  223. /**
  224. * Identify where the end of the middle / payload section ends.
  225. *
  226. * @param row row of black/white values to search
  227. * @return Array, containing index of start of 'end block' and end of 'end
  228. * block'
  229. */
  230. - (ZXIntArray *)decodeEnd:(ZXBitArray *)row {
  231. [row reverse];
  232. int endStart = [self skipWhiteSpace:row];
  233. if (endStart == -1) {
  234. [row reverse];
  235. return nil;
  236. }
  237. ZXIntArray *endPattern = [self findGuardPattern:row rowOffset:endStart pattern:ZX_ITF_END_PATTERN_REVERSED patternLen:sizeof(ZX_ITF_END_PATTERN_REVERSED)/sizeof(int)];
  238. if (!endPattern) {
  239. [row reverse];
  240. return nil;
  241. }
  242. if (![self validateQuietZone:row startPattern:endPattern.array[0]]) {
  243. [row reverse];
  244. return nil;
  245. }
  246. int temp = endPattern.array[0];
  247. endPattern.array[0] = [row size] - endPattern.array[1];
  248. endPattern.array[1] = [row size] - temp;
  249. [row reverse];
  250. return endPattern;
  251. }
  252. /**
  253. * @param row row of black/white values to search
  254. * @param rowOffset position to start search
  255. * @param pattern pattern of counts of number of black and white pixels that are
  256. * being searched for as a pattern
  257. * @return start/end horizontal offset of guard pattern, as an array of two
  258. * ints or nil if pattern is not found
  259. */
  260. - (ZXIntArray *)findGuardPattern:(ZXBitArray *)row rowOffset:(int)rowOffset pattern:(const int[])pattern patternLen:(int)patternLen {
  261. int patternLength = patternLen;
  262. ZXIntArray *counters = [[ZXIntArray alloc] initWithLength:patternLength];
  263. int32_t *array = counters.array;
  264. int width = row.size;
  265. BOOL isWhite = NO;
  266. int counterPosition = 0;
  267. int patternStart = rowOffset;
  268. for (int x = rowOffset; x < width; x++) {
  269. if ([row get:x] ^ isWhite) {
  270. array[counterPosition]++;
  271. } else {
  272. if (counterPosition == patternLength - 1) {
  273. if ([ZXOneDReader patternMatchVariance:counters pattern:pattern maxIndividualVariance:ZX_ITF_MAX_INDIVIDUAL_VARIANCE] < ZX_ITF_MAX_AVG_VARIANCE) {
  274. return [[ZXIntArray alloc] initWithInts:patternStart, x, -1];
  275. }
  276. patternStart += array[0] + array[1];
  277. for (int y = 2; y < patternLength; y++) {
  278. array[y - 2] = array[y];
  279. }
  280. array[patternLength - 2] = 0;
  281. array[patternLength - 1] = 0;
  282. counterPosition--;
  283. } else {
  284. counterPosition++;
  285. }
  286. array[counterPosition] = 1;
  287. isWhite = !isWhite;
  288. }
  289. }
  290. return nil;
  291. }
  292. /**
  293. * Attempts to decode a sequence of ITF black/white lines into single
  294. * digit.
  295. *
  296. * @param counters the counts of runs of observed black/white/black/... values
  297. * @return The decoded digit or -1 if digit cannot be decoded
  298. */
  299. - (int)decodeDigit:(ZXIntArray *)counters {
  300. float bestVariance = ZX_ITF_MAX_AVG_VARIANCE; // worst variance we'll accept
  301. int bestMatch = -1;
  302. int max = ZX_ITF_PATTERNS_LEN;
  303. for (int i = 0; i < max; i++) {
  304. int pattern[counters.length];
  305. for (int ind = 0; ind < counters.length; ind++){
  306. pattern[ind] = ZX_ITF_PATTERNS[i][ind];
  307. }
  308. float variance = [ZXOneDReader patternMatchVariance:counters pattern:pattern maxIndividualVariance:ZX_ITF_MAX_INDIVIDUAL_VARIANCE];
  309. if (variance < bestVariance) {
  310. bestVariance = variance;
  311. bestMatch = i;
  312. }
  313. }
  314. if (bestMatch >= 0) {
  315. return bestMatch;
  316. } else {
  317. return -1;
  318. }
  319. }
  320. @end