Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

ZXCode39Reader.m 9.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  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 "ZXCode39Reader.h"
  18. #import "ZXErrors.h"
  19. #import "ZXIntArray.h"
  20. #import "ZXResult.h"
  21. #import "ZXResultPoint.h"
  22. unichar ZX_CODE39_ALPHABET[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D',
  23. 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
  24. 'X', 'Y', 'Z', '-', '.', ' ', '*', '$', '/', '+', '%'};
  25. NSString *ZX_CODE39_ALPHABET_STRING = nil;
  26. /**
  27. * These represent the encodings of characters, as patterns of wide and narrow bars.
  28. * The 9 least-significant bits of each int correspond to the pattern of wide and narrow,
  29. * with 1s representing "wide" and 0s representing narrow.
  30. */
  31. const int ZX_CODE39_CHARACTER_ENCODINGS[] = {
  32. 0x034, 0x121, 0x061, 0x160, 0x031, 0x130, 0x070, 0x025, 0x124, 0x064, // 0-9
  33. 0x109, 0x049, 0x148, 0x019, 0x118, 0x058, 0x00D, 0x10C, 0x04C, 0x01C, // A-J
  34. 0x103, 0x043, 0x142, 0x013, 0x112, 0x052, 0x007, 0x106, 0x046, 0x016, // K-T
  35. 0x181, 0x0C1, 0x1C0, 0x091, 0x190, 0x0D0, 0x085, 0x184, 0x0C4, 0x094, // U-*
  36. 0x0A8, 0x0A2, 0x08A, 0x02A // $-%
  37. };
  38. const int ZX_CODE39_ASTERISK_ENCODING = 0x094;
  39. @interface ZXCode39Reader ()
  40. @property (nonatomic, assign, readonly) BOOL extendedMode;
  41. @property (nonatomic, assign, readonly) BOOL usingCheckDigit;
  42. @property (nonatomic, strong, readonly) ZXIntArray *counters;
  43. @end
  44. @implementation ZXCode39Reader
  45. + (void)load {
  46. ZX_CODE39_ALPHABET_STRING = [[NSString alloc] initWithCharacters:ZX_CODE39_ALPHABET
  47. length:sizeof(ZX_CODE39_ALPHABET) / sizeof(unichar)];
  48. }
  49. - (id)init {
  50. return [self initUsingCheckDigit:NO extendedMode:NO];
  51. }
  52. - (id)initUsingCheckDigit:(BOOL)isUsingCheckDigit {
  53. return [self initUsingCheckDigit:isUsingCheckDigit extendedMode:NO];
  54. }
  55. - (id)initUsingCheckDigit:(BOOL)usingCheckDigit extendedMode:(BOOL)extendedMode {
  56. if (self = [super init]) {
  57. _usingCheckDigit = usingCheckDigit;
  58. _extendedMode = extendedMode;
  59. _counters = [[ZXIntArray alloc] initWithLength:9];
  60. }
  61. return self;
  62. }
  63. - (ZXResult *)decodeRow:(int)rowNumber row:(ZXBitArray *)row hints:(ZXDecodeHints *)hints error:(NSError **)error {
  64. ZXIntArray *theCounters = self.counters;
  65. [theCounters clear];
  66. NSMutableString *result = [NSMutableString stringWithCapacity:20];
  67. ZXIntArray *start = [self findAsteriskPattern:row counters:theCounters];
  68. if (!start) {
  69. if (error) *error = ZXNotFoundErrorInstance();
  70. return nil;
  71. }
  72. // Read off white space
  73. int nextStart = [row nextSet:start.array[1]];
  74. int end = [row size];
  75. unichar decodedChar;
  76. int lastStart;
  77. do {
  78. if (![ZXOneDReader recordPattern:row start:nextStart counters:theCounters]) {
  79. if (error) *error = ZXNotFoundErrorInstance();
  80. return nil;
  81. }
  82. int pattern = [self toNarrowWidePattern:theCounters];
  83. if (pattern < 0) {
  84. if (error) *error = ZXNotFoundErrorInstance();
  85. return nil;
  86. }
  87. decodedChar = [self patternToChar:pattern];
  88. if (decodedChar == 0) {
  89. if (error) *error = ZXNotFoundErrorInstance();
  90. return nil;
  91. }
  92. [result appendFormat:@"%C", decodedChar];
  93. lastStart = nextStart;
  94. for (int i = 0; i < theCounters.length; i++) {
  95. nextStart += theCounters.array[i];
  96. }
  97. // Read off white space
  98. nextStart = [row nextSet:nextStart];
  99. } while (decodedChar != '*');
  100. [result deleteCharactersInRange:NSMakeRange([result length] - 1, 1)];
  101. int lastPatternSize = 0;
  102. for (int i = 0; i < theCounters.length; i++) {
  103. lastPatternSize += theCounters.array[i];
  104. }
  105. int whiteSpaceAfterEnd = nextStart - lastStart - lastPatternSize;
  106. if (nextStart != end && (whiteSpaceAfterEnd * 2) < lastPatternSize) {
  107. if (error) *error = ZXNotFoundErrorInstance();
  108. return nil;
  109. }
  110. if (self.usingCheckDigit) {
  111. int max = (int)[result length] - 1;
  112. int total = 0;
  113. for (int i = 0; i < max; i++) {
  114. total += [ZX_CODE39_ALPHABET_STRING rangeOfString:[result substringWithRange:NSMakeRange(i, 1)]].location;
  115. }
  116. if ([result characterAtIndex:max] != ZX_CODE39_ALPHABET[total % 43]) {
  117. if (error) *error = ZXChecksumErrorInstance();
  118. return nil;
  119. }
  120. [result deleteCharactersInRange:NSMakeRange(max, 1)];
  121. }
  122. if ([result length] == 0) {
  123. // false positive
  124. if (error) *error = ZXNotFoundErrorInstance();
  125. return nil;
  126. }
  127. NSString *resultString;
  128. if (self.extendedMode) {
  129. resultString = [self decodeExtended:result];
  130. if (!resultString) {
  131. if (error) *error = ZXFormatErrorInstance();
  132. return nil;
  133. }
  134. } else {
  135. resultString = result;
  136. }
  137. float left = (float) (start.array[1] + start.array[0]) / 2.0f;
  138. float right = (lastStart + lastPatternSize) / 2.0f;
  139. return [ZXResult resultWithText:resultString
  140. rawBytes:nil
  141. resultPoints:@[[[ZXResultPoint alloc] initWithX:left y:(float)rowNumber],
  142. [[ZXResultPoint alloc] initWithX:right y:(float)rowNumber]]
  143. format:kBarcodeFormatCode39];
  144. }
  145. - (ZXIntArray *)findAsteriskPattern:(ZXBitArray *)row counters:(ZXIntArray *)counters {
  146. int width = row.size;
  147. int rowOffset = [row nextSet:0];
  148. int counterPosition = 0;
  149. int patternStart = rowOffset;
  150. BOOL isWhite = NO;
  151. int patternLength = counters.length;
  152. int32_t *array = counters.array;
  153. for (int i = rowOffset; i < width; i++) {
  154. if ([row get:i] ^ isWhite) {
  155. array[counterPosition]++;
  156. } else {
  157. if (counterPosition == patternLength - 1) {
  158. if ([self toNarrowWidePattern:counters] == ZX_CODE39_ASTERISK_ENCODING &&
  159. [row isRange:MAX(0, patternStart - ((i - patternStart) / 2)) end:patternStart value:NO]) {
  160. return [[ZXIntArray alloc] initWithInts:patternLength, i, -1];
  161. }
  162. patternStart += array[0] + array[1];
  163. for (int y = 2; y < counters.length; y++) {
  164. array[y - 2] = array[y];
  165. }
  166. array[patternLength - 2] = 0;
  167. array[patternLength - 1] = 0;
  168. counterPosition--;
  169. } else {
  170. counterPosition++;
  171. }
  172. array[counterPosition] = 1;
  173. isWhite = !isWhite;
  174. }
  175. }
  176. return nil;
  177. }
  178. - (int)toNarrowWidePattern:(ZXIntArray *)counters {
  179. int numCounters = counters.length;
  180. int maxNarrowCounter = 0;
  181. int wideCounters;
  182. do {
  183. int minCounter = INT_MAX;
  184. int32_t *array = counters.array;
  185. for (int i = 0; i < numCounters; i++) {
  186. int counter = array[i];
  187. if (counter < minCounter && counter > maxNarrowCounter) {
  188. minCounter = counter;
  189. }
  190. }
  191. maxNarrowCounter = minCounter;
  192. wideCounters = 0;
  193. int totalWideCountersWidth = 0;
  194. int pattern = 0;
  195. for (int i = 0; i < numCounters; i++) {
  196. int counter = array[i];
  197. if (array[i] > maxNarrowCounter) {
  198. pattern |= 1 << (numCounters - 1 - i);
  199. wideCounters++;
  200. totalWideCountersWidth += counter;
  201. }
  202. }
  203. if (wideCounters == 3) {
  204. for (int i = 0; i < numCounters && wideCounters > 0; i++) {
  205. int counter = array[i];
  206. if (array[i] > maxNarrowCounter) {
  207. wideCounters--;
  208. if ((counter * 2) >= totalWideCountersWidth) {
  209. return -1;
  210. }
  211. }
  212. }
  213. return pattern;
  214. }
  215. } while (wideCounters > 3);
  216. return -1;
  217. }
  218. - (unichar)patternToChar:(int)pattern {
  219. for (int i = 0; i < sizeof(ZX_CODE39_CHARACTER_ENCODINGS) / sizeof(int); i++) {
  220. if (ZX_CODE39_CHARACTER_ENCODINGS[i] == pattern) {
  221. return ZX_CODE39_ALPHABET[i];
  222. }
  223. }
  224. return 0;
  225. }
  226. - (NSString *)decodeExtended:(NSMutableString *)encoded {
  227. NSUInteger length = [encoded length];
  228. NSMutableString *decoded = [NSMutableString stringWithCapacity:length];
  229. for (int i = 0; i < length; i++) {
  230. unichar c = [encoded characterAtIndex:i];
  231. if (c == '+' || c == '$' || c == '%' || c == '/') {
  232. unichar next = [encoded characterAtIndex:i + 1];
  233. unichar decodedChar = '\0';
  234. switch (c) {
  235. case '+':
  236. if (next >= 'A' && next <= 'Z') {
  237. decodedChar = (unichar)(next + 32);
  238. } else {
  239. return nil;
  240. }
  241. break;
  242. case '$':
  243. if (next >= 'A' && next <= 'Z') {
  244. decodedChar = (unichar)(next - 64);
  245. } else {
  246. return nil;
  247. }
  248. break;
  249. case '%':
  250. if (next >= 'A' && next <= 'E') {
  251. decodedChar = (unichar)(next - 38);
  252. } else if (next >= 'F' && next <= 'W') {
  253. decodedChar = (unichar)(next - 11);
  254. } else {
  255. return nil;
  256. }
  257. break;
  258. case '/':
  259. if (next >= 'A' && next <= 'O') {
  260. decodedChar = (unichar)(next - 32);
  261. } else if (next == 'Z') {
  262. decodedChar = ':';
  263. } else {
  264. return nil;
  265. }
  266. break;
  267. }
  268. [decoded appendFormat:@"%C", decodedChar];
  269. i++;
  270. } else {
  271. [decoded appendFormat:@"%C", c];
  272. }
  273. }
  274. return decoded;
  275. }
  276. @end