JLChen
2021-05-18 a869383e163a18cdedcf587383c1eca043129754
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
/*
 * 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 "ZXBoolArray.h"
#import "ZXCodaBarReader.h"
#import "ZXCodaBarWriter.h"
 
const unichar ZX_CODA_START_END_CHARS[] = {'A', 'B', 'C', 'D'};
const unichar ZX_CODA_ALT_START_END_CHARS[] = {'T', 'N', '*', 'E'};
const unichar ZX_CHARS_WHICH_ARE_TEN_LENGTH_EACH_AFTER_DECODED[] = {'/', ':', '+', '.'};
static unichar ZX_CODA_DEFAULT_GUARD;
 
@implementation ZXCodaBarWriter
 
+ (void)initialize {
  if ([self class] != [ZXCodaBarWriter class]) return;
 
  ZX_CODA_DEFAULT_GUARD = ZX_CODA_START_END_CHARS[0];
}
 
- (ZXBoolArray *)encode:(NSString *)contents {
  if ([contents length] < 2) {
    // Can't have a start/end guard, so tentatively add default guards
    contents = [NSString stringWithFormat:@"%C%@%C", ZX_CODA_DEFAULT_GUARD, contents, ZX_CODA_DEFAULT_GUARD];
  } else {
    // Verify input and calculate decoded length.
    unichar firstChar = [[contents uppercaseString] characterAtIndex:0];
    unichar lastChar = [[contents uppercaseString] characterAtIndex:contents.length - 1];
    BOOL startsNormal = [ZXCodaBarReader arrayContains:ZX_CODA_START_END_CHARS length:sizeof(ZX_CODA_START_END_CHARS) / sizeof(unichar) key:firstChar];
    BOOL endsNormal = [ZXCodaBarReader arrayContains:ZX_CODA_START_END_CHARS length:sizeof(ZX_CODA_START_END_CHARS) / sizeof(unichar) key:lastChar];
    BOOL startsAlt = [ZXCodaBarReader arrayContains:ZX_CODA_ALT_START_END_CHARS length:sizeof(ZX_CODA_ALT_START_END_CHARS) / sizeof(unichar) key:firstChar];
    BOOL endsAlt = [ZXCodaBarReader arrayContains:ZX_CODA_ALT_START_END_CHARS length:sizeof(ZX_CODA_ALT_START_END_CHARS) / sizeof(unichar) key:lastChar];
    if (startsNormal) {
      if (!endsNormal) {
        @throw [NSException exceptionWithName:NSInvalidArgumentException
                                       reason:[NSString stringWithFormat:@"Invalid start/end guards: %@", contents]
                                     userInfo:nil];
      }
      // else already has valid start/end
    } else if (startsAlt) {
      if (!endsAlt) {
        @throw [NSException exceptionWithName:NSInvalidArgumentException
                                       reason:[NSString stringWithFormat:@"Invalid start/end guards: %@", contents]
                                     userInfo:nil];
      }
      // else already has valid start/end
    } else {
      // Doesn't start with a guard
      if (endsNormal || endsAlt) {
        @throw [NSException exceptionWithName:NSInvalidArgumentException
                                       reason:[NSString stringWithFormat:@"Invalid start/end guards: %@", contents]
                                     userInfo:nil];
      }
      // else doesn't end with guard either, so add a default
      contents = [NSString stringWithFormat:@"%C%@%C", ZX_CODA_DEFAULT_GUARD, contents, ZX_CODA_DEFAULT_GUARD];
    }
  }
 
  // The start character and the end character are decoded to 10 length each.
  int resultLength = 20;
  for (int i = 1; i < contents.length - 1; i++) {
    if (([contents characterAtIndex:i] >= '0' && [contents characterAtIndex:i] <= '9') ||
        [contents characterAtIndex:i] == '-' || [contents characterAtIndex:i] == '$') {
      resultLength += 9;
    } else if ([ZXCodaBarReader arrayContains:ZX_CHARS_WHICH_ARE_TEN_LENGTH_EACH_AFTER_DECODED length:4 key:[contents characterAtIndex:i]]) {
      resultLength += 10;
    } else {
      @throw [NSException exceptionWithName:NSInvalidArgumentException
                                     reason:[NSString stringWithFormat:@"Cannot encode : '%C'", [contents characterAtIndex:i]]
                                   userInfo:nil];
    }
  }
  // A blank is placed between each character.
  resultLength += contents.length - 1;
 
  ZXBoolArray *result = [[ZXBoolArray alloc] initWithLength:resultLength];
  int position = 0;
  for (int index = 0; index < contents.length; index++) {
    unichar c = [[contents uppercaseString] characterAtIndex:index];
    if (index == 0 || index == contents.length - 1) {
      // The start/end chars are not in the CodaBarReader.ALPHABET.
      switch (c) {
        case 'T':
          c = 'A';
          break;
        case 'N':
          c = 'B';
          break;
        case '*':
          c = 'C';
          break;
        case 'E':
          c = 'D';
          break;
      }
    }
    int code = 0;
    for (int i = 0; i < ZX_CODA_ALPHABET_LEN; i++) {
      // Found any, because I checked above.
      if (c == ZX_CODA_ALPHABET[i]) {
        code = ZX_CODA_CHARACTER_ENCODINGS[i];
        break;
      }
    }
    BOOL color = YES;
    int counter = 0;
    int bit = 0;
    while (bit < 7) { // A character consists of 7 digit.
      result.array[position] = color;
      position++;
      if (((code >> (6 - bit)) & 1) == 0 || counter == 1) {
        color = !color; // Flip the color.
        bit++;
        counter = 0;
      } else {
        counter++;
      }
    }
    if (index < contents.length - 1) {
      result.array[position] = NO;
      position++;
    }
  }
  return result;
}
 
@end