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    }