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 }