123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534 |
- /*
- * Copyright 2012 ZXing authors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
- #import "ZXBitArray.h"
- #import "ZXByteArray.h"
- #import "ZXCode128Reader.h"
- #import "ZXDecodeHints.h"
- #import "ZXErrors.h"
- #import "ZXIntArray.h"
- #import "ZXOneDReader.h"
- #import "ZXResult.h"
- #import "ZXResultPoint.h"
-
- const int ZX_CODE128_CODE_PATTERNS_LEN = 107;
- const int ZX_CODE128_CODE_PATTERNS[ZX_CODE128_CODE_PATTERNS_LEN][7] = {
- {2, 1, 2, 2, 2, 2}, // 0
- {2, 2, 2, 1, 2, 2},
- {2, 2, 2, 2, 2, 1},
- {1, 2, 1, 2, 2, 3},
- {1, 2, 1, 3, 2, 2},
- {1, 3, 1, 2, 2, 2}, // 5
- {1, 2, 2, 2, 1, 3},
- {1, 2, 2, 3, 1, 2},
- {1, 3, 2, 2, 1, 2},
- {2, 2, 1, 2, 1, 3},
- {2, 2, 1, 3, 1, 2}, // 10
- {2, 3, 1, 2, 1, 2},
- {1, 1, 2, 2, 3, 2},
- {1, 2, 2, 1, 3, 2},
- {1, 2, 2, 2, 3, 1},
- {1, 1, 3, 2, 2, 2}, // 15
- {1, 2, 3, 1, 2, 2},
- {1, 2, 3, 2, 2, 1},
- {2, 2, 3, 2, 1, 1},
- {2, 2, 1, 1, 3, 2},
- {2, 2, 1, 2, 3, 1}, // 20
- {2, 1, 3, 2, 1, 2},
- {2, 2, 3, 1, 1, 2},
- {3, 1, 2, 1, 3, 1},
- {3, 1, 1, 2, 2, 2},
- {3, 2, 1, 1, 2, 2}, // 25
- {3, 2, 1, 2, 2, 1},
- {3, 1, 2, 2, 1, 2},
- {3, 2, 2, 1, 1, 2},
- {3, 2, 2, 2, 1, 1},
- {2, 1, 2, 1, 2, 3}, // 30
- {2, 1, 2, 3, 2, 1},
- {2, 3, 2, 1, 2, 1},
- {1, 1, 1, 3, 2, 3},
- {1, 3, 1, 1, 2, 3},
- {1, 3, 1, 3, 2, 1}, // 35
- {1, 1, 2, 3, 1, 3},
- {1, 3, 2, 1, 1, 3},
- {1, 3, 2, 3, 1, 1},
- {2, 1, 1, 3, 1, 3},
- {2, 3, 1, 1, 1, 3}, // 40
- {2, 3, 1, 3, 1, 1},
- {1, 1, 2, 1, 3, 3},
- {1, 1, 2, 3, 3, 1},
- {1, 3, 2, 1, 3, 1},
- {1, 1, 3, 1, 2, 3}, // 45
- {1, 1, 3, 3, 2, 1},
- {1, 3, 3, 1, 2, 1},
- {3, 1, 3, 1, 2, 1},
- {2, 1, 1, 3, 3, 1},
- {2, 3, 1, 1, 3, 1}, // 50
- {2, 1, 3, 1, 1, 3},
- {2, 1, 3, 3, 1, 1},
- {2, 1, 3, 1, 3, 1},
- {3, 1, 1, 1, 2, 3},
- {3, 1, 1, 3, 2, 1}, // 55
- {3, 3, 1, 1, 2, 1},
- {3, 1, 2, 1, 1, 3},
- {3, 1, 2, 3, 1, 1},
- {3, 3, 2, 1, 1, 1},
- {3, 1, 4, 1, 1, 1}, // 60
- {2, 2, 1, 4, 1, 1},
- {4, 3, 1, 1, 1, 1},
- {1, 1, 1, 2, 2, 4},
- {1, 1, 1, 4, 2, 2},
- {1, 2, 1, 1, 2, 4}, // 65
- {1, 2, 1, 4, 2, 1},
- {1, 4, 1, 1, 2, 2},
- {1, 4, 1, 2, 2, 1},
- {1, 1, 2, 2, 1, 4},
- {1, 1, 2, 4, 1, 2}, // 70
- {1, 2, 2, 1, 1, 4},
- {1, 2, 2, 4, 1, 1},
- {1, 4, 2, 1, 1, 2},
- {1, 4, 2, 2, 1, 1},
- {2, 4, 1, 2, 1, 1}, // 75
- {2, 2, 1, 1, 1, 4},
- {4, 1, 3, 1, 1, 1},
- {2, 4, 1, 1, 1, 2},
- {1, 3, 4, 1, 1, 1},
- {1, 1, 1, 2, 4, 2}, // 80
- {1, 2, 1, 1, 4, 2},
- {1, 2, 1, 2, 4, 1},
- {1, 1, 4, 2, 1, 2},
- {1, 2, 4, 1, 1, 2},
- {1, 2, 4, 2, 1, 1}, // 85
- {4, 1, 1, 2, 1, 2},
- {4, 2, 1, 1, 1, 2},
- {4, 2, 1, 2, 1, 1},
- {2, 1, 2, 1, 4, 1},
- {2, 1, 4, 1, 2, 1}, // 90
- {4, 1, 2, 1, 2, 1},
- {1, 1, 1, 1, 4, 3},
- {1, 1, 1, 3, 4, 1},
- {1, 3, 1, 1, 4, 1},
- {1, 1, 4, 1, 1, 3}, // 95
- {1, 1, 4, 3, 1, 1},
- {4, 1, 1, 1, 1, 3},
- {4, 1, 1, 3, 1, 1},
- {1, 1, 3, 1, 4, 1},
- {1, 1, 4, 1, 3, 1}, // 100
- {3, 1, 1, 1, 4, 1},
- {4, 1, 1, 1, 3, 1},
- {2, 1, 1, 4, 1, 2},
- {2, 1, 1, 2, 1, 4},
- {2, 1, 1, 2, 3, 2}, // 105
- {2, 3, 3, 1, 1, 1, 2}
- };
-
- static float ZX_CODE128_MAX_AVG_VARIANCE = 0.25f;
- static float ZX_CODE128_MAX_INDIVIDUAL_VARIANCE = 0.7f;
-
- const int ZX_CODE128_CODE_SHIFT = 98;
- const int ZX_CODE128_CODE_CODE_C = 99;
- const int ZX_CODE128_CODE_CODE_B = 100;
- const int ZX_CODE128_CODE_CODE_A = 101;
- const int ZX_CODE128_CODE_FNC_1 = 102;
- const int ZX_CODE128_CODE_FNC_2 = 97;
- const int ZX_CODE128_CODE_FNC_3 = 96;
- const int ZX_CODE128_CODE_FNC_4_A = 101;
- const int ZX_CODE128_CODE_FNC_4_B = 100;
- const int ZX_CODE128_CODE_START_A = 103;
- const int ZX_CODE128_CODE_START_B = 104;
- const int ZX_CODE128_CODE_START_C = 105;
- const int ZX_CODE128_CODE_STOP = 106;
-
- @implementation ZXCode128Reader
-
- - (ZXIntArray *)findStartPattern:(ZXBitArray *)row {
- int width = row.size;
- int rowOffset = [row nextSet:0];
-
- int counterPosition = 0;
- ZXIntArray *counters = [[ZXIntArray alloc] initWithLength:6];
- int32_t *array = counters.array;
- int patternStart = rowOffset;
- BOOL isWhite = NO;
- int patternLength = (int)counters.length;
-
- for (int i = rowOffset; i < width; i++) {
- if ([row get:i] ^ isWhite) {
- array[counterPosition]++;
- } else {
- if (counterPosition == patternLength - 1) {
- float bestVariance = ZX_CODE128_MAX_AVG_VARIANCE;
- int bestMatch = -1;
- for (int startCode = ZX_CODE128_CODE_START_A; startCode <= ZX_CODE128_CODE_START_C; startCode++) {
- float variance = [ZXOneDReader patternMatchVariance:counters pattern:ZX_CODE128_CODE_PATTERNS[startCode] maxIndividualVariance:ZX_CODE128_MAX_INDIVIDUAL_VARIANCE];
- if (variance < bestVariance) {
- bestVariance = variance;
- bestMatch = startCode;
- }
- }
- // Look for whitespace before start pattern, >= 50% of width of start pattern
- if (bestMatch >= 0 &&
- [row isRange:MAX(0, patternStart - (i - patternStart) / 2) end:patternStart value:NO]) {
- return [[ZXIntArray alloc] initWithInts:patternStart, i, bestMatch, -1];
- }
- patternStart += array[0] + array[1];
- for (int y = 2; y < patternLength; y++) {
- array[y - 2] = array[y];
- }
- array[patternLength - 2] = 0;
- array[patternLength - 1] = 0;
- counterPosition--;
- } else {
- counterPosition++;
- }
- array[counterPosition] = 1;
- isWhite = !isWhite;
- }
- }
-
- return nil;
- }
-
- - (int)decodeCode:(ZXBitArray *)row counters:(ZXIntArray *)counters rowOffset:(int)rowOffset {
- if (![ZXOneDReader recordPattern:row start:rowOffset counters:counters]) {
- return -1;
- }
- float bestVariance = ZX_CODE128_MAX_AVG_VARIANCE;
- int bestMatch = -1;
-
- for (int d = 0; d < ZX_CODE128_CODE_PATTERNS_LEN; d++) {
- float variance = [ZXOneDReader patternMatchVariance:counters pattern:ZX_CODE128_CODE_PATTERNS[d] maxIndividualVariance:ZX_CODE128_MAX_INDIVIDUAL_VARIANCE];
- if (variance < bestVariance) {
- bestVariance = variance;
- bestMatch = d;
- }
- }
-
- if (bestMatch >= 0) {
- return bestMatch;
- } else {
- return -1;
- }
- }
-
- - (ZXResult *)decodeRow:(int)rowNumber row:(ZXBitArray *)row hints:(ZXDecodeHints *)hints error:(NSError **)error {
- BOOL convertFNC1 = hints && hints.assumeGS1;
-
- ZXIntArray *startPatternInfo = [self findStartPattern:row];
- if (!startPatternInfo) {
- if (error) *error = ZXNotFoundErrorInstance();
- return nil;
- }
-
- int startCode = startPatternInfo.array[2];
- int codeSet;
-
- NSMutableArray *rawCodes = [NSMutableArray arrayWithObject:@(startCode)];
-
- switch (startCode) {
- case ZX_CODE128_CODE_START_A:
- codeSet = ZX_CODE128_CODE_CODE_A;
- break;
- case ZX_CODE128_CODE_START_B:
- codeSet = ZX_CODE128_CODE_CODE_B;
- break;
- case ZX_CODE128_CODE_START_C:
- codeSet = ZX_CODE128_CODE_CODE_C;
- break;
- default:
- if (error) *error = ZXFormatErrorInstance();
- return nil;
- }
-
- BOOL done = NO;
- BOOL isNextShifted = NO;
-
- NSMutableString *result = [NSMutableString stringWithCapacity:20];
-
- int lastStart = startPatternInfo.array[0];
- int nextStart = startPatternInfo.array[1];
- ZXIntArray *counters = [[ZXIntArray alloc] initWithLength:6];
-
- int lastCode = 0;
- int code = 0;
- int checksumTotal = startCode;
- int multiplier = 0;
- BOOL lastCharacterWasPrintable = YES;
- BOOL upperMode = NO;
- BOOL shiftUpperMode = NO;
-
- while (!done) {
- BOOL unshift = isNextShifted;
- isNextShifted = NO;
-
- // Save off last code
- lastCode = code;
-
- // Decode another code from image
- code = [self decodeCode:row counters:counters rowOffset:nextStart];
- if (code == -1) {
- if (error) *error = ZXNotFoundErrorInstance();
- return nil;
- }
-
- [rawCodes addObject:@(code)];
-
- // Remember whether the last code was printable or not (excluding ZX_CODE128_CODE_STOP)
- if (code != ZX_CODE128_CODE_STOP) {
- lastCharacterWasPrintable = YES;
- }
-
- // Add to checksum computation (if not ZX_CODE128_CODE_STOP of course)
- if (code != ZX_CODE128_CODE_STOP) {
- multiplier++;
- checksumTotal += multiplier * code;
- }
-
- // Advance to where the next code will to start
- lastStart = nextStart;
- nextStart += [counters sum];
-
- // Take care of illegal start codes
- switch (code) {
- case ZX_CODE128_CODE_START_A:
- case ZX_CODE128_CODE_START_B:
- case ZX_CODE128_CODE_START_C:
- if (error) *error = ZXFormatErrorInstance();
- return nil;
- }
-
- switch (codeSet) {
- case ZX_CODE128_CODE_CODE_A:
- if (code < 64) {
- if (shiftUpperMode == upperMode) {
- [result appendFormat:@"%C", (unichar)(' ' + code)];
- } else {
- [result appendFormat:@"%C", (unichar)(' ' + code + 128)];
- }
- shiftUpperMode = NO;
- } else if (code < 96) {
- if (shiftUpperMode == upperMode) {
- [result appendFormat:@"%C", (unichar)(code - 64)];
- } else {
- [result appendFormat:@"%C", (unichar)(code + 64)];
- }
- shiftUpperMode = NO;
- } else {
- // Don't let CODE_STOP, which always appears, affect whether whether we think the last
- // code was printable or not.
- if (code != ZX_CODE128_CODE_STOP) {
- lastCharacterWasPrintable = NO;
- }
-
- switch (code) {
- case ZX_CODE128_CODE_FNC_1:
- if (convertFNC1) {
- if (result.length == 0) {
- // GS1 specification 5.4.3.7. and 5.4.6.4. If the first char after the start code
- // is FNC1 then this is GS1-128. We add the symbology identifier.
- [result appendString:@"]C1"];
- } else {
- // GS1 specification 5.4.7.5. Every subsequent FNC1 is returned as ASCII 29 (GS)
- [result appendFormat:@"%C", (unichar) 29];
- }
- }
- break;
- case ZX_CODE128_CODE_FNC_2:
- case ZX_CODE128_CODE_FNC_3:
- // do nothing?
- break;
- case ZX_CODE128_CODE_FNC_4_A:
- if (!upperMode && shiftUpperMode) {
- upperMode = YES;
- shiftUpperMode = NO;
- } else if (upperMode && shiftUpperMode) {
- upperMode = NO;
- shiftUpperMode = NO;
- } else {
- shiftUpperMode = YES;
- }
- break;
- case ZX_CODE128_CODE_SHIFT:
- isNextShifted = YES;
- codeSet = ZX_CODE128_CODE_CODE_B;
- break;
- case ZX_CODE128_CODE_CODE_B:
- codeSet = ZX_CODE128_CODE_CODE_B;
- break;
- case ZX_CODE128_CODE_CODE_C:
- codeSet = ZX_CODE128_CODE_CODE_C;
- break;
- case ZX_CODE128_CODE_STOP:
- done = YES;
- break;
- }
- }
- break;
- case ZX_CODE128_CODE_CODE_B:
- if (code < 96) {
- if (shiftUpperMode == upperMode) {
- [result appendFormat:@"%C", (unichar)(' ' + code)];
- } else {
- [result appendFormat:@"%C", (unichar)(' ' + code + 128)];
- }
- shiftUpperMode = NO;
- } else {
- if (code != ZX_CODE128_CODE_STOP) {
- lastCharacterWasPrintable = NO;
- }
-
- switch (code) {
- case ZX_CODE128_CODE_FNC_1:
- if (convertFNC1) {
- if (result.length == 0) {
- // GS1 specification 5.4.3.7. and 5.4.6.4. If the first char after the start code
- // is FNC1 then this is GS1-128. We add the symbology identifier.
- [result appendString:@"]C1"];
- } else {
- // GS1 specification 5.4.7.5. Every subsequent FNC1 is returned as ASCII 29 (GS)
- [result appendFormat:@"%C", (unichar) 29];
- }
- }
- break;
- case ZX_CODE128_CODE_FNC_2:
- case ZX_CODE128_CODE_FNC_3:
- // do nothing?
- break;
- case ZX_CODE128_CODE_FNC_4_B:
- if (!upperMode && shiftUpperMode) {
- upperMode = YES;
- shiftUpperMode = NO;
- } else if (upperMode && shiftUpperMode) {
- upperMode = NO;
- shiftUpperMode = NO;
- } else {
- shiftUpperMode = YES;
- }
- break;
- case ZX_CODE128_CODE_SHIFT:
- isNextShifted = YES;
- codeSet = ZX_CODE128_CODE_CODE_A;
- break;
- case ZX_CODE128_CODE_CODE_A:
- codeSet = ZX_CODE128_CODE_CODE_A;
- break;
- case ZX_CODE128_CODE_CODE_C:
- codeSet = ZX_CODE128_CODE_CODE_C;
- break;
- case ZX_CODE128_CODE_STOP:
- done = YES;
- break;
- }
- }
- break;
- case ZX_CODE128_CODE_CODE_C:
- if (code < 100) {
- if (code < 10) {
- [result appendString:@"0"];
- }
- [result appendFormat:@"%d", code];
- } else {
- if (code != ZX_CODE128_CODE_STOP) {
- lastCharacterWasPrintable = NO;
- }
-
- switch (code) {
- case ZX_CODE128_CODE_FNC_1:
- if (convertFNC1) {
- if (result.length == 0) {
- // GS1 specification 5.4.3.7. and 5.4.6.4. If the first char after the start code
- // is FNC1 then this is GS1-128. We add the symbology identifier.
- [result appendString:@"]C1"];
- } else {
- // GS1 specification 5.4.7.5. Every subsequent FNC1 is returned as ASCII 29 (GS)
- [result appendFormat:@"%C", (unichar) 29];
- }
- }
- break;
- case ZX_CODE128_CODE_CODE_A:
- codeSet = ZX_CODE128_CODE_CODE_A;
- break;
- case ZX_CODE128_CODE_CODE_B:
- codeSet = ZX_CODE128_CODE_CODE_B;
- break;
- case ZX_CODE128_CODE_STOP:
- done = YES;
- break;
- }
- }
- break;
- }
-
- // Unshift back to another code set if we were shifted
- if (unshift) {
- codeSet = codeSet == ZX_CODE128_CODE_CODE_A ? ZX_CODE128_CODE_CODE_B : ZX_CODE128_CODE_CODE_A;
- }
- }
-
- int lastPatternSize = nextStart - lastStart;
-
- // Check for ample whitespace following pattern, but, to do this we first need to remember that
- // we fudged decoding CODE_STOP since it actually has 7 bars, not 6. There is a black bar left
- // to read off. Would be slightly better to properly read. Here we just skip it:
- nextStart = [row nextUnset:nextStart];
- if (![row isRange:nextStart end:MIN(row.size, nextStart + (nextStart - lastStart) / 2) value:NO]) {
- if (error) *error = ZXNotFoundErrorInstance();
- return nil;
- }
-
- // Pull out from sum the value of the penultimate check code
- checksumTotal -= multiplier * lastCode;
- // lastCode is the checksum then:
- if (checksumTotal % 103 != lastCode) {
- if (error) *error = ZXChecksumErrorInstance();
- return nil;
- }
-
- // Need to pull out the check digits from string
- NSUInteger resultLength = [result length];
- if (resultLength == 0) {
- // false positive
- if (error) *error = ZXNotFoundErrorInstance();
- return nil;
- }
-
- // Only bother if the result had at least one character, and if the checksum digit happened to
- // be a printable character. If it was just interpreted as a control code, nothing to remove.
- if (resultLength > 0 && lastCharacterWasPrintable) {
- if (codeSet == ZX_CODE128_CODE_CODE_C) {
- [result deleteCharactersInRange:NSMakeRange(resultLength - 2, 2)];
- } else {
- [result deleteCharactersInRange:NSMakeRange(resultLength - 1, 1)];
- }
- }
-
- float left = (float)(startPatternInfo.array[1] + startPatternInfo.array[0]) / 2.0f;
- float right = lastStart + lastPatternSize / 2.0f;
-
- NSUInteger rawCodesSize = [rawCodes count];
- ZXByteArray *rawBytes = [[ZXByteArray alloc] initWithLength:(unsigned int)rawCodesSize];
- for (int i = 0; i < rawCodesSize; i++) {
- rawBytes.array[i] = (int8_t)[rawCodes[i] intValue];
- }
-
- return [ZXResult resultWithText:result
- rawBytes:rawBytes
- resultPoints:@[[[ZXResultPoint alloc] initWithX:left y:(float)rowNumber],
- [[ZXResultPoint alloc] initWithX:right y:(float)rowNumber]]
- format:kBarcodeFormatCode128];
- }
-
- @end
|