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.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440
  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 "ZXBarcodeFormat.h"
  18. #import "ZXDecodeHints.h"
  19. #import "ZXErrors.h"
  20. #import "ZXIntArray.h"
  21. #import "ZXResult.h"
  22. #import "ZXResultPointCallback.h"
  23. #import "ZXRSS14Reader.h"
  24. #import "ZXRSSFinderPattern.h"
  25. #import "ZXRSSPair.h"
  26. #import "ZXRSSUtils.h"
  27. const int ZX_RSS14_OUTSIDE_EVEN_TOTAL_SUBSET[5] = {1,10,34,70,126};
  28. const int ZX_RSS14_INSIDE_ODD_TOTAL_SUBSET[4] = {4,20,48,81};
  29. const int ZX_RSS14_OUTSIDE_GSUM[5] = {0,161,961,2015,2715};
  30. const int ZX_RSS14_INSIDE_GSUM[4] = {0,336,1036,1516};
  31. const int ZX_RSS14_OUTSIDE_ODD_WIDEST[5] = {8,6,4,3,1};
  32. const int ZX_RSS14_INSIDE_ODD_WIDEST[4] = {2,4,6,8};
  33. @interface ZXRSS14Reader ()
  34. @property (nonatomic, strong, readonly) NSMutableArray *possibleLeftPairs;
  35. @property (nonatomic, strong, readonly) NSMutableArray *possibleRightPairs;
  36. @end
  37. @implementation ZXRSS14Reader
  38. - (id)init {
  39. if (self = [super init]) {
  40. _possibleLeftPairs = [NSMutableArray array];
  41. _possibleRightPairs = [NSMutableArray array];
  42. }
  43. return self;
  44. }
  45. - (ZXResult *)decodeRow:(int)rowNumber row:(ZXBitArray *)row hints:(ZXDecodeHints *)hints error:(NSError **)error {
  46. ZXRSSPair *leftPair = [self decodePair:row right:NO rowNumber:rowNumber hints:hints];
  47. [self addOrTally:self.possibleLeftPairs pair:leftPair];
  48. [row reverse];
  49. ZXRSSPair *rightPair = [self decodePair:row right:YES rowNumber:rowNumber hints:hints];
  50. [self addOrTally:self.possibleRightPairs pair:rightPair];
  51. [row reverse];
  52. for (ZXRSSPair *left in self.possibleLeftPairs) {
  53. if ([left count] > 1) {
  54. for (ZXRSSPair *right in self.possibleRightPairs) {
  55. if ([right count] > 1) {
  56. if ([self checkChecksum:left rightPair:right]) {
  57. return [self constructResult:left rightPair:right];
  58. }
  59. }
  60. }
  61. }
  62. }
  63. if (error) *error = ZXNotFoundErrorInstance();
  64. return nil;
  65. }
  66. - (void)addOrTally:(NSMutableArray *)possiblePairs pair:(ZXRSSPair *)pair {
  67. if (pair == nil) {
  68. return;
  69. }
  70. BOOL found = NO;
  71. for (ZXRSSPair *other in possiblePairs) {
  72. if (other.value == pair.value) {
  73. [other incrementCount];
  74. found = YES;
  75. break;
  76. }
  77. }
  78. if (!found) {
  79. [possiblePairs addObject:pair];
  80. }
  81. }
  82. - (void)reset {
  83. [self.possibleLeftPairs removeAllObjects];
  84. [self.possibleRightPairs removeAllObjects];
  85. }
  86. - (ZXResult *)constructResult:(ZXRSSPair *)leftPair rightPair:(ZXRSSPair *)rightPair {
  87. long long symbolValue = 4537077LL * leftPair.value + rightPair.value;
  88. NSString *text = [@(symbolValue) stringValue];
  89. NSMutableString *buffer = [NSMutableString stringWithCapacity:14];
  90. for (int i = 13 - (int)[text length]; i > 0; i--) {
  91. [buffer appendString:@"0"];
  92. }
  93. [buffer appendString:text];
  94. int checkDigit = 0;
  95. for (int i = 0; i < 13; i++) {
  96. int digit = [buffer characterAtIndex:i] - '0';
  97. checkDigit += (i & 0x01) == 0 ? 3 * digit : digit;
  98. }
  99. checkDigit = 10 - (checkDigit % 10);
  100. if (checkDigit == 10) {
  101. checkDigit = 0;
  102. }
  103. [buffer appendFormat:@"%d", checkDigit];
  104. NSArray *leftPoints = [[leftPair finderPattern] resultPoints];
  105. NSArray *rightPoints = [[rightPair finderPattern] resultPoints];
  106. return [ZXResult resultWithText:buffer
  107. rawBytes:nil
  108. resultPoints:@[leftPoints[0], leftPoints[1], rightPoints[0], rightPoints[1]]
  109. format:kBarcodeFormatRSS14];
  110. }
  111. - (BOOL)checkChecksum:(ZXRSSPair *)leftPair rightPair:(ZXRSSPair *)rightPair {
  112. // int leftFPValue = leftPair.finderPattern.value;
  113. // int rightFPValue = rightPair.finderPattern.value;
  114. // if ((leftFPValue == 0 && rightFPValue == 8) || (leftFPValue == 8 && rightFPValue == 0)) {
  115. // }
  116. int checkValue = (leftPair.checksumPortion + 16 * rightPair.checksumPortion) % 79;
  117. int targetCheckValue = 9 * leftPair.finderPattern.value + rightPair.finderPattern.value;
  118. if (targetCheckValue > 72) {
  119. targetCheckValue--;
  120. }
  121. if (targetCheckValue > 8) {
  122. targetCheckValue--;
  123. }
  124. return checkValue == targetCheckValue;
  125. }
  126. - (ZXRSSPair *)decodePair:(ZXBitArray *)row right:(BOOL)right rowNumber:(int)rowNumber hints:(ZXDecodeHints *)hints {
  127. ZXIntArray *startEnd = [self findFinderPattern:row rowOffset:0 rightFinderPattern:right];
  128. if (!startEnd) {
  129. return nil;
  130. }
  131. ZXRSSFinderPattern *pattern = [self parseFoundFinderPattern:row rowNumber:rowNumber right:right startEnd:startEnd];
  132. if (!pattern) {
  133. return nil;
  134. }
  135. id<ZXResultPointCallback> resultPointCallback = hints == nil ? nil : hints.resultPointCallback;
  136. if (resultPointCallback != nil) {
  137. float center = (startEnd.array[0] + startEnd.array[1]) / 2.0f;
  138. if (right) {
  139. center = [row size] - 1 - center;
  140. }
  141. [resultPointCallback foundPossibleResultPoint:[[ZXResultPoint alloc] initWithX:center y:rowNumber]];
  142. }
  143. ZXRSSDataCharacter *outside = [self decodeDataCharacter:row pattern:pattern outsideChar:YES];
  144. ZXRSSDataCharacter *inside = [self decodeDataCharacter:row pattern:pattern outsideChar:NO];
  145. if (!outside || !inside) {
  146. return nil;
  147. }
  148. return [[ZXRSSPair alloc] initWithValue:1597 * outside.value + inside.value
  149. checksumPortion:outside.checksumPortion + 4 * inside.checksumPortion
  150. finderPattern:pattern];
  151. }
  152. - (ZXRSSDataCharacter *)decodeDataCharacter:(ZXBitArray *)row pattern:(ZXRSSFinderPattern *)pattern outsideChar:(BOOL)outsideChar {
  153. ZXIntArray *counters = self.dataCharacterCounters;
  154. [counters clear];
  155. int32_t *array = counters.array;
  156. if (outsideChar) {
  157. if (![ZXOneDReader recordPatternInReverse:row start:[pattern startEnd].array[0] counters:counters]) {
  158. return nil;
  159. }
  160. } else {
  161. if (![ZXOneDReader recordPattern:row start:[pattern startEnd].array[1] counters:counters]) {
  162. return nil;
  163. }
  164. for (int i = 0, j = counters.length - 1; i < j; i++, j--) {
  165. int temp = array[i];
  166. array[i] = array[j];
  167. array[j] = temp;
  168. }
  169. }
  170. int numModules = outsideChar ? 16 : 15;
  171. float elementWidth = (float)[ZXAbstractRSSReader count:counters] / (float)numModules;
  172. for (int i = 0; i < counters.length; i++) {
  173. float value = (float) array[i] / elementWidth;
  174. int count = (int)(value + 0.5f);
  175. if (count < 1) {
  176. count = 1;
  177. } else if (count > 8) {
  178. count = 8;
  179. }
  180. int offset = i / 2;
  181. if ((i & 0x01) == 0) {
  182. self.oddCounts.array[offset] = count;
  183. self.oddRoundingErrors[offset] = value - count;
  184. } else {
  185. self.evenCounts.array[offset] = count;
  186. self.evenRoundingErrors[offset] = value - count;
  187. }
  188. }
  189. if (![self adjustOddEvenCounts:outsideChar numModules:numModules]) {
  190. return nil;
  191. }
  192. int oddSum = 0;
  193. int oddChecksumPortion = 0;
  194. for (int i = self.oddCounts.length - 1; i >= 0; i--) {
  195. oddChecksumPortion *= 9;
  196. oddChecksumPortion += self.oddCounts.array[i];
  197. oddSum += self.oddCounts.array[i];
  198. }
  199. int evenChecksumPortion = 0;
  200. int evenSum = 0;
  201. for (int i = self.evenCounts.length - 1; i >= 0; i--) {
  202. evenChecksumPortion *= 9;
  203. evenChecksumPortion += self.evenCounts.array[i];
  204. evenSum += self.evenCounts.array[i];
  205. }
  206. int checksumPortion = oddChecksumPortion + 3 * evenChecksumPortion;
  207. if (outsideChar) {
  208. if ((oddSum & 0x01) != 0 || oddSum > 12 || oddSum < 4) {
  209. return nil;
  210. }
  211. int group = (12 - oddSum) / 2;
  212. int oddWidest = ZX_RSS14_OUTSIDE_ODD_WIDEST[group];
  213. int evenWidest = 9 - oddWidest;
  214. int vOdd = [ZXRSSUtils rssValue:self.oddCounts maxWidth:oddWidest noNarrow:NO];
  215. int vEven = [ZXRSSUtils rssValue:self.evenCounts maxWidth:evenWidest noNarrow:YES];
  216. int tEven = ZX_RSS14_OUTSIDE_EVEN_TOTAL_SUBSET[group];
  217. int gSum = ZX_RSS14_OUTSIDE_GSUM[group];
  218. return [[ZXRSSDataCharacter alloc] initWithValue:vOdd * tEven + vEven + gSum checksumPortion:checksumPortion];
  219. } else {
  220. if ((evenSum & 0x01) != 0 || evenSum > 10 || evenSum < 4) {
  221. return nil;
  222. }
  223. int group = (10 - evenSum) / 2;
  224. int oddWidest = ZX_RSS14_INSIDE_ODD_WIDEST[group];
  225. int evenWidest = 9 - oddWidest;
  226. int vOdd = [ZXRSSUtils rssValue:self.oddCounts maxWidth:oddWidest noNarrow:YES];
  227. int vEven = [ZXRSSUtils rssValue:self.evenCounts maxWidth:evenWidest noNarrow:NO];
  228. int tOdd = ZX_RSS14_INSIDE_ODD_TOTAL_SUBSET[group];
  229. int gSum = ZX_RSS14_INSIDE_GSUM[group];
  230. return [[ZXRSSDataCharacter alloc] initWithValue:vEven * tOdd + vOdd + gSum checksumPortion:checksumPortion];
  231. }
  232. }
  233. - (ZXIntArray *)findFinderPattern:(ZXBitArray *)row rowOffset:(int)rowOffset rightFinderPattern:(BOOL)rightFinderPattern {
  234. ZXIntArray *counters = self.decodeFinderCounters;
  235. [counters clear];
  236. int32_t *array = counters.array;
  237. int width = row.size;
  238. BOOL isWhite = NO;
  239. while (rowOffset < width) {
  240. isWhite = ![row get:rowOffset];
  241. if (rightFinderPattern == isWhite) {
  242. break;
  243. }
  244. rowOffset++;
  245. }
  246. int counterPosition = 0;
  247. int patternStart = rowOffset;
  248. for (int x = rowOffset; x < width; x++) {
  249. if ([row get:x] ^ isWhite) {
  250. array[counterPosition]++;
  251. } else {
  252. if (counterPosition == 3) {
  253. if ([ZXAbstractRSSReader isFinderPattern:counters]) {
  254. return [[ZXIntArray alloc] initWithInts:patternStart, x, -1];
  255. }
  256. patternStart += array[0] + array[1];
  257. array[0] = array[2];
  258. array[1] = array[3];
  259. array[2] = 0;
  260. array[3] = 0;
  261. counterPosition--;
  262. } else {
  263. counterPosition++;
  264. }
  265. array[counterPosition] = 1;
  266. isWhite = !isWhite;
  267. }
  268. }
  269. return nil;
  270. }
  271. - (ZXRSSFinderPattern *)parseFoundFinderPattern:(ZXBitArray *)row rowNumber:(int)rowNumber right:(BOOL)right startEnd:(ZXIntArray *)startEnd {
  272. BOOL firstIsBlack = [row get:startEnd.array[0]];
  273. int firstElementStart = startEnd.array[0] - 1;
  274. while (firstElementStart >= 0 && firstIsBlack ^ [row get:firstElementStart]) {
  275. firstElementStart--;
  276. }
  277. firstElementStart++;
  278. int firstCounter = startEnd.array[0] - firstElementStart;
  279. ZXIntArray *counters = self.decodeFinderCounters;
  280. int32_t *array = counters.array;
  281. for (int i = counters.length - 1; i > 0; i--) {
  282. array[i] = array[i-1];
  283. }
  284. array[0] = firstCounter;
  285. int value = [ZXAbstractRSSReader parseFinderValue:counters finderPatternType:ZX_RSS_PATTERNS_RSS14_PATTERNS];
  286. if (value == -1) {
  287. return nil;
  288. }
  289. int start = firstElementStart;
  290. int end = startEnd.array[1];
  291. if (right) {
  292. start = [row size] - 1 - start;
  293. end = [row size] - 1 - end;
  294. }
  295. return [[ZXRSSFinderPattern alloc] initWithValue:value
  296. startEnd:[[ZXIntArray alloc] initWithInts:firstElementStart, startEnd.array[1], -1]
  297. start:start
  298. end:end
  299. rowNumber:rowNumber];
  300. }
  301. - (BOOL)adjustOddEvenCounts:(BOOL)outsideChar numModules:(int)numModules {
  302. int oddSum = [ZXAbstractRSSReader count:self.oddCounts];
  303. int evenSum = [ZXAbstractRSSReader count:self.evenCounts];
  304. int mismatch = oddSum + evenSum - numModules;
  305. BOOL oddParityBad = (oddSum & 0x01) == (outsideChar ? 1 : 0);
  306. BOOL evenParityBad = (evenSum & 0x01) == 1;
  307. BOOL incrementOdd = NO;
  308. BOOL decrementOdd = NO;
  309. BOOL incrementEven = NO;
  310. BOOL decrementEven = NO;
  311. if (outsideChar) {
  312. if (oddSum > 12) {
  313. decrementOdd = YES;
  314. } else if (oddSum < 4) {
  315. incrementOdd = YES;
  316. }
  317. if (evenSum > 12) {
  318. decrementEven = YES;
  319. } else if (evenSum < 4) {
  320. incrementEven = YES;
  321. }
  322. } else {
  323. if (oddSum > 11) {
  324. decrementOdd = YES;
  325. } else if (oddSum < 5) {
  326. incrementOdd = YES;
  327. }
  328. if (evenSum > 10) {
  329. decrementEven = YES;
  330. } else if (evenSum < 4) {
  331. incrementEven = YES;
  332. }
  333. }
  334. if (mismatch == 1) {
  335. if (oddParityBad) {
  336. if (evenParityBad) {
  337. return NO;
  338. }
  339. decrementOdd = YES;
  340. } else {
  341. if (!evenParityBad) {
  342. return NO;
  343. }
  344. decrementEven = YES;
  345. }
  346. } else if (mismatch == -1) {
  347. if (oddParityBad) {
  348. if (evenParityBad) {
  349. return NO;
  350. }
  351. incrementOdd = YES;
  352. } else {
  353. if (!evenParityBad) {
  354. return NO;
  355. }
  356. incrementEven = YES;
  357. }
  358. } else if (mismatch == 0) {
  359. if (oddParityBad) {
  360. if (!evenParityBad) {
  361. return NO;
  362. }
  363. if (oddSum < evenSum) {
  364. incrementOdd = YES;
  365. decrementEven = YES;
  366. } else {
  367. decrementOdd = YES;
  368. incrementEven = YES;
  369. }
  370. } else {
  371. if (evenParityBad) {
  372. return NO;
  373. }
  374. }
  375. } else {
  376. return NO;
  377. }
  378. if (incrementOdd) {
  379. if (decrementOdd) {
  380. return NO;
  381. }
  382. [ZXAbstractRSSReader increment:self.oddCounts errors:self.oddRoundingErrors];
  383. }
  384. if (decrementOdd) {
  385. [ZXAbstractRSSReader decrement:self.oddCounts errors:self.oddRoundingErrors];
  386. }
  387. if (incrementEven) {
  388. if (decrementEven) {
  389. return NO;
  390. }
  391. [ZXAbstractRSSReader increment:self.evenCounts errors:self.oddRoundingErrors];
  392. }
  393. if (decrementEven) {
  394. [ZXAbstractRSSReader decrement:self.evenCounts errors:self.evenRoundingErrors];
  395. }
  396. return YES;
  397. }
  398. @end