Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

ZXPDF417ScanningDecoder.m 29KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685
  1. /*
  2. * Copyright 2013 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 "ZXBitMatrix.h"
  17. #import "ZXDecoderResult.h"
  18. #import "ZXErrors.h"
  19. #import "ZXIntArray.h"
  20. #import "ZXPDF417BarcodeMetadata.h"
  21. #import "ZXPDF417BarcodeValue.h"
  22. #import "ZXPDF417BoundingBox.h"
  23. #import "ZXPDF417Codeword.h"
  24. #import "ZXPDF417CodewordDecoder.h"
  25. #import "ZXPDF417Common.h"
  26. #import "ZXPDF417DecodedBitStreamParser.h"
  27. #import "ZXPDF417DetectionResult.h"
  28. #import "ZXPDF417DetectionResultRowIndicatorColumn.h"
  29. #import "ZXPDF417ECErrorCorrection.h"
  30. #import "ZXPDF417ScanningDecoder.h"
  31. #import "ZXResultPoint.h"
  32. const int ZX_PDF417_CODEWORD_SKEW_SIZE = 2;
  33. const int ZX_PDF417_MAX_ERRORS = 3;
  34. const int ZX_PDF417_MAX_EC_CODEWORDS = 512;
  35. static ZXPDF417ECErrorCorrection *errorCorrection;
  36. @implementation ZXPDF417ScanningDecoder
  37. + (void)initialize {
  38. if ([self class] != [ZXPDF417ScanningDecoder class]) return;
  39. errorCorrection = [[ZXPDF417ECErrorCorrection alloc] init];
  40. }
  41. // TODO don't pass in minCodewordWidth and maxCodewordWidth, pass in barcode columns for start and stop pattern
  42. // columns. That way width can be deducted from the pattern column.
  43. // This approach also allows to detect more details about the barcode, e.g. if a bar type (white or black) is wider
  44. // than it should be. This can happen if the scanner used a bad blackpoint.
  45. + (ZXDecoderResult *)decode:(ZXBitMatrix *)image
  46. imageTopLeft:(ZXResultPoint *)imageTopLeft
  47. imageBottomLeft:(ZXResultPoint *)imageBottomLeft
  48. imageTopRight:(ZXResultPoint *)imageTopRight
  49. imageBottomRight:(ZXResultPoint *)imageBottomRight
  50. minCodewordWidth:(int)minCodewordWidth
  51. maxCodewordWidth:(int)maxCodewordWidth
  52. error:(NSError **)error {
  53. ZXPDF417BoundingBox *boundingBox = [[ZXPDF417BoundingBox alloc] initWithImage:image topLeft:imageTopLeft bottomLeft:imageBottomLeft topRight:imageTopRight bottomRight:imageBottomRight];
  54. ZXPDF417DetectionResultRowIndicatorColumn *leftRowIndicatorColumn;
  55. ZXPDF417DetectionResultRowIndicatorColumn *rightRowIndicatorColumn;
  56. ZXPDF417DetectionResult *detectionResult;
  57. for (int i = 0; i < 2; i++) {
  58. if (imageTopLeft) {
  59. leftRowIndicatorColumn = [self rowIndicatorColumn:image boundingBox:boundingBox startPoint:imageTopLeft leftToRight:YES
  60. minCodewordWidth:minCodewordWidth maxCodewordWidth:maxCodewordWidth];
  61. }
  62. if (imageTopRight) {
  63. rightRowIndicatorColumn = [self rowIndicatorColumn:image boundingBox:boundingBox startPoint:imageTopRight leftToRight:NO
  64. minCodewordWidth:minCodewordWidth maxCodewordWidth:maxCodewordWidth];
  65. }
  66. detectionResult = [self merge:leftRowIndicatorColumn rightRowIndicatorColumn:rightRowIndicatorColumn error:error];
  67. if (!detectionResult) {
  68. return nil;
  69. }
  70. if (i == 0 && detectionResult.boundingBox &&
  71. (detectionResult.boundingBox.minY < boundingBox.minY ||
  72. detectionResult.boundingBox.maxY > boundingBox.maxY)) {
  73. boundingBox = [detectionResult boundingBox];
  74. } else {
  75. detectionResult.boundingBox = boundingBox;
  76. break;
  77. }
  78. }
  79. int maxBarcodeColumn = detectionResult.barcodeColumnCount + 1;
  80. [detectionResult setDetectionResultColumn:0 detectionResultColumn:leftRowIndicatorColumn];
  81. [detectionResult setDetectionResultColumn:maxBarcodeColumn detectionResultColumn:rightRowIndicatorColumn];
  82. BOOL leftToRight = leftRowIndicatorColumn != nil;
  83. for (int barcodeColumnCount = 1; barcodeColumnCount <= maxBarcodeColumn; barcodeColumnCount++) {
  84. int barcodeColumn = leftToRight ? barcodeColumnCount : maxBarcodeColumn - barcodeColumnCount;
  85. if ([detectionResult detectionResultColumn:barcodeColumn]) {
  86. // This will be the case for the opposite row indicator column, which doesn't need to be decoded again.
  87. continue;
  88. }
  89. ZXPDF417DetectionResultColumn *detectionResultColumn;
  90. if (barcodeColumn == 0 || barcodeColumn == maxBarcodeColumn) {
  91. detectionResultColumn = [[ZXPDF417DetectionResultRowIndicatorColumn alloc] initWithBoundingBox:boundingBox isLeft:barcodeColumn == 0];
  92. } else {
  93. detectionResultColumn = [[ZXPDF417DetectionResultColumn alloc] initWithBoundingBox:boundingBox];
  94. }
  95. [detectionResult setDetectionResultColumn:barcodeColumn detectionResultColumn:detectionResultColumn];
  96. int startColumn = -1;
  97. int previousStartColumn = startColumn;
  98. // TODO start at a row for which we know the start position, then detect upwards and downwards from there.
  99. for (int imageRow = boundingBox.minY; imageRow <= boundingBox.maxY; imageRow++) {
  100. startColumn = [self startColumn:detectionResult barcodeColumn:barcodeColumn imageRow:imageRow leftToRight:leftToRight];
  101. if (startColumn < 0 || startColumn > boundingBox.maxX) {
  102. if (previousStartColumn == -1) {
  103. continue;
  104. }
  105. startColumn = previousStartColumn;
  106. }
  107. ZXPDF417Codeword *codeword = [self detectCodeword:image minColumn:boundingBox.minX maxColumn:boundingBox.maxX leftToRight:leftToRight
  108. startColumn:startColumn imageRow:imageRow minCodewordWidth:minCodewordWidth maxCodewordWidth:maxCodewordWidth];
  109. if (codeword) {
  110. [detectionResultColumn setCodeword:imageRow codeword:codeword];
  111. previousStartColumn = startColumn;
  112. minCodewordWidth = MIN(minCodewordWidth, codeword.width);
  113. maxCodewordWidth = MAX(maxCodewordWidth, codeword.width);
  114. }
  115. }
  116. }
  117. return [self createDecoderResult:detectionResult error:error];
  118. }
  119. + (ZXPDF417DetectionResult *)merge:(ZXPDF417DetectionResultRowIndicatorColumn *)leftRowIndicatorColumn
  120. rightRowIndicatorColumn:(ZXPDF417DetectionResultRowIndicatorColumn *)rightRowIndicatorColumn
  121. error:(NSError **)error {
  122. if (!leftRowIndicatorColumn && !rightRowIndicatorColumn) {
  123. return nil;
  124. }
  125. ZXPDF417BarcodeMetadata *barcodeMetadata = [self barcodeMetadata:leftRowIndicatorColumn rightRowIndicatorColumn:rightRowIndicatorColumn];
  126. if (!barcodeMetadata) {
  127. return nil;
  128. }
  129. ZXPDF417BoundingBox *leftBoundingBox, *rightBoundingBox;
  130. if (![self adjustBoundingBox:&leftBoundingBox rowIndicatorColumn:leftRowIndicatorColumn error:error]) {
  131. return nil;
  132. }
  133. if (![self adjustBoundingBox:&rightBoundingBox rowIndicatorColumn:rightRowIndicatorColumn error:error]) {
  134. return nil;
  135. }
  136. ZXPDF417BoundingBox *boundingBox = [ZXPDF417BoundingBox mergeLeftBox:leftBoundingBox rightBox:rightBoundingBox];
  137. if (!boundingBox) {
  138. if (error) *error = ZXNotFoundErrorInstance();
  139. return nil;
  140. }
  141. return [[ZXPDF417DetectionResult alloc] initWithBarcodeMetadata:barcodeMetadata boundingBox:boundingBox];
  142. }
  143. + (BOOL)adjustBoundingBox:(ZXPDF417BoundingBox **)boundingBox
  144. rowIndicatorColumn:(ZXPDF417DetectionResultRowIndicatorColumn *)rowIndicatorColumn
  145. error:(NSError **)error {
  146. if (!rowIndicatorColumn) {
  147. *boundingBox = nil;
  148. return YES;
  149. }
  150. ZXIntArray *rowHeights;
  151. if (![rowIndicatorColumn getRowHeights:&rowHeights]) {
  152. if (error) *error = ZXFormatErrorInstance();
  153. *boundingBox = nil;
  154. return NO;
  155. }
  156. if (!rowHeights) {
  157. *boundingBox = nil;
  158. return YES;
  159. }
  160. int maxRowHeight = [self max:rowHeights];
  161. int missingStartRows = 0;
  162. for (int i = 0; i < rowHeights.length; i++) {
  163. int rowHeight = rowHeights.array[i];
  164. missingStartRows += maxRowHeight - rowHeight;
  165. if (rowHeight > 0) {
  166. break;
  167. }
  168. }
  169. NSArray *codewords = rowIndicatorColumn.codewords;
  170. for (int row = 0; missingStartRows > 0 && codewords[row] == [NSNull null]; row++) {
  171. missingStartRows--;
  172. }
  173. int missingEndRows = 0;
  174. for (int row = rowHeights.length - 1; row >= 0; row--) {
  175. missingEndRows += maxRowHeight - rowHeights.array[row];
  176. if (rowHeights.array[row] > 0) {
  177. break;
  178. }
  179. }
  180. for (int row = (int)[codewords count] - 1; missingEndRows > 0 && codewords[row] == [NSNull null]; row--) {
  181. missingEndRows--;
  182. }
  183. *boundingBox = [rowIndicatorColumn.boundingBox addMissingRows:missingStartRows
  184. missingEndRows:missingEndRows
  185. isLeft:rowIndicatorColumn.isLeft];
  186. return *boundingBox != nil;
  187. }
  188. + (int)max:(ZXIntArray *)values {
  189. int maxValue = -1;
  190. for (int i = 0; i < values.length; i++) {
  191. int value = values.array[i];
  192. maxValue = MAX(maxValue, value);
  193. }
  194. return maxValue;
  195. }
  196. + (ZXPDF417BarcodeMetadata *)barcodeMetadata:(ZXPDF417DetectionResultRowIndicatorColumn *)leftRowIndicatorColumn
  197. rightRowIndicatorColumn:(ZXPDF417DetectionResultRowIndicatorColumn *)rightRowIndicatorColumn {
  198. ZXPDF417BarcodeMetadata *leftBarcodeMetadata;
  199. if (!leftRowIndicatorColumn ||
  200. !(leftBarcodeMetadata = leftRowIndicatorColumn.barcodeMetadata)) {
  201. return rightRowIndicatorColumn ? rightRowIndicatorColumn.barcodeMetadata : nil;
  202. }
  203. ZXPDF417BarcodeMetadata *rightBarcodeMetadata;
  204. if (!rightRowIndicatorColumn ||
  205. !(rightBarcodeMetadata = rightRowIndicatorColumn.barcodeMetadata)) {
  206. return leftRowIndicatorColumn.barcodeMetadata;
  207. }
  208. if (leftBarcodeMetadata.columnCount != rightBarcodeMetadata.columnCount &&
  209. leftBarcodeMetadata.errorCorrectionLevel != rightBarcodeMetadata.errorCorrectionLevel &&
  210. leftBarcodeMetadata.rowCount != rightBarcodeMetadata.rowCount) {
  211. return nil;
  212. }
  213. return leftBarcodeMetadata;
  214. }
  215. + (ZXPDF417DetectionResultRowIndicatorColumn *)rowIndicatorColumn:(ZXBitMatrix *)image
  216. boundingBox:(ZXPDF417BoundingBox *)boundingBox
  217. startPoint:(ZXResultPoint *)startPoint
  218. leftToRight:(BOOL)leftToRight
  219. minCodewordWidth:(int)minCodewordWidth
  220. maxCodewordWidth:(int)maxCodewordWidth {
  221. ZXPDF417DetectionResultRowIndicatorColumn *rowIndicatorColumn = [[ZXPDF417DetectionResultRowIndicatorColumn alloc] initWithBoundingBox:boundingBox
  222. isLeft:leftToRight];
  223. for (int i = 0; i < 2; i++) {
  224. int increment = i == 0 ? 1 : -1;
  225. int startColumn = (int) startPoint.x;
  226. for (int imageRow = (int) startPoint.y; imageRow <= boundingBox.maxY &&
  227. imageRow >= boundingBox.minY; imageRow += increment) {
  228. ZXPDF417Codeword *codeword = [self detectCodeword:image minColumn:0 maxColumn:image.width leftToRight:leftToRight startColumn:startColumn imageRow:imageRow
  229. minCodewordWidth:minCodewordWidth maxCodewordWidth:maxCodewordWidth];
  230. if (codeword) {
  231. [rowIndicatorColumn setCodeword:imageRow codeword:codeword];
  232. if (leftToRight) {
  233. startColumn = codeword.startX;
  234. } else {
  235. startColumn = codeword.endX;
  236. }
  237. }
  238. }
  239. }
  240. return rowIndicatorColumn;
  241. }
  242. + (BOOL)adjustCodewordCount:(ZXPDF417DetectionResult *)detectionResult barcodeMatrix:(NSArray *)barcodeMatrix {
  243. ZXIntArray *numberOfCodewords = [(ZXPDF417BarcodeValue *)barcodeMatrix[0][1] value];
  244. int calculatedNumberOfCodewords = [detectionResult barcodeColumnCount] * [detectionResult barcodeRowCount];
  245. [self numberOfECCodeWords:detectionResult.barcodeECLevel];
  246. if (numberOfCodewords.length == 0) {
  247. if (calculatedNumberOfCodewords < 1 || calculatedNumberOfCodewords > ZX_PDF417_MAX_CODEWORDS_IN_BARCODE) {
  248. return NO;
  249. }
  250. [(ZXPDF417BarcodeValue *)barcodeMatrix[0][1] setValue:calculatedNumberOfCodewords];
  251. } else if (numberOfCodewords.array[0] != calculatedNumberOfCodewords) {
  252. // The calculated one is more reliable as it is derived from the row indicator columns
  253. [(ZXPDF417BarcodeValue *)barcodeMatrix[0][1] setValue:calculatedNumberOfCodewords];
  254. }
  255. return YES;
  256. }
  257. + (ZXDecoderResult *)createDecoderResult:(ZXPDF417DetectionResult *)detectionResult error:(NSError **)error {
  258. NSArray *barcodeMatrix = [self createBarcodeMatrix:detectionResult];
  259. if (!barcodeMatrix) {
  260. if (error) *error = ZXFormatErrorInstance();
  261. return nil;
  262. }
  263. if (![self adjustCodewordCount:detectionResult barcodeMatrix:barcodeMatrix]) {
  264. if (error) *error = ZXNotFoundErrorInstance();
  265. return nil;
  266. }
  267. NSMutableArray *erasures = [NSMutableArray array];
  268. ZXIntArray *codewords = [[ZXIntArray alloc] initWithLength:detectionResult.barcodeRowCount * detectionResult.barcodeColumnCount];
  269. NSMutableArray *ambiguousIndexValuesList = [NSMutableArray array];
  270. NSMutableArray *ambiguousIndexesList = [NSMutableArray array];
  271. for (int row = 0; row < detectionResult.barcodeRowCount; row++) {
  272. for (int column = 0; column < detectionResult.barcodeColumnCount; column++) {
  273. ZXIntArray *values = [(ZXPDF417BarcodeValue *)barcodeMatrix[row][column + 1] value];
  274. int codewordIndex = row * detectionResult.barcodeColumnCount + column;
  275. if (values.length == 0) {
  276. [erasures addObject:@(codewordIndex)];
  277. } else if (values.length == 1) {
  278. codewords.array[codewordIndex] = values.array[0];
  279. } else {
  280. [ambiguousIndexesList addObject:@(codewordIndex)];
  281. [ambiguousIndexValuesList addObject:values];
  282. }
  283. }
  284. }
  285. return [self createDecoderResultFromAmbiguousValues:detectionResult.barcodeECLevel
  286. codewords:codewords
  287. erasureArray:[ZXPDF417Common toIntArray:erasures]
  288. ambiguousIndexes:[ZXPDF417Common toIntArray:ambiguousIndexesList]
  289. ambiguousIndexValues:ambiguousIndexValuesList
  290. error:error];
  291. }
  292. /**
  293. * This method deals with the fact, that the decoding process doesn't always yield a single most likely value. The
  294. * current error correction implementation doesn't deal with erasures very well, so it's better to provide a value
  295. * for these ambiguous codewords instead of treating it as an erasure. The problem is that we don't know which of
  296. * the ambiguous values to choose. We try decode using the first value, and if that fails, we use another of the
  297. * ambiguous values and try to decode again. This usually only happens on very hard to read and decode barcodes,
  298. * so decoding the normal barcodes is not affected by this.
  299. *
  300. * @param erasureArray contains the indexes of erasures
  301. * @param ambiguousIndexes array with the indexes that have more than one most likely value
  302. * @param ambiguousIndexValues two dimensional array that contains the ambiguous values. The first dimension must
  303. * be the same length as the ambiguousIndexes array
  304. */
  305. + (ZXDecoderResult *)createDecoderResultFromAmbiguousValues:(int)ecLevel
  306. codewords:(ZXIntArray *)codewords
  307. erasureArray:(ZXIntArray *)erasureArray
  308. ambiguousIndexes:(ZXIntArray *)ambiguousIndexes
  309. ambiguousIndexValues:(NSArray *)ambiguousIndexValues
  310. error:(NSError **)error {
  311. ZXIntArray *ambiguousIndexCount = [[ZXIntArray alloc] initWithLength:ambiguousIndexes.length];
  312. int tries = 100;
  313. while (tries-- > 0) {
  314. for (int i = 0; i < ambiguousIndexCount.length; i++) {
  315. ZXIntArray *a = ambiguousIndexValues[i];
  316. codewords.array[ambiguousIndexes.array[i]] = a.array[(ambiguousIndexCount.array[i] + 1) % [(ZXIntArray *)ambiguousIndexValues[i] length]];
  317. }
  318. NSError *e;
  319. ZXDecoderResult *result = [self decodeCodewords:codewords ecLevel:ecLevel erasures:erasureArray error:&e];
  320. if (result) {
  321. return result;
  322. } else if (e.code != ZXChecksumError) {
  323. if (error) *error = e;
  324. return nil;
  325. }
  326. if (ambiguousIndexCount.length == 0) {
  327. if (error) *error = ZXChecksumErrorInstance();
  328. return nil;
  329. }
  330. for (int i = 0; i < ambiguousIndexCount.length; i++) {
  331. if (ambiguousIndexCount.array[i] < [(ZXIntArray *)ambiguousIndexValues[i] length] - 1) {
  332. ambiguousIndexCount.array[i]++;
  333. break;
  334. } else {
  335. ambiguousIndexCount.array[i] = 0;
  336. if (i == ambiguousIndexes.length - 1) {
  337. if (error) *error = ZXChecksumErrorInstance();
  338. return nil;
  339. }
  340. }
  341. }
  342. }
  343. if (error) *error = ZXChecksumErrorInstance();
  344. return nil;
  345. }
  346. + (NSArray *)createBarcodeMatrix:(ZXPDF417DetectionResult *)detectionResult {
  347. NSMutableArray *barcodeMatrix = [NSMutableArray array];
  348. for (int row = 0; row < detectionResult.barcodeRowCount; row++) {
  349. [barcodeMatrix addObject:[NSMutableArray array]];
  350. for (int column = 0; column < detectionResult.barcodeColumnCount + 2; column++) {
  351. barcodeMatrix[row][column] = [[ZXPDF417BarcodeValue alloc] init];
  352. }
  353. }
  354. int column = 0;
  355. for (ZXPDF417DetectionResultColumn *detectionResultColumn in [detectionResult detectionResultColumns]) {
  356. if ((id)detectionResultColumn != [NSNull null]) {
  357. for (ZXPDF417Codeword *codeword in detectionResultColumn.codewords) {
  358. if ((id)codeword != [NSNull null]) {
  359. int rowNumber = codeword.rowNumber;
  360. if (rowNumber >= 0) {
  361. if (rowNumber >= barcodeMatrix.count) {
  362. return nil;
  363. }
  364. [(ZXPDF417BarcodeValue *)barcodeMatrix[rowNumber][column] setValue:codeword.value];
  365. }
  366. }
  367. }
  368. }
  369. column++;
  370. }
  371. return barcodeMatrix;
  372. }
  373. + (BOOL)isValidBarcodeColumn:(ZXPDF417DetectionResult *)detectionResult barcodeColumn:(int)barcodeColumn {
  374. return barcodeColumn >= 0 && barcodeColumn <= detectionResult.barcodeColumnCount + 1;
  375. }
  376. + (int)startColumn:(ZXPDF417DetectionResult *)detectionResult
  377. barcodeColumn:(int)barcodeColumn
  378. imageRow:(int)imageRow
  379. leftToRight:(BOOL)leftToRight {
  380. int offset = leftToRight ? 1 : -1;
  381. ZXPDF417Codeword *codeword;
  382. if ([self isValidBarcodeColumn:detectionResult barcodeColumn:barcodeColumn - offset]) {
  383. codeword = [[detectionResult detectionResultColumn:barcodeColumn - offset] codeword:imageRow];
  384. }
  385. if (codeword) {
  386. return leftToRight ? codeword.endX : codeword.startX;
  387. }
  388. codeword = [[detectionResult detectionResultColumn:barcodeColumn] codewordNearby:imageRow];
  389. if (codeword) {
  390. return leftToRight ? codeword.startX : codeword.endX;
  391. }
  392. if ([self isValidBarcodeColumn:detectionResult barcodeColumn:barcodeColumn - offset]) {
  393. codeword = [[detectionResult detectionResultColumn:barcodeColumn - offset] codewordNearby:imageRow];
  394. }
  395. if (codeword) {
  396. return leftToRight ? codeword.endX : codeword.startX;
  397. }
  398. int skippedColumns = 0;
  399. while ([self isValidBarcodeColumn:detectionResult barcodeColumn:barcodeColumn - offset]) {
  400. barcodeColumn -= offset;
  401. for (ZXPDF417Codeword *previousRowCodeword in [detectionResult detectionResultColumn:barcodeColumn].codewords) {
  402. if ((id)previousRowCodeword != [NSNull null]) {
  403. return (leftToRight ? previousRowCodeword.endX : previousRowCodeword.startX) +
  404. offset *
  405. skippedColumns *
  406. (previousRowCodeword.endX - previousRowCodeword.startX);
  407. }
  408. }
  409. skippedColumns++;
  410. }
  411. return leftToRight ? detectionResult.boundingBox.minX : detectionResult.boundingBox.maxX;
  412. }
  413. + (ZXPDF417Codeword *)detectCodeword:(ZXBitMatrix *)image
  414. minColumn:(int)minColumn
  415. maxColumn:(int)maxColumn
  416. leftToRight:(BOOL)leftToRight
  417. startColumn:(int)startColumn
  418. imageRow:(int)imageRow
  419. minCodewordWidth:(int)minCodewordWidth
  420. maxCodewordWidth:(int)maxCodewordWidth {
  421. startColumn = [self adjustCodewordStartColumn:image minColumn:minColumn maxColumn:maxColumn leftToRight:leftToRight codewordStartColumn:startColumn imageRow:imageRow];
  422. // we usually know fairly exact now how long a codeword is. We should provide minimum and maximum expected length
  423. // and try to adjust the read pixels, e.g. remove single pixel errors or try to cut off exceeding pixels.
  424. // min and maxCodewordWidth should not be used as they are calculated for the whole barcode an can be inaccurate
  425. // for the current position
  426. NSMutableArray *moduleBitCount = [self moduleBitCount:image minColumn:minColumn maxColumn:maxColumn leftToRight:leftToRight startColumn:startColumn imageRow:imageRow];
  427. if (!moduleBitCount) {
  428. return nil;
  429. }
  430. int endColumn;
  431. int codewordBitCount = [ZXPDF417Common bitCountSum:moduleBitCount];
  432. if (leftToRight) {
  433. endColumn = startColumn + codewordBitCount;
  434. } else {
  435. for (int i = 0; i < [moduleBitCount count] / 2; i++) {
  436. int tmpCount = [moduleBitCount[i] intValue];
  437. moduleBitCount[i] = moduleBitCount[[moduleBitCount count] - 1 - i];
  438. moduleBitCount[[moduleBitCount count] - 1 - i] = @(tmpCount);
  439. }
  440. endColumn = startColumn;
  441. startColumn = endColumn - codewordBitCount;
  442. }
  443. // TODO implement check for width and correction of black and white bars
  444. // use start (and maybe stop pattern) to determine if blackbars are wider than white bars. If so, adjust.
  445. // should probably done only for codewords with a lot more than 17 bits.
  446. // The following fixes 10-1.png, which has wide black bars and small white bars
  447. // for (int i = 0; i < moduleBitCount.length; i++) {
  448. // if (i % 2 == 0) {
  449. // moduleBitCount[i]--;
  450. // } else {
  451. // moduleBitCount[i]++;
  452. // }
  453. // }
  454. // We could also use the width of surrounding codewords for more accurate results, but this seems
  455. // sufficient for now
  456. if (![self checkCodewordSkew:codewordBitCount minCodewordWidth:minCodewordWidth maxCodewordWidth:maxCodewordWidth]) {
  457. // We could try to use the startX and endX position of the codeword in the same column in the previous row,
  458. // create the bit count from it and normalize it to 8. This would help with single pixel errors.
  459. return nil;
  460. }
  461. int decodedValue = [ZXPDF417CodewordDecoder decodedValue:moduleBitCount];
  462. int codeword = [ZXPDF417Common codeword:decodedValue];
  463. if (codeword == -1) {
  464. return nil;
  465. }
  466. return [[ZXPDF417Codeword alloc] initWithStartX:startColumn endX:endColumn bucket:[self codewordBucketNumber:decodedValue] value:codeword];
  467. }
  468. + (NSMutableArray *)moduleBitCount:(ZXBitMatrix *)image
  469. minColumn:(int)minColumn
  470. maxColumn:(int)maxColumn
  471. leftToRight:(BOOL)leftToRight
  472. startColumn:(int)startColumn
  473. imageRow:(int)imageRow {
  474. int imageColumn = startColumn;
  475. NSMutableArray *moduleBitCount = [NSMutableArray arrayWithCapacity:8];
  476. for (int i = 0; i < 8; i++) {
  477. [moduleBitCount addObject:@0];
  478. }
  479. int moduleNumber = 0;
  480. int increment = leftToRight ? 1 : -1;
  481. BOOL previousPixelValue = leftToRight;
  482. while (((leftToRight && imageColumn < maxColumn) || (!leftToRight && imageColumn >= minColumn)) &&
  483. moduleNumber < [moduleBitCount count]) {
  484. if ([image getX:imageColumn y:imageRow] == previousPixelValue) {
  485. moduleBitCount[moduleNumber] = @([moduleBitCount[moduleNumber] intValue] + 1);
  486. imageColumn += increment;
  487. } else {
  488. moduleNumber++;
  489. previousPixelValue = !previousPixelValue;
  490. }
  491. }
  492. if (moduleNumber == [moduleBitCount count] ||
  493. (((leftToRight && imageColumn == maxColumn) || (!leftToRight && imageColumn == minColumn)) && moduleNumber == [moduleBitCount count] - 1)) {
  494. return moduleBitCount;
  495. }
  496. return nil;
  497. }
  498. + (int)numberOfECCodeWords:(int)barcodeECLevel {
  499. return 2 << barcodeECLevel;
  500. }
  501. + (int)adjustCodewordStartColumn:(ZXBitMatrix *)image
  502. minColumn:(int)minColumn
  503. maxColumn:(int)maxColumn
  504. leftToRight:(BOOL)leftToRight
  505. codewordStartColumn:(int)codewordStartColumn
  506. imageRow:(int)imageRow {
  507. int correctedStartColumn = codewordStartColumn;
  508. int increment = leftToRight ? -1 : 1;
  509. // there should be no black pixels before the start column. If there are, then we need to start earlier.
  510. for (int i = 0; i < 2; i++) {
  511. while (((leftToRight && correctedStartColumn >= minColumn) || (!leftToRight && correctedStartColumn < maxColumn)) &&
  512. leftToRight == [image getX:correctedStartColumn y:imageRow]) {
  513. if (abs(codewordStartColumn - correctedStartColumn) > ZX_PDF417_CODEWORD_SKEW_SIZE) {
  514. return codewordStartColumn;
  515. }
  516. correctedStartColumn += increment;
  517. }
  518. increment = -increment;
  519. leftToRight = !leftToRight;
  520. }
  521. return correctedStartColumn;
  522. }
  523. + (BOOL)checkCodewordSkew:(int)codewordSize minCodewordWidth:(int)minCodewordWidth maxCodewordWidth:(int)maxCodewordWidth {
  524. return minCodewordWidth - ZX_PDF417_CODEWORD_SKEW_SIZE <= codewordSize &&
  525. codewordSize <= maxCodewordWidth + ZX_PDF417_CODEWORD_SKEW_SIZE;
  526. }
  527. + (ZXDecoderResult *)decodeCodewords:(ZXIntArray *)codewords ecLevel:(int)ecLevel erasures:(ZXIntArray *)erasures error:(NSError **)error {
  528. if (codewords.length == 0) {
  529. if (error) *error = ZXFormatErrorInstance();
  530. return nil;
  531. }
  532. int numECCodewords = 1 << (ecLevel + 1);
  533. int correctedErrorsCount = [self correctErrors:codewords erasures:erasures numECCodewords:numECCodewords];
  534. if (correctedErrorsCount == -1) {
  535. if (error) *error = ZXChecksumErrorInstance();
  536. return nil;
  537. }
  538. if (![self verifyCodewordCount:codewords numECCodewords:numECCodewords]) {
  539. if (error) *error = ZXFormatErrorInstance();
  540. return nil;
  541. }
  542. // Decode the codewords
  543. ZXDecoderResult *decoderResult = [ZXPDF417DecodedBitStreamParser decode:codewords ecLevel:[@(ecLevel) stringValue] error:error];
  544. if (!decoderResult) {
  545. return nil;
  546. }
  547. decoderResult.errorsCorrected = @(correctedErrorsCount);
  548. decoderResult.erasures = @(erasures.length);
  549. return decoderResult;
  550. }
  551. /**
  552. * Given data and error-correction codewords received, possibly corrupted by errors, attempts to
  553. * correct the errors in-place.
  554. *
  555. * @param codewords data and error correction codewords
  556. * @param erasures positions of any known erasures
  557. * @param numECCodewords number of error correction codewords that are available in codewords
  558. * @throws ChecksumException if error correction fails
  559. */
  560. + (int)correctErrors:(ZXIntArray *)codewords erasures:(ZXIntArray *)erasures numECCodewords:(int)numECCodewords {
  561. if (erasures &&
  562. (erasures.length > numECCodewords / 2 + ZX_PDF417_MAX_ERRORS ||
  563. numECCodewords < 0 ||
  564. numECCodewords > ZX_PDF417_MAX_EC_CODEWORDS)) {
  565. // Too many errors or EC Codewords is corrupted
  566. return -1;
  567. }
  568. return [errorCorrection decode:codewords numECCodewords:numECCodewords erasures:erasures];
  569. }
  570. /**
  571. * Verify that all is OK with the codeword array.
  572. */
  573. + (BOOL)verifyCodewordCount:(ZXIntArray *)codewords numECCodewords:(int)numECCodewords {
  574. if (codewords.length < 4) {
  575. // Codeword array size should be at least 4 allowing for
  576. // Count CW, At least one Data CW, Error Correction CW, Error Correction CW
  577. return NO;
  578. }
  579. // The first codeword, the Symbol Length Descriptor, shall always encode the total number of data
  580. // codewords in the symbol, including the Symbol Length Descriptor itself, data codewords and pad
  581. // codewords, but excluding the number of error correction codewords.
  582. int numberOfCodewords = codewords.array[0];
  583. if (numberOfCodewords > codewords.length) {
  584. return NO;
  585. }
  586. if (numberOfCodewords == 0) {
  587. // Reset to the length of the array - 8 (Allow for at least level 3 Error Correction (8 Error Codewords)
  588. if (numECCodewords < codewords.length) {
  589. codewords.array[0] = codewords.length - numECCodewords;
  590. } else {
  591. return NO;
  592. }
  593. }
  594. return YES;
  595. }
  596. + (NSArray *)bitCountForCodeword:(int)codeword {
  597. NSMutableArray *result = [NSMutableArray array];
  598. for (int i = 0; i < 8; i++) {
  599. [result addObject:@0];
  600. }
  601. int previousValue = 0;
  602. int i = (int)[result count] - 1;
  603. while (YES) {
  604. if ((codeword & 0x1) != previousValue) {
  605. previousValue = codeword & 0x1;
  606. i--;
  607. if (i < 0) {
  608. break;
  609. }
  610. }
  611. result[i] = @([result[i] intValue] + 1);
  612. codeword >>= 1;
  613. }
  614. return result;
  615. }
  616. + (int)codewordBucketNumber:(int)codeword {
  617. return [self codewordBucketNumberWithModuleBitCount:[self bitCountForCodeword:codeword]];
  618. }
  619. + (int)codewordBucketNumberWithModuleBitCount:(NSArray *)moduleBitCount {
  620. return ([moduleBitCount[0] intValue] - [moduleBitCount[2] intValue] + [moduleBitCount[4] intValue] - [moduleBitCount[6] intValue] + 9) % 9;
  621. }
  622. - (NSString *)description:(NSArray *)barcodeMatrix {
  623. NSMutableString *result = [NSMutableString string];
  624. for (int row = 0; row < [barcodeMatrix count]; row++) {
  625. [result appendFormat:@"Row %2d: ", row];
  626. for (int column = 0; column < [(NSArray *)barcodeMatrix[row] count]; column++) {
  627. ZXPDF417BarcodeValue *barcodeValue = barcodeMatrix[row][column];
  628. if ([barcodeValue value].length == 0) {
  629. [result appendString:@" "];
  630. } else {
  631. [result appendFormat:@"%4d(%2d)", [barcodeValue value].array[0],
  632. [[barcodeValue confidence:[barcodeValue value].array[0]] intValue]];
  633. }
  634. }
  635. [result appendString:@"\n"];
  636. }
  637. return [NSString stringWithString:result];
  638. }
  639. @end