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