You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ZXCode93Reader.m 8.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  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 "ZXCode93Reader.h"
  18. #import "ZXErrors.h"
  19. #import "ZXIntArray.h"
  20. #import "ZXResult.h"
  21. #import "ZXResultPoint.h"
  22. NSString *ZX_CODE93_ALPHABET_STRING = nil;
  23. const unichar ZX_CODE93_ALPHABET[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D',
  24. 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
  25. 'X', 'Y', 'Z', '-', '.', ' ', '$', '/', '+', '%', 'a', 'b', 'c', 'd', '*'};
  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. */
  30. const int ZX_CODE93_CHARACTER_ENCODINGS[] = {
  31. 0x114, 0x148, 0x144, 0x142, 0x128, 0x124, 0x122, 0x150, 0x112, 0x10A, // 0-9
  32. 0x1A8, 0x1A4, 0x1A2, 0x194, 0x192, 0x18A, 0x168, 0x164, 0x162, 0x134, // A-J
  33. 0x11A, 0x158, 0x14C, 0x146, 0x12C, 0x116, 0x1B4, 0x1B2, 0x1AC, 0x1A6, // K-T
  34. 0x196, 0x19A, 0x16C, 0x166, 0x136, 0x13A, // U-Z
  35. 0x12E, 0x1D4, 0x1D2, 0x1CA, 0x16E, 0x176, 0x1AE, // - - %
  36. 0x126, 0x1DA, 0x1D6, 0x132, 0x15E, // Control chars? $-*
  37. };
  38. const int ZX_CODE93_ASTERISK_ENCODING = 0x15E;
  39. @interface ZXCode93Reader ()
  40. @property (nonatomic, strong, readonly) ZXIntArray *counters;
  41. @end
  42. @implementation ZXCode93Reader
  43. + (void)initialize {
  44. if ([self class] != [ZXCode93Reader class]) return;
  45. ZX_CODE93_ALPHABET_STRING = [[NSString alloc] initWithCharacters:ZX_CODE93_ALPHABET
  46. length:sizeof(ZX_CODE93_ALPHABET) / sizeof(unichar)];
  47. }
  48. - (id)init {
  49. if (self = [super init]) {
  50. _counters = [[ZXIntArray alloc] initWithLength:6];
  51. }
  52. return self;
  53. }
  54. - (ZXResult *)decodeRow:(int)rowNumber row:(ZXBitArray *)row hints:(ZXDecodeHints *)hints error:(NSError **)error {
  55. ZXIntArray *start = [self findAsteriskPattern:row];
  56. if (!start) {
  57. if (error) *error = ZXNotFoundErrorInstance();
  58. return nil;
  59. }
  60. // Read off white space
  61. int nextStart = [row nextSet:start.array[1]];
  62. int end = row.size;
  63. ZXIntArray *theCounters = self.counters;
  64. memset(theCounters.array, 0, theCounters.length * sizeof(int32_t));
  65. NSMutableString *result = [NSMutableString string];
  66. unichar decodedChar;
  67. int lastStart;
  68. do {
  69. if (![ZXOneDReader recordPattern:row start:nextStart counters:theCounters]) {
  70. if (error) *error = ZXNotFoundErrorInstance();
  71. return nil;
  72. }
  73. int pattern = [self toPattern:theCounters];
  74. if (pattern < 0) {
  75. if (error) *error = ZXNotFoundErrorInstance();
  76. return nil;
  77. }
  78. decodedChar = [self patternToChar:pattern];
  79. if (decodedChar == 0) {
  80. if (error) *error = ZXNotFoundErrorInstance();
  81. return nil;
  82. }
  83. [result appendFormat:@"%C", decodedChar];
  84. lastStart = nextStart;
  85. for (int i = 0; i < theCounters.length; i++) {
  86. nextStart += theCounters.array[i];
  87. }
  88. // Read off white space
  89. nextStart = [row nextSet:nextStart];
  90. } while (decodedChar != '*');
  91. [result deleteCharactersInRange:NSMakeRange([result length] - 1, 1)]; // remove asterisk
  92. int lastPatternSize = [theCounters sum];
  93. // Should be at least one more black module
  94. if (nextStart == end || ![row get:nextStart]) {
  95. if (error) *error = ZXNotFoundErrorInstance();
  96. return nil;
  97. }
  98. if ([result length] < 2) {
  99. // false positive -- need at least 2 checksum digits
  100. if (error) *error = ZXNotFoundErrorInstance();
  101. return nil;
  102. }
  103. if (![self checkChecksums:result error:error]) {
  104. return nil;
  105. }
  106. [result deleteCharactersInRange:NSMakeRange([result length] - 2, 2)];
  107. NSString *resultString = [self decodeExtended:result];
  108. if (!resultString) {
  109. if (error) *error = ZXFormatErrorInstance();
  110. return nil;
  111. }
  112. float left = (float) (start.array[1] + start.array[0]) / 2.0f;
  113. float right = lastStart + lastPatternSize / 2.0f;
  114. return [ZXResult resultWithText:resultString
  115. rawBytes:nil
  116. resultPoints:@[[[ZXResultPoint alloc] initWithX:left y:(float)rowNumber],
  117. [[ZXResultPoint alloc] initWithX:right y:(float)rowNumber]]
  118. format:kBarcodeFormatCode93];
  119. }
  120. - (ZXIntArray *)findAsteriskPattern:(ZXBitArray *)row {
  121. int width = row.size;
  122. int rowOffset = [row nextSet:0];
  123. [self.counters clear];
  124. ZXIntArray *theCounters = self.counters;
  125. int patternStart = rowOffset;
  126. BOOL isWhite = NO;
  127. int patternLength = theCounters.length;
  128. int counterPosition = 0;
  129. for (int i = rowOffset; i < width; i++) {
  130. if ([row get:i] ^ isWhite) {
  131. theCounters.array[counterPosition]++;
  132. } else {
  133. if (counterPosition == patternLength - 1) {
  134. if ([self toPattern:theCounters] == ZX_CODE93_ASTERISK_ENCODING) {
  135. return [[ZXIntArray alloc] initWithInts:patternStart, i, -1];
  136. }
  137. patternStart += theCounters.array[0] + theCounters.array[1];
  138. for (int y = 2; y < patternLength; y++) {
  139. theCounters.array[y - 2] = theCounters.array[y];
  140. }
  141. theCounters.array[patternLength - 2] = 0;
  142. theCounters.array[patternLength - 1] = 0;
  143. counterPosition--;
  144. } else {
  145. counterPosition++;
  146. }
  147. theCounters.array[counterPosition] = 1;
  148. isWhite = !isWhite;
  149. }
  150. }
  151. return nil;
  152. }
  153. - (int)toPattern:(ZXIntArray *)counters {
  154. int max = counters.length;
  155. int sum = [counters sum];
  156. int32_t *array = counters.array;
  157. int pattern = 0;
  158. for (int i = 0; i < max; i++) {
  159. int scaled = round(array[i] * 9.0f / sum);
  160. if (scaled < 1 || scaled > 4) {
  161. return -1;
  162. }
  163. if ((i & 0x01) == 0) {
  164. for (int j = 0; j < scaled; j++) {
  165. pattern = (pattern << 1) | 0x01;
  166. }
  167. } else {
  168. pattern <<= scaled;
  169. }
  170. }
  171. return pattern;
  172. }
  173. - (unichar)patternToChar:(int)pattern {
  174. for (int i = 0; i < sizeof(ZX_CODE93_CHARACTER_ENCODINGS) / sizeof(int); i++) {
  175. if (ZX_CODE93_CHARACTER_ENCODINGS[i] == pattern) {
  176. return ZX_CODE93_ALPHABET[i];
  177. }
  178. }
  179. return -1;
  180. }
  181. - (NSString *)decodeExtended:(NSMutableString *)encoded {
  182. NSUInteger length = [encoded length];
  183. NSMutableString *decoded = [NSMutableString stringWithCapacity:length];
  184. for (int i = 0; i < length; i++) {
  185. unichar c = [encoded characterAtIndex:i];
  186. if (c >= 'a' && c <= 'd') {
  187. if (i >= length - 1) {
  188. return nil;
  189. }
  190. unichar next = [encoded characterAtIndex:i + 1];
  191. unichar decodedChar = '\0';
  192. switch (c) {
  193. case 'd':
  194. if (next >= 'A' && next <= 'Z') {
  195. decodedChar = (unichar)(next + 32);
  196. } else {
  197. return nil;
  198. }
  199. break;
  200. case 'a':
  201. if (next >= 'A' && next <= 'Z') {
  202. decodedChar = (unichar)(next - 64);
  203. } else {
  204. return nil;
  205. }
  206. break;
  207. case 'b':
  208. if (next >= 'A' && next <= 'E') {
  209. decodedChar = (unichar)(next - 38);
  210. } else if (next >= 'F' && next <= 'W') {
  211. decodedChar = (unichar)(next - 11);
  212. } else {
  213. return nil;
  214. }
  215. break;
  216. case 'c':
  217. if (next >= 'A' && next <= 'O') {
  218. decodedChar = (unichar)(next - 32);
  219. } else if (next == 'Z') {
  220. decodedChar = ':';
  221. } else {
  222. return nil;
  223. }
  224. break;
  225. }
  226. [decoded appendFormat:@"%C", decodedChar];
  227. i++;
  228. } else {
  229. [decoded appendFormat:@"%C", c];
  230. }
  231. }
  232. return decoded;
  233. }
  234. - (BOOL)checkChecksums:(NSMutableString *)result error:(NSError **)error {
  235. NSUInteger length = [result length];
  236. if (![self checkOneChecksum:result checkPosition:(int)length - 2 weightMax:20 error:error]) {
  237. return NO;
  238. }
  239. return [self checkOneChecksum:result checkPosition:(int)length - 1 weightMax:15 error:error];
  240. }
  241. - (BOOL)checkOneChecksum:(NSMutableString *)result checkPosition:(int)checkPosition weightMax:(int)weightMax error:(NSError **)error {
  242. int weight = 1;
  243. int total = 0;
  244. for (int i = checkPosition - 1; i >= 0; i--) {
  245. total += weight * [ZX_CODE93_ALPHABET_STRING rangeOfString:[NSString stringWithFormat:@"%C", [result characterAtIndex:i]]].location;
  246. if (++weight > weightMax) {
  247. weight = 1;
  248. }
  249. }
  250. if ([result characterAtIndex:checkPosition] != ZX_CODE93_ALPHABET[total % 47]) {
  251. if (error) *error = ZXChecksumErrorInstance();
  252. return NO;
  253. }
  254. return YES;
  255. }
  256. @end