1 /***********************************************************************************************************************
2 * Copyright (c) 2003, International Barcode Consortium
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without modification,
6 * are permitted provided that the following conditions are met:
7 *
8 * Redistributions of source code must retain the above copyright notice, this list of
9 * conditions and the following disclaimer.
10 * Redistributions in binary form must reproduce the above copyright notice, this list of
11 * conditions and the following disclaimer in the documentation and/or other materials
12 * provided with the distribution.
13 * Neither the name of the International Barcode Consortium nor the names of any contributors may be used to endorse
14 * or promote products derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
18 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
19 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24 * POSSIBILITY OF SUCH DAMAGE.
25 ***********************************************************************************************************************/
26
27 package net.sourceforge.barbecue.linear.codabar;
28
29 import net.sourceforge.barbecue.BarcodeException;
30 import net.sourceforge.barbecue.BlankModule;
31 import net.sourceforge.barbecue.Module;
32 import net.sourceforge.barbecue.SeparatorModule;
33 import net.sourceforge.barbecue.linear.LinearBarcode;
34
35 import java.util.ArrayList;
36 import java.util.List;
37 import java.text.CharacterIterator;
38 import java.text.StringCharacterIterator;
39
40 /**
41 * This is a concrete implementation of the Codabar barcode, AKA USD-4,
42 * Monarch, NW-7 and 2of7. This implementation imposes no restrictions
43 * on your choice of start and stop characters, so you will need to check
44 * that you are using chars acceptable to your barcode scanner. This
45 * implementation does support the traditional a, b, c, d, e, t, n and * start/stop chars.
46 * Omitting the start/stop chars from your data will cause the barcode to use
47 * the default start/stops chars, A and C, respectively.
48 * <br/>
49 * This implementation provides no support for check digits (as they are not
50 * included in the Codabar specification). However, many uses of Codabars
51 * mandate the use of a check digit, and the algorithms used vary from
52 * application to application. The most common algorithm is Mod-16. If you wish
53 * to implement a check digit in your Codabar barcode, you must calculate it
54 * yourself and insert it into the data to be encoded before the Stop char (or at
55 * the end of the data if you are letting barbecue insert start and stop chars for
56 * you).
57 *
58 * @author <a href="mailto:opensource@ianbourke.com">Ian Bourke</a>
59 */
60 public class CodabarBarcode extends LinearBarcode {
61 /** The default codabar start character */
62 public static final String DEFAULT_START = "A";
63 /** The default codabar stop character */
64 public static final String DEFAULT_STOP = "C";
65
66 private String label;
67
68 /**
69 * Constructs a new Codabar barcode with thte specified data.
70 * @param data The data to encode
71 * @throws BarcodeException If the data is invalid
72 */
73 public CodabarBarcode(String data) throws BarcodeException {
74 super(data);
75 this.label = data;
76 validateData();
77 }
78
79 /**
80 * Returns the text label to be displayed underneath the barcode.
81 * @return The barcode label
82 */
83 public String getLabel() {
84 return label;
85 }
86
87 /**
88 * Encodes the data of the barcode into bars.
89 * @return The encoded bar data
90 */
91 protected Module[] encodeData() {
92 List modules = new ArrayList();
93 for (int i = 0; i < data.length(); i++) {
94 if (i > 0) {
95 modules.add(new SeparatorModule(1));
96 }
97 char c = data.charAt(i);
98 Module module = ModuleFactory.getModule(String.valueOf(c));
99 modules.add(module);
100 }
101 return (Module[]) modules.toArray(new Module[0]);
102 }
103
104 /**
105 * Calculates the check sum digit for the barcode.
106 * @return Null - Codabar has no checksum
107 */
108 protected Module calculateChecksum() {
109 return null;
110 }
111
112 /**
113 * Returns the pre-amble for the barcode.
114 * @return A BlankModule
115 */
116 protected Module getPreAmble() {
117 if(drawingQuietSection) {
118 return new BlankModule(0);
119 } else {
120 return null;
121 }
122 }
123
124 /**
125 * Returns the post-amble for the barcode.
126 * @return A BlankModule
127 */
128 protected Module getPostAmble() {
129 if(drawingQuietSection) {
130 return new BlankModule(0);
131 } else {
132 return null;
133 }
134 }
135
136 private void validateData() throws BarcodeException {
137 replaceTraditionalStartStopWithModern();
138 addDefaultStartStopIfRequired();
139 int index = 0;
140 StringBuffer buf = new StringBuffer();
141 StringCharacterIterator iter = new StringCharacterIterator(data);
142 for (char c = iter.first(); c != CharacterIterator.DONE; c = iter.next()) {
143 if (!Character.isWhitespace(c)) {
144 if (!ModuleFactory.isValid(String.valueOf(c))) {
145 throw new BarcodeException(c
146 + " is not a valid character for Codabar encoding");
147 }
148 checkStartStop(c, index);
149 buf.append(c);
150 }
151 index += 1;
152 }
153 data = buf.toString();
154 }
155
156 private void addDefaultStartStopIfRequired() {
157 StringBuffer newData = new StringBuffer();
158 if (!Character.isLetter(data.charAt(0))) {
159 newData.append(DEFAULT_START);
160 }
161 newData.append(data);
162 if (!Character.isLetter(data.charAt(data.length() - 1))) {
163 newData.append(DEFAULT_STOP);
164 }
165 data = newData.toString();
166 }
167
168 private void replaceTraditionalStartStopWithModern() {
169 data = data.replace('a', 'A');
170 data = data.replace('t', 'A');
171 data = data.replace('b', 'B');
172 data = data.replace('n', 'B');
173 data = data.replace('c', 'C');
174 data = data.replace('*', 'C');
175 data = data.replace('d', 'D');
176 data = data.replace('e', 'D');
177 }
178
179 private void checkStartStop(char c, int index) throws BarcodeException {
180 if (Character.isLetter(c) && index > 0 && index < data.length() - 1) {
181 throw new BarcodeException(c
182 + " is only allowed as the first and last characters for Codabar barcodes");
183 }
184 }
185 }