123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294 |
- /*
- * Copyright 2014 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 "ZXAztecHighLevelEncoder.h"
- #import "ZXAztecState.h"
- #import "ZXByteArray.h"
-
- NSArray *ZX_AZTEC_MODE_NAMES = nil;
-
- const int ZX_AZTEC_MODE_UPPER = 0; // 5 bits
- const int ZX_AZTEC_MODE_LOWER = 1; // 5 bits
- const int ZX_AZTEC_MODE_DIGIT = 2; // 4 bits
- const int ZX_AZTEC_MODE_MIXED = 3; // 5 bits
- const int ZX_AZTEC_MODE_PUNCT = 4; // 5 bits
-
- // The Latch Table shows, for each pair of Modes, the optimal method for
- // getting from one mode to another. In the worst possible case, this can
- // be up to 14 bits. In the best possible case, we are already there!
- // The high half-word of each entry gives the number of bits.
- // The low half-word of each entry are the actual bits necessary to change
- const int ZX_AZTEC_LATCH_TABLE[][5] = {
- {
- 0,
- (5 << 16) + 28, // UPPER -> LOWER
- (5 << 16) + 30, // UPPER -> DIGIT
- (5 << 16) + 29, // UPPER -> MIXED
- (10 << 16) + (29 << 5) + 30, // UPPER -> MIXED -> PUNCT
- },
- {
- (9 << 16) + (30 << 4) + 14, // LOWER -> DIGIT -> UPPER
- 0,
- (5 << 16) + 30, // LOWER -> DIGIT
- (5 << 16) + 29, // LOWER -> MIXED
- (10 << 16) + (29 << 5) + 30, // LOWER -> MIXED -> PUNCT
- },
- {
- (4 << 16) + 14, // DIGIT -> UPPER
- (9 << 16) + (14 << 5) + 28, // DIGIT -> UPPER -> LOWER
- 0,
- (9 << 16) + (14 << 5) + 29, // DIGIT -> UPPER -> MIXED
- (14 << 16) + (14 << 10) + (29 << 5) + 30,
- // DIGIT -> UPPER -> MIXED -> PUNCT
- },
- {
- (5 << 16) + 29, // MIXED -> UPPER
- (5 << 16) + 28, // MIXED -> LOWER
- (10 << 16) + (29 << 5) + 30, // MIXED -> UPPER -> DIGIT
- 0,
- (5 << 16) + 30, // MIXED -> PUNCT
- },
- {
- (5 << 16) + 31, // PUNCT -> UPPER
- (10 << 16) + (31 << 5) + 28, // PUNCT -> UPPER -> LOWER
- (10 << 16) + (31 << 5) + 30, // PUNCT -> UPPER -> DIGIT
- (10 << 16) + (31 << 5) + 29, // PUNCT -> UPPER -> MIXED
- 0,
- },
- };
-
- // A reverse mapping from [mode][char] to the encoding for that character
- // in that mode. An entry of 0 indicates no mapping exists.
- const int ZX_AZTEC_CHAR_MAP_HEIGHT = 5;
- const int ZX_AZTEC_CHAR_MAP_WIDTH = 256;
- static int ZX_AZTEC_CHAR_MAP[ZX_AZTEC_CHAR_MAP_HEIGHT][ZX_AZTEC_CHAR_MAP_WIDTH];
-
- // A map showing the available shift codes. (The shifts to BINARY are not
- // shown
- int ZX_AZTEC_SHIFT_TABLE[ZX_AZTEC_SHIFT_TABLE_SIZE][ZX_AZTEC_SHIFT_TABLE_SIZE];
-
- @interface ZXAztecHighLevelEncoder ()
-
- @property (nonatomic, assign, readonly) ZXByteArray *text;
-
- @end
-
- @implementation ZXAztecHighLevelEncoder
-
- + (void)load {
- ZX_AZTEC_MODE_NAMES = @[@"UPPER", @"LOWER", @"DIGIT", @"MIXED", @"PUNCT"];
-
- memset(ZX_AZTEC_CHAR_MAP, 0, ZX_AZTEC_CHAR_MAP_HEIGHT * ZX_AZTEC_CHAR_MAP_WIDTH * sizeof(int));
- ZX_AZTEC_CHAR_MAP[ZX_AZTEC_MODE_UPPER][' '] = 1;
- for (int c = 'A'; c <= 'Z'; c++) {
- ZX_AZTEC_CHAR_MAP[ZX_AZTEC_MODE_UPPER][c] = c - 'A' + 2;
- }
- ZX_AZTEC_CHAR_MAP[ZX_AZTEC_MODE_LOWER][' '] = 1;
- for (int c = 'a'; c <= 'z'; c++) {
- ZX_AZTEC_CHAR_MAP[ZX_AZTEC_MODE_LOWER][c] = c - 'a' + 2;
- }
- ZX_AZTEC_CHAR_MAP[ZX_AZTEC_MODE_DIGIT][' '] = 1;
- for (int c = '0'; c <= '9'; c++) {
- ZX_AZTEC_CHAR_MAP[ZX_AZTEC_MODE_DIGIT][c] = c - '0' + 2;
- }
- ZX_AZTEC_CHAR_MAP[ZX_AZTEC_MODE_DIGIT][','] = 12;
- ZX_AZTEC_CHAR_MAP[ZX_AZTEC_MODE_DIGIT]['.'] = 13;
-
- const int mixedTable[] = {
- '\0', ' ', '\1', '\2', '\3', '\4', '\5', '\6', '\7', '\b', '\t', '\n',
- '\13', '\f', '\r', '\33', '\34', '\35', '\36', '\37', '@', '\\', '^',
- '_', '`', '|', '~', '\177'
- };
- for (int i = 0; i < sizeof(mixedTable) / sizeof(int); i++) {
- ZX_AZTEC_CHAR_MAP[ZX_AZTEC_MODE_MIXED][mixedTable[i]] = i;
- }
-
- const int punctTable[] = {
- '\0', '\r', '\0', '\0', '\0', '\0', '!', '\'', '#', '$', '%', '&', '\'',
- '(', ')', '*', '+', ',', '-', '.', '/', ':', ';', '<', '=', '>', '?',
- '[', ']', '{', '}'
- };
- for (int i = 0; i < sizeof(punctTable) / sizeof(int); i++) {
- if (punctTable[i] > 0) {
- ZX_AZTEC_CHAR_MAP[ZX_AZTEC_MODE_PUNCT][punctTable[i]] = i;
- }
- }
-
- memset(ZX_AZTEC_SHIFT_TABLE, -1, ZX_AZTEC_SHIFT_TABLE_SIZE * ZX_AZTEC_SHIFT_TABLE_SIZE * sizeof(int));
- ZX_AZTEC_SHIFT_TABLE[ZX_AZTEC_MODE_UPPER][ZX_AZTEC_MODE_PUNCT] = 0;
-
- ZX_AZTEC_SHIFT_TABLE[ZX_AZTEC_MODE_LOWER][ZX_AZTEC_MODE_PUNCT] = 0;
- ZX_AZTEC_SHIFT_TABLE[ZX_AZTEC_MODE_LOWER][ZX_AZTEC_MODE_UPPER] = 28;
-
- ZX_AZTEC_SHIFT_TABLE[ZX_AZTEC_MODE_MIXED][ZX_AZTEC_MODE_PUNCT] = 0;
-
- ZX_AZTEC_SHIFT_TABLE[ZX_AZTEC_MODE_DIGIT][ZX_AZTEC_MODE_PUNCT] = 0;
- ZX_AZTEC_SHIFT_TABLE[ZX_AZTEC_MODE_DIGIT][ZX_AZTEC_MODE_UPPER] = 15;
- }
-
- - (id)initWithText:(ZXByteArray *)text {
- if (self = [super init]) {
- _text = text;
- }
-
- return self;
- }
-
- - (ZXBitArray *)encode {
- NSArray *states = @[[ZXAztecState initialState]];
- for (int index = 0; index < self.text.length; index++) {
- int pairCode;
- int nextChar = index + 1 < self.text.length ? self.text.array[index + 1] : 0;
- switch (self.text.array[index]) {
- case '\r':
- pairCode = nextChar == '\n' ? 2 : 0;
- break;
- case '.' :
- pairCode = nextChar == ' ' ? 3 : 0;
- break;
- case ',' :
- pairCode = nextChar == ' ' ? 4 : 0;
- break;
- case ':' :
- pairCode = nextChar == ' ' ? 5 : 0;
- break;
- default:
- pairCode = 0;
- }
- if (pairCode > 0) {
- // We have one of the four special PUNCT pairs. Treat them specially.
- // Get a new set of states for the two new characters.
- states = [self updateStateListForPair:states index:index pairCode:pairCode];
- index++;
- } else {
- // Get a new set of states for the new character.
- states = [self updateStateListForChar:states index:index];
- }
- }
- // We are left with a set of states. Find the shortest one.
- ZXAztecState *minState = [[states sortedArrayUsingComparator:^NSComparisonResult(ZXAztecState *a, ZXAztecState *b) {
- return a.bitCount - b.bitCount;
- }] firstObject];
- // Convert it to a bit array, and return.
- return [minState toBitArray:self.text];
- }
-
- // We update a set of states for a new character by updating each state
- // for the new character, merging the results, and then removing the
- // non-optimal states.
- - (NSArray *)updateStateListForChar:(NSArray *)states index:(int)index {
- NSMutableArray *result = [NSMutableArray array];
- for (ZXAztecState *state in states) {
- [self updateStateForChar:state index:index result:result];
- }
- return [self simplifyStates:result];
- }
-
- // Return a set of states that represent the possible ways of updating this
- // state for the next character. The resulting set of states are added to
- // the "result" list.
- - (void)updateStateForChar:(ZXAztecState *)state index:(int)index result:(NSMutableArray *)result {
- unichar ch = (unichar) (self.text.array[index] & 0xFF);
- BOOL charInCurrentTable = ZX_AZTEC_CHAR_MAP[state.mode][ch] > 0;
- ZXAztecState *stateNoBinary = nil;
- for (int mode = 0; mode <= ZX_AZTEC_MODE_PUNCT; mode++) {
- int charInMode = ZX_AZTEC_CHAR_MAP[mode][ch];
- if (charInMode > 0) {
- if (!stateNoBinary) {
- // Only create stateNoBinary the first time it's required.
- stateNoBinary = [state endBinaryShift:index];
- }
- // Try generating the character by latching to its mode
- if (!charInCurrentTable || mode == state.mode || mode == ZX_AZTEC_MODE_DIGIT) {
- // If the character is in the current table, we don't want to latch to
- // any other mode except possibly digit (which uses only 4 bits). Any
- // other latch would be equally successful *after* this character, and
- // so wouldn't save any bits.
- ZXAztecState *latch_state = [stateNoBinary latchAndAppend:mode value:charInMode];
- [result addObject:latch_state];
- }
- // Try generating the character by switching to its mode.
- if (!charInCurrentTable && ZX_AZTEC_SHIFT_TABLE[state.mode][mode] >= 0) {
- // It never makes sense to temporarily shift to another mode if the
- // character exists in the current mode. That can never save bits.
- ZXAztecState *shift_state = [stateNoBinary shiftAndAppend:mode value:charInMode];
- [result addObject:shift_state];
- }
- }
- }
- if (state.binaryShiftByteCount > 0 || ZX_AZTEC_CHAR_MAP[state.mode][ch] == 0) {
- // It's never worthwhile to go into binary shift mode if you're not already
- // in binary shift mode, and the character exists in your current mode.
- // That can never save bits over just outputting the char in the current mode.
- ZXAztecState *binaryState = [state addBinaryShiftChar:index];
- [result addObject:binaryState];
- }
- }
-
- - (NSArray *)updateStateListForPair:(NSArray *)states index:(int)index pairCode:(int)pairCode {
- NSMutableArray *result = [NSMutableArray array];
- for (ZXAztecState *state in states) {
- [self updateStateForPair:state index:index pairCode:pairCode result:result];
- }
- return [self simplifyStates:result];
- }
-
- - (void)updateStateForPair:(ZXAztecState *)state index:(int)index pairCode:(int)pairCode result:(NSMutableArray *)result {
- ZXAztecState *stateNoBinary = [state endBinaryShift:index];
- // Possibility 1. Latch to ZX_AZTEC_MODE_PUNCT, and then append this code
- [result addObject:[stateNoBinary latchAndAppend:ZX_AZTEC_MODE_PUNCT value:pairCode]];
- if (state.mode != ZX_AZTEC_MODE_PUNCT) {
- // Possibility 2. Shift to ZX_AZTEC_MODE_PUNCT, and then append this code.
- // Every state except ZX_AZTEC_MODE_PUNCT (handled above) can shift
- [result addObject:[stateNoBinary shiftAndAppend:ZX_AZTEC_MODE_PUNCT value:pairCode]];
- }
- if (pairCode == 3 || pairCode == 4) {
- // both characters are in DIGITS. Sometimes better to just add two digits
- ZXAztecState *digit_state = [[stateNoBinary
- latchAndAppend:ZX_AZTEC_MODE_DIGIT value:16 - pairCode] // period or comma in DIGIT
- latchAndAppend:ZX_AZTEC_MODE_DIGIT value:1]; // space in DIGIT
- [result addObject:digit_state];
- }
- if (state.binaryShiftByteCount > 0) {
- // It only makes sense to do the characters as binary if we're already
- // in binary mode.
- ZXAztecState *binaryState = [[state addBinaryShiftChar:index] addBinaryShiftChar:index + 1];
- [result addObject:binaryState];
- }
- }
-
- - (NSArray *)simplifyStates:(NSArray *)states {
- NSMutableArray *result = [NSMutableArray array];
- for (ZXAztecState *newState in states) {
- BOOL add = YES;
- NSArray *resultCopy = [NSArray arrayWithArray:result];
- for (ZXAztecState *oldState in resultCopy) {
- if ([oldState isBetterThanOrEqualTo:newState]) {
- add = NO;
- break;
- }
- if ([newState isBetterThanOrEqualTo:oldState]) {
- [result removeObject:oldState];
- }
- }
- if (add) {
- [result addObject:newState];
- }
- }
- return result;
- }
-
- @end
|