• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2015 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 package com.google.zxing.oned;
17 
18 import com.google.zxing.BarcodeFormat;
19 
20 import java.util.Collection;
21 import java.util.Collections;
22 
23 /**
24  * This object renders a CODE93 code as a BitMatrix
25  */
26 public class Code93Writer extends OneDimensionalCodeWriter {
27 
28   @Override
getSupportedWriteFormats()29   protected Collection<BarcodeFormat> getSupportedWriteFormats() {
30     return Collections.singleton(BarcodeFormat.CODE_93);
31   }
32 
33   /**
34    * @param contents barcode contents to encode. It should not be encoded for extended characters.
35    * @return a {@code boolean[]} of horizontal pixels (false = white, true = black)
36    */
37   @Override
encode(String contents)38   public boolean[] encode(String contents) {
39     contents = convertToExtended(contents);
40     int length = contents.length();
41     if (length > 80) {
42       throw new IllegalArgumentException("Requested contents should be less than 80 digits long after " +
43           "converting to extended encoding, but got " + length);
44     }
45 
46     //length of code + 2 start/stop characters + 2 checksums, each of 9 bits, plus a termination bar
47     int codeWidth = (contents.length() + 2 + 2) * 9 + 1;
48 
49     boolean[] result = new boolean[codeWidth];
50 
51     //start character (*)
52     int pos = appendPattern(result, 0, Code93Reader.ASTERISK_ENCODING);
53 
54     for (int i = 0; i < length; i++) {
55       int indexInString = Code93Reader.ALPHABET_STRING.indexOf(contents.charAt(i));
56       pos += appendPattern(result, pos, Code93Reader.CHARACTER_ENCODINGS[indexInString]);
57     }
58 
59     //add two checksums
60     int check1 = computeChecksumIndex(contents, 20);
61     pos += appendPattern(result, pos, Code93Reader.CHARACTER_ENCODINGS[check1]);
62 
63     //append the contents to reflect the first checksum added
64     contents += Code93Reader.ALPHABET_STRING.charAt(check1);
65 
66     int check2 = computeChecksumIndex(contents, 15);
67     pos += appendPattern(result, pos, Code93Reader.CHARACTER_ENCODINGS[check2]);
68 
69     //end character (*)
70     pos += appendPattern(result, pos, Code93Reader.ASTERISK_ENCODING);
71 
72     //termination bar (single black bar)
73     result[pos] = true;
74 
75     return result;
76   }
77 
78   /**
79    * @param target output to append to
80    * @param pos start position
81    * @param pattern pattern to append
82    * @param startColor unused
83    * @return 9
84    * @deprecated without replacement; intended as an internal-only method
85    */
86   @Deprecated
appendPattern(boolean[] target, int pos, int[] pattern, boolean startColor)87   protected static int appendPattern(boolean[] target, int pos, int[] pattern, boolean startColor) {
88     for (int bit : pattern) {
89       target[pos++] = bit != 0;
90     }
91     return 9;
92   }
93 
appendPattern(boolean[] target, int pos, int a)94   private static int appendPattern(boolean[] target, int pos, int a) {
95     for (int i = 0; i < 9; i++) {
96       int temp = a & (1 << (8 - i));
97       target[pos + i] = temp != 0;
98     }
99     return 9;
100   }
101 
computeChecksumIndex(String contents, int maxWeight)102   private static int computeChecksumIndex(String contents, int maxWeight) {
103     int weight = 1;
104     int total = 0;
105 
106     for (int i = contents.length() - 1; i >= 0; i--) {
107       int indexInString = Code93Reader.ALPHABET_STRING.indexOf(contents.charAt(i));
108       total += indexInString * weight;
109       if (++weight > maxWeight) {
110         weight = 1;
111       }
112     }
113     return total % 47;
114   }
115 
convertToExtended(String contents)116   static String convertToExtended(String contents) {
117     int length = contents.length();
118     StringBuilder extendedContent = new StringBuilder(length * 2);
119     for (int i = 0; i < length; i++) {
120       char character = contents.charAt(i);
121       // ($)=a, (%)=b, (/)=c, (+)=d. see Code93Reader.ALPHABET_STRING
122       if (character == 0) {
123         // NUL: (%)U
124         extendedContent.append("bU");
125       } else if (character <= 26) {
126         // SOH - SUB: ($)A - ($)Z
127         extendedContent.append('a');
128         extendedContent.append((char) ('A' + character - 1));
129       } else if (character <= 31) {
130         // ESC - US: (%)A - (%)E
131         extendedContent.append('b');
132         extendedContent.append((char) ('A' + character - 27));
133       } else if (character == ' ' || character == '$' || character == '%' || character == '+') {
134         // space $ % +
135         extendedContent.append(character);
136       } else if (character <= ',') {
137         // ! " # & ' ( ) * ,: (/)A - (/)L
138         extendedContent.append('c');
139         extendedContent.append((char) ('A' + character - '!'));
140       } else if (character <= '9') {
141         extendedContent.append(character);
142       } else if (character == ':') {
143         // :: (/)Z
144         extendedContent.append("cZ");
145       } else if (character <= '?') {
146         // ; - ?: (%)F - (%)J
147         extendedContent.append('b');
148         extendedContent.append((char) ('F' + character - ';'));
149       } else if (character == '@') {
150         // @: (%)V
151         extendedContent.append("bV");
152       } else if (character <= 'Z') {
153         // A - Z
154         extendedContent.append(character);
155       } else if (character <= '_') {
156         // [ - _: (%)K - (%)O
157         extendedContent.append('b');
158         extendedContent.append((char) ('K' + character - '['));
159       } else if (character == '`') {
160         // `: (%)W
161         extendedContent.append("bW");
162       } else if (character <= 'z') {
163         // a - z: (*)A - (*)Z
164         extendedContent.append('d');
165         extendedContent.append((char) ('A' + character - 'a'));
166       } else if (character <= 127) {
167         // { - DEL: (%)P - (%)T
168         extendedContent.append('b');
169         extendedContent.append((char) ('P' + character - '{'));
170       } else {
171         throw new IllegalArgumentException(
172           "Requested content contains a non-encodable character: '" + character + "'");
173       }
174     }
175     return extendedContent.toString();
176   }
177 
178 }
179