001 /** 002 * This library is free software; you can redistribute it and/or modify it 003 * under the terms of the GNU Lesser General Public License (LGPL) as 004 * published by the Free Software Foundation; either version 3.0 of the 005 * License, or (at your option) any later version. 006 * 007 * This library is distributed in the hope that it will be useful, but 008 * WITHOUT ANY WARRANTY; without even the implied warranty of 009 * MERCHANTABILITY of FITNESS FOR A PARTICULAR PURPOSE. See the GNU 010 * Lesser General Public License for more details. 011 */ 012 013 /** 014 * Title: JBarcodeBean 015 * Description: Barcode JavaBeans Component 016 * Copyright: Copyright (C) 2004 017 * Company: Dafydd Walters 018 */ 019 package net.sourceforge.jbarcodebean.model; 020 021 import net.sourceforge.jbarcodebean.BarcodeException; 022 023 /** 024 * Code 128 barcode strategy implementation. This format can encode the 025 * full 128 character ASCII character set (from 0 to 127 decimal), plus 026 * four special Code 128 function codes. These four codes are defined 027 * as constant class members for convenience: FNC_1 through FNC_4. 028 */ 029 public class Code128 extends AbstractBarcodeStrategy { 030 031 /** Code 128 FUNCTION CODE 1 */ 032 public static final char FNC_1 = '\u0080'; 033 /** Code 128 FUNCTION CODE 2 */ 034 public static final char FNC_2 = '\u0081'; 035 /** Code 128 FUNCTION CODE 3 */ 036 public static final char FNC_3 = '\u0082'; 037 /** Code 128 FUNCTION CODE 4 */ 038 public static final char FNC_4 = '\u0083'; 039 040 private static final char START_A = '\u0084'; 041 private static final char START_B = '\u0085'; 042 private static final char START_C = '\u0086'; 043 044 private static final char MODE_A = '\u0087'; 045 private static final char MODE_B = '\u0088'; 046 private static final char MODE_C = '\u0089'; 047 048 private static final char SHIFT = '\u008A'; 049 private static final char STOP = '\u008B'; 050 051 /** 052 * A static array of 053 * {@link AbstractBarcodeStrategy.CharacterCode CharacterCode} objects 054 * for Code 128. The 055 * {@link AbstractBarcodeStrategy#getCodes() getCodes()} method 056 * returns this array. 057 * 058 * The <tt>character</tt> member of the elements in this array corresponds to the 059 * MODE B version of the character. 060 */ 061 protected static CharacterCode[] codes = { 062 new CharacterCode(' ', new byte[] {2,1,2,2,2,2}, 0), 063 new CharacterCode('!', new byte[] {2,2,2,1,2,2}, 1), 064 new CharacterCode('"', new byte[] {2,2,2,2,2,1}, 2), 065 new CharacterCode('#', new byte[] {1,2,1,2,2,3}, 3), 066 new CharacterCode('$', new byte[] {1,2,1,3,2,2}, 4), 067 new CharacterCode('%', new byte[] {1,3,1,2,2,2}, 5), 068 new CharacterCode('&', new byte[] {1,2,2,2,1,3}, 6), 069 new CharacterCode('\'', new byte[] {1,2,2,3,1,2}, 7), 070 new CharacterCode('(', new byte[] {1,3,2,2,1,2}, 8), 071 new CharacterCode(')', new byte[] {2,2,1,2,1,3}, 9), 072 new CharacterCode('*', new byte[] {2,2,1,3,1,2}, 10), 073 new CharacterCode('+', new byte[] {2,3,1,2,1,2}, 11), 074 new CharacterCode(',', new byte[] {1,1,2,2,3,2}, 12), 075 new CharacterCode('-', new byte[] {1,2,2,1,3,2}, 13), 076 new CharacterCode('.', new byte[] {1,2,2,2,3,1}, 14), 077 new CharacterCode('/', new byte[] {1,1,3,2,2,2}, 15), 078 new CharacterCode('0', new byte[] {1,2,3,1,2,2}, 16), 079 new CharacterCode('1', new byte[] {1,2,3,2,2,1}, 17), 080 new CharacterCode('2', new byte[] {2,2,3,2,1,1}, 18), 081 new CharacterCode('3', new byte[] {2,2,1,1,3,2}, 19), 082 new CharacterCode('4', new byte[] {2,2,1,2,3,1}, 20), 083 new CharacterCode('5', new byte[] {2,1,3,2,1,2}, 21), 084 new CharacterCode('6', new byte[] {2,2,3,1,1,2}, 22), 085 new CharacterCode('7', new byte[] {3,1,2,1,3,1}, 23), 086 new CharacterCode('8', new byte[] {3,1,1,2,2,2}, 24), 087 new CharacterCode('9', new byte[] {3,2,1,1,2,2}, 25), 088 new CharacterCode(':', new byte[] {3,2,1,2,2,1}, 26), 089 new CharacterCode(';', new byte[] {3,1,2,2,1,2}, 27), 090 new CharacterCode('<', new byte[] {3,2,2,1,1,2}, 28), 091 new CharacterCode('=', new byte[] {3,2,2,2,1,1}, 29), 092 new CharacterCode('>', new byte[] {2,1,2,1,2,3}, 30), 093 new CharacterCode('?', new byte[] {2,1,2,3,2,1}, 31), 094 new CharacterCode('@', new byte[] {2,3,2,1,2,1}, 32), 095 new CharacterCode('A', new byte[] {1,1,1,3,2,3}, 33), 096 new CharacterCode('B', new byte[] {1,3,1,1,2,3}, 34), 097 new CharacterCode('C', new byte[] {1,3,1,3,2,1}, 35), 098 new CharacterCode('D', new byte[] {1,1,2,3,1,3}, 36), 099 new CharacterCode('E', new byte[] {1,3,2,1,1,3}, 37), 100 new CharacterCode('F', new byte[] {1,3,2,3,1,1}, 38), 101 new CharacterCode('G', new byte[] {2,1,1,3,1,3}, 39), 102 new CharacterCode('H', new byte[] {2,3,1,1,1,3}, 40), 103 new CharacterCode('I', new byte[] {2,3,1,3,1,1}, 41), 104 new CharacterCode('J', new byte[] {1,1,2,1,3,3}, 42), 105 new CharacterCode('K', new byte[] {1,1,2,3,3,1}, 43), 106 new CharacterCode('L', new byte[] {1,3,2,1,3,1}, 44), 107 new CharacterCode('M', new byte[] {1,1,3,1,2,3}, 45), 108 new CharacterCode('N', new byte[] {1,1,3,3,2,1}, 46), 109 new CharacterCode('O', new byte[] {1,3,3,1,2,1}, 47), 110 new CharacterCode('P', new byte[] {3,1,3,1,2,1}, 48), 111 new CharacterCode('Q', new byte[] {2,1,1,3,3,1}, 49), 112 new CharacterCode('R', new byte[] {2,3,1,1,3,1}, 50), 113 new CharacterCode('S', new byte[] {2,1,3,1,1,3}, 51), 114 new CharacterCode('T', new byte[] {2,1,3,3,1,1}, 52), 115 new CharacterCode('U', new byte[] {2,1,3,1,3,1}, 53), 116 new CharacterCode('V', new byte[] {3,1,1,1,2,3}, 54), 117 new CharacterCode('W', new byte[] {3,1,1,3,2,1}, 55), 118 new CharacterCode('X', new byte[] {3,3,1,1,2,1}, 56), 119 new CharacterCode('Y', new byte[] {3,1,2,1,1,3}, 57), 120 new CharacterCode('Z', new byte[] {3,1,2,3,1,1}, 58), 121 new CharacterCode('[', new byte[] {3,3,2,1,1,1}, 59), 122 new CharacterCode('\\', new byte[] {3,1,4,1,1,1}, 60), 123 new CharacterCode(']', new byte[] {2,2,1,4,1,1}, 61), 124 new CharacterCode('^', new byte[] {4,3,1,1,1,1}, 62), 125 new CharacterCode('_', new byte[] {1,1,1,2,2,4}, 63), 126 new CharacterCode('`', new byte[] {1,1,1,4,2,2}, 64), 127 new CharacterCode('a', new byte[] {1,2,1,1,2,4}, 65), 128 new CharacterCode('b', new byte[] {1,2,1,4,2,1}, 66), 129 new CharacterCode('c', new byte[] {1,4,1,1,2,2}, 67), 130 new CharacterCode('d', new byte[] {1,4,1,2,2,1}, 68), 131 new CharacterCode('e', new byte[] {1,1,2,2,1,4}, 69), 132 new CharacterCode('f', new byte[] {1,1,2,4,1,2}, 70), 133 new CharacterCode('g', new byte[] {1,2,2,1,1,4}, 71), 134 new CharacterCode('h', new byte[] {1,2,2,4,1,1}, 72), 135 new CharacterCode('i', new byte[] {1,4,2,1,1,2}, 73), 136 new CharacterCode('j', new byte[] {1,4,2,2,1,1}, 74), 137 new CharacterCode('k', new byte[] {2,4,1,2,1,1}, 75), 138 new CharacterCode('l', new byte[] {2,2,1,1,1,4}, 76), 139 new CharacterCode('m', new byte[] {4,1,3,1,1,1}, 77), 140 new CharacterCode('n', new byte[] {2,4,1,1,1,2}, 78), 141 new CharacterCode('o', new byte[] {1,3,4,1,1,1}, 79), 142 new CharacterCode('p', new byte[] {1,1,1,2,4,2}, 80), 143 new CharacterCode('q', new byte[] {1,2,1,1,4,2}, 81), 144 new CharacterCode('r', new byte[] {1,2,1,2,4,1}, 82), 145 new CharacterCode('s', new byte[] {1,1,4,2,1,2}, 83), 146 new CharacterCode('t', new byte[] {1,2,4,1,1,2}, 84), 147 new CharacterCode('u', new byte[] {1,2,4,2,1,1}, 85), 148 new CharacterCode('v', new byte[] {4,1,1,2,1,2}, 86), 149 new CharacterCode('w', new byte[] {4,2,1,1,1,2}, 87), 150 new CharacterCode('x', new byte[] {4,2,1,2,1,1}, 88), 151 new CharacterCode('y', new byte[] {2,1,2,1,4,1}, 89), 152 new CharacterCode('z', new byte[] {2,1,4,1,2,1}, 90), 153 new CharacterCode('{', new byte[] {4,1,2,1,2,1}, 91), 154 new CharacterCode('|', new byte[] {1,1,1,1,4,3}, 92), 155 new CharacterCode('}', new byte[] {1,1,1,3,4,1}, 93), 156 new CharacterCode('~', new byte[] {1,3,1,1,4,1}, 94), 157 new CharacterCode('\u007F', new byte[] {1,1,4,1,1,3}, 95), 158 new CharacterCode(FNC_3, new byte[] {1,1,4,3,1,1}, 96), 159 new CharacterCode(FNC_2, new byte[] {4,1,1,1,1,3}, 97), 160 new CharacterCode(SHIFT, new byte[] {4,1,1,3,1,1}, 98), 161 new CharacterCode(MODE_C, new byte[] {1,1,3,1,4,1}, 99), 162 new CharacterCode(FNC_4, new byte[] {1,1,4,1,3,1}, 100), 163 new CharacterCode(MODE_A, new byte[] {3,1,1,1,4,1}, 101), 164 new CharacterCode(FNC_1, new byte[] {4,1,1,1,3,1}, 102), 165 new CharacterCode(START_A, new byte[] {2,1,1,4,1,2}, 103), 166 new CharacterCode(START_B, new byte[] {2,1,1,2,1,4}, 104), 167 new CharacterCode(START_C, new byte[] {2,1,1,2,3,2}, 105), 168 new CharacterCode(STOP, new byte[] {2,3,3,1,1,1,2}, 106) 169 }; 170 171 /** 172 * Always returns {@link BarcodeStrategy#MANDATORY_CHECKSUM}. 173 */ 174 public int requiresChecksum() { 175 // Checksum is not mandatory 176 return MANDATORY_CHECKSUM; 177 } 178 179 /** 180 * This implementation of <tt>getCodes</tt> returns an array of 181 * {@link AbstractBarcodeStrategy.CharacterCode CharacterCode} objects 182 * for the Code 128 format. 183 */ 184 protected CharacterCode[] getCodes() { 185 return codes; 186 } 187 188 /** 189 * Returns the <tt>text</tt> parameter with function characters and 190 * control codes stripped out. 191 */ 192 protected String getBarcodeLabelText(String text) { 193 char ch; 194 String label = new String(); 195 196 for (int i = 0; i < text.length(); i++) { 197 ch = text.charAt(i); 198 if (ch >= ' ' && ch <= '~') { 199 label += ch; 200 } 201 } 202 203 return label; 204 } 205 206 /** 207 * Always returns 11 (eleven). 208 */ 209 protected byte getMarginWidth() { 210 return 11; 211 } 212 213 /** 214 * Always returns <tt>0xffff</tt>, signalling to <tt><b>AbstractBarcodeStrategy</b></tt> 215 * superclass that it should not prefix a standard start character. In 216 * Code 128 there are three possible start characters, and the 217 * <tt><b>preprocess</b></tt> method handles the insertion of the correct 218 * start code instead. 219 */ 220 protected char getStartSentinel() { 221 return 0xffff; 222 } 223 224 /** 225 * Always returns the Code 128 STOP character. 226 */ 227 protected char getStopSentinel() { 228 return STOP; 229 } 230 231 /** 232 * Always returns <tt>false</tt>. 233 */ 234 protected boolean isInterleaved() { 235 return false; 236 } 237 238 /** 239 * Inserts start character and code change characters. 240 */ 241 protected String preprocess(String text) throws BarcodeException { 242 243 String preprocessed = new String(); 244 char mode = 0; 245 char startFunction = 0; 246 char c1 = 0; 247 char c2 = 0; 248 249 // Base the choice of start character on the first two characters 250 // after the optional function character. 251 252 c1 = text.charAt(0); 253 if (c1 == FNC_1 || c1 == FNC_2 || c1 == FNC_3 || c1 == FNC_4) { 254 startFunction = c1; 255 c1 = text.charAt(1); 256 if (text.length() > 2) { 257 c2 = text.charAt(2); 258 } 259 } 260 else { 261 if (text.length() > 1) { 262 c2 = text.charAt(1); 263 } 264 } 265 266 if (c1 >= '0' && c1 <= '9' && c2 >= '0' && c2 <= '9' && 267 startFunction != FNC_2 && startFunction != FNC_3 && 268 startFunction != FNC_4) { 269 // Use "C" start character. 270 preprocessed += START_C; 271 mode = MODE_C; 272 } 273 else if (c1 >= ' ' && c1 <= '\u007f') { 274 // Use "B" start character. 275 preprocessed += START_B; 276 mode = MODE_B; 277 } 278 else { 279 // Use "A" start character. 280 preprocessed += START_A; 281 mode = MODE_A; 282 } 283 284 for (int i = 0; i < text.length(); i++) { 285 c1 = text.charAt(i); 286 if (i + 1 < text.length()) { 287 c2 = text.charAt(i + 1); 288 } else { 289 c2 = 0; 290 } 291 292 if (c1 > FNC_4) { 293 throw new BarcodeException("Invalid character in barcode"); 294 } 295 296 switch (mode) { 297 298 case MODE_C : 299 300 if (c1 == FNC_1) { 301 // FNC_1. 302 preprocessed += FNC_1; 303 } 304 else if (c1 >= '0' && c1 <= '9' && c2 >= '0' && c2 <= '9') { 305 // Two digit encode. 306 preprocessed += convertCodeC(text.substring(i,i+2)); 307 i++; 308 } 309 else if (c1 >= ' ' && c1 <= '\u007f') { 310 // Need to change to mode B. 311 preprocessed += FNC_4; // Corresponds to Mode B. 312 mode = MODE_B; 313 i--; 314 } 315 else { 316 // Need to change to mode A. 317 preprocessed += MODE_A; 318 mode = MODE_A; 319 i--; 320 } 321 322 break; 323 324 case MODE_B : 325 326 if (c1 >= '0' && c1 <= '9' && c2 >= '0' && c2 <= '9') { 327 // Two digits; Change to mode C. 328 preprocessed += MODE_C; 329 mode = MODE_C; 330 i--; 331 } 332 else if (c1 >= ' ' && c1 <= '\u007f') { 333 // Acceptable mode B character. 334 preprocessed += c1; 335 } 336 else if (c1 == FNC_1 || c1 == FNC_2 || c1 == FNC_3 || c1 == FNC_4) { 337 // A function code. 338 preprocessed += c1; 339 } 340 else { 341 // Control character; Change to mode A. 342 preprocessed += MODE_A; 343 mode = MODE_A; 344 i--; 345 } 346 347 break; 348 349 case MODE_A : 350 351 if (c1 >= '0' && c1 <= '9' && c2 >= '0' && c2 <= '9') { 352 // Two digits; Change to mode C. 353 preprocessed += MODE_C; 354 mode = MODE_C; 355 i--; 356 } 357 else if (c1 < ' ') { 358 // Control character. 359 preprocessed += (c1 + '`'); 360 } 361 else if (c1 <= '_') { 362 // Upper case character. 363 preprocessed += c1; 364 } 365 else if (c1 == FNC_1 || c1 == FNC_2 || c1 == FNC_3) { 366 // Function code 1, 2 or 3. 367 preprocessed += c1; 368 } 369 else if (c1 == FNC_4) { 370 // Function code 4. 371 preprocessed += MODE_A; // Corresponds to FNC_4. 372 } 373 else { 374 // Change to mode B. 375 preprocessed += FNC_4; // Corresponds to Mode B. 376 mode = MODE_B; 377 i--; 378 } 379 380 break; 381 } 382 } 383 384 return preprocessed; 385 } 386 387 /** 388 * Converts the Mode C two-digit combinations to their corresponding 389 * Mode B counterparts. 390 */ 391 private char convertCodeC(String twoDigits) { 392 393 char result = (char) (' ' + new Integer(twoDigits).intValue()); 394 395 switch (result) { 396 397 case '\u0080' : // 96 398 result = FNC_3; 399 break; 400 401 case '\u0081' : // 97 402 result = FNC_2; 403 break; 404 405 case '\u0082' : // 98 406 result = SHIFT; 407 break; 408 409 case '\u0083' : // 99 410 result = MODE_C; 411 break; 412 } 413 414 return result; 415 } 416 417 /** 418 * Does nothing except return the String passed to the method. 419 */ 420 protected String postprocess(String text) { 421 return text; 422 } 423 424 /** 425 * Returns a String containing the checksum-encoded version of the text passed 426 * to the method. 427 * It is assumed that the text passed to this method includes the Start 428 * character (populated by <tt>preprocess</tt>), but not the Stop character 429 * (end sentinel). 430 */ 431 protected String augmentWithChecksum(String text) throws BarcodeException { 432 int checkTotal = 0; 433 CharacterCode cc; 434 435 // Start character 436 cc = getCharacterCode(text.charAt(0)); 437 checkTotal = cc.check; 438 439 for (int i = 1; i < text.length(); i++) { 440 char ch = text.charAt(i); 441 cc = getCharacterCode(ch); // get code by character 442 if (cc == null) { 443 throw new BarcodeException("Invalid character in barcode"); 444 } 445 checkTotal += cc.check * i; 446 } 447 cc = getCharacterCode(checkTotal % 103); // get code by check number 448 return text + cc.character; 449 } 450 }