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.ean;
28
29 import net.sourceforge.barbecue.BarcodeException;
30 import net.sourceforge.barbecue.CompositeModule;
31 import net.sourceforge.barbecue.Module;
32 import net.sourceforge.barbecue.linear.code128.Accumulator;
33 import net.sourceforge.barbecue.linear.code128.CharBuffer;
34 import net.sourceforge.barbecue.linear.code128.Code128Barcode;
35 import net.sourceforge.barbecue.linear.code128.ModuleFactory;
36
37 import java.text.CharacterIterator;
38 import java.text.StringCharacterIterator;
39 import java.util.StringTokenizer;
40
41 /**
42 * An implementation of the UCC 128 and EAN 128 code formats. These are almost identical
43 * to the vanilla Code 128 format, but they are encoded in character set C and include the
44 * FNC1 character at the start. In addition, an Application Identifier must be provided
45 * that identifies the application domain of the barcode. Please see the convenienve methods
46 * on BarcodeFactory that provide application domain specific instances of this barcode type.
47 *
48 * @author <a href="mailto:opensource@ianbourke.com">Ian Bourke</a>
49 */
50 public class UCCEAN128Barcode extends Code128Barcode {
51 /**
52 * SSCC-18 application identifier.
53 */
54 public static final String SSCC_18_AI = "00";
55 /**
56 * SCC-14 shipping code application identifier.
57 */
58 public static final String SCC_14_AI = "01";
59 /**
60 * Global Trade Item Number application identifier.
61 */
62 public static final String GTIN_AI = SCC_14_AI;
63 /**
64 * EAN 128 application identifier for all EAN 128 formats.
65 */
66 public static final String EAN128_AI = "01";
67 /**
68 * Shipment Identification Number application identifier.
69 */
70 public static final String SHIPMENT_ID_AI = "402";
71 /**
72 * US Postal service application identifier for all USPS formats.
73 */
74 public static final String USPS_AI = "420";
75
76 private final String applicationIdentifier;
77 private final boolean includeCheckDigit;
78 private String labelData;
79 private boolean labelDataEncoded = false;
80
81 /**
82 * Creates a new UCC/EAN 128 barcode with the given application identifier and
83 * data to encode. The AI will be prepended to the data (which also has a mod 10
84 * check digit appended) before encoding, and will appear in parentheses in the
85 * printed label underneath the barcode. A mod 10 check digit will be generated.
86 * @param applicationIdentifier The application identifier for this barcode
87 * @param data The data to encode
88 * @throws BarcodeException If the data to be encoded is invalid
89 */
90 public UCCEAN128Barcode(String applicationIdentifier, String data) throws BarcodeException {
91 this(applicationIdentifier, data, true);
92 }
93
94
95 /**
96 * Creates a new UCC/EAN 128 barcode with the given application identifier and
97 * data to encode. The AI will be prepended to the data (which also has a mod 10
98 * check digit appended) before encoding, and will appear in parentheses in the
99 * printed label underneath the barcode.
100 * @param applicationIdentifier The application identifier for this barcode
101 * @param data The data to encode
102 * @param includeCheckDigit specifies whether a mod 10 check digit should be generated or not
103 * @throws BarcodeException If the data to be encoded is invalid
104 */
105 public UCCEAN128Barcode(String applicationIdentifier, String data, boolean includeCheckDigit) throws BarcodeException {
106 super(FNC_1 + applicationIdentifier + data + getMod10CheckDigit(data, includeCheckDigit), C);
107 if (applicationIdentifier == null || applicationIdentifier.length() == 0) {
108 throw new IllegalArgumentException("Application Identifier must be provided");
109 }
110 this.applicationIdentifier = applicationIdentifier;
111 this.includeCheckDigit = includeCheckDigit;
112 this.labelData = data;
113 }
114
115 /**
116 * Creates a new UCC/EAN 128 barcode based on the provided data, with an
117 * optional Modulo 10 check digit.
118 * @param data The data to encode
119 * @param includeCheckDigit if true then a modulo 10 check digit based on
120 * data is appended
121 * @throws BarcodeException If the data to be encoded is invalid
122 */
123 public UCCEAN128Barcode(String data, boolean includeCheckDigit) throws BarcodeException {
124 super(FNC_1 + data + getMod10CheckDigit(data, includeCheckDigit), C);
125 this.applicationIdentifier = "";
126 this.includeCheckDigit = includeCheckDigit;
127 this.labelData = data;
128 }
129
130 /**
131 * Creates a new UCC/EAN 128 barcode with the given application identifier and
132 * data to encode. The AI will be prepended to the data (which also has a mod 10
133 * check digit appended) before encoding, and will appear in parentheses in the
134 * printed label underneath the barcode. A mod 10 check digit will be generated.
135 * @param encodedData The data to encode. The application identifiers should
136 * be enclosed in parentheses, i.e., (01) 123435 (14) 1234235 e.t.c.
137 *
138 * Concatenating Element Strings of variable length, which includes all Application
139 * Identifiers that do not start with two characters contained in Figure 5.3.8.2.1 ? 1,
140 * involves the use of a Separator Character. The Separator Character used is the
141 * Function 1 Character (FNC1). It is placed immediately after the last symbol
142 * character of a variable length data string and is followed by the Application
143 * Identifier of the next Element String. If the Element String is the last to be encoded,
144 * it is followed by the Symbol Check and Stop Characters and not the FNC1
145 * Separator Character.
146 * @throws BarcodeException If the data to be encoded is invalid
147 */
148 public UCCEAN128Barcode(String encodedData) throws BarcodeException
149 {
150 super(FNC_1, C);
151 this.applicationIdentifier = EAN128_AI;
152 this.includeCheckDigit = false;
153
154 StringTokenizer st = new StringTokenizer(encodedData,"()",true);
155
156 StringBuffer sb = new StringBuffer();
157 StringBuffer labelBuffer = new StringBuffer();
158
159 boolean lastAIwasVariableLength = false;
160
161 while (st.hasMoreTokens())
162 {
163 String tok = st.nextToken();
164
165 String ai = null;
166
167 if (tok.equals("("))
168 {
169 ai = st.nextToken();
170 st.nextToken();
171 }
172
173 String barcode_data = st.nextToken();
174
175 if (lastAIwasVariableLength)
176 sb.append(FNC_1);
177
178 lastAIwasVariableLength = (getAILength(ai) == 0);
179
180 sb.append(ai);
181 sb.append(barcode_data);
182
183 labelBuffer.append("("+ai+")");
184 labelBuffer.append(barcode_data);
185
186 if (ai.equals(EAN128_AI))
187 {
188 String checkDigit = getGTINCheckDigit(barcode_data);
189 sb.append(checkDigit);
190
191 labelBuffer.append(checkDigit);
192 }
193 }
194 this.data += sb.toString();
195 this.labelData = labelBuffer.toString();
196 this.labelDataEncoded = true;
197 }
198
199 /** Get the length of a pre-defined length EAN application identifier.
200 * @return the number of characters (including the application identifier) or zero if the AI
201 * does not have a fixed length.
202 * Note: See the UCC/EAN-128 Symbology Specifications for details.
203 * No separator character is required when these appliction identifiers
204 * are used.
205 */
206 private int getAILength(String ai)
207 {
208 if (ai.equals("00"))
209 return 20;
210 if (ai.equals("01"))
211 return 16;
212 if (ai.equals("02"))
213 return 16;
214 if (ai.equals("03"))
215 return 16;
216 if (ai.equals("04"))
217 return 18;
218 if (ai.equals("11"))
219 return 8;
220 if (ai.equals("12"))
221 return 8;
222 if (ai.equals("13"))
223 return 8;
224 if (ai.equals("14"))
225 return 8;
226 if (ai.equals("15"))
227 return 8;
228 if (ai.equals("16"))
229 return 8;
230 if (ai.equals("17"))
231 return 8;
232 if (ai.equals("18"))
233 return 8;
234 if (ai.equals("19"))
235 return 8;
236 if (ai.equals("20"))
237 return 4;
238 if (ai.equals("31"))
239 return 10;
240 if (ai.equals("32"))
241 return 10;
242 if (ai.equals("33"))
243 return 10;
244 if (ai.equals("34"))
245 return 10;
246 if (ai.equals("35"))
247 return 10;
248 if (ai.equals("36"))
249 return 10;
250 if (ai.equals("41"))
251 return 16;
252
253 return 0;
254 }
255
256 /**
257 * Returns the pre-amble for this barcode type. This is basically
258 * a specific instance of the Code 128 barcode that always uses
259 * a C start char and FNC1 char in succession.
260 * @return The pre-amble module
261 */
262 protected Module getPreAmble() {
263 CompositeModule module = new CompositeModule();
264 if(drawingQuietSection) {
265 module.add(QUIET_SECTION);
266 }
267 module.add(START_C);
268 return module;
269 }
270
271 /**
272 * Returns the text to be displayed underneath the barcode.
273 * @return The text that the barcode represents
274 */
275 public String getLabel()
276 {
277 if (null != labelData && labelDataEncoded)
278 {
279 return labelData;
280 }
281
282 if (null != label)
283 {
284 return label;
285 }
286
287 return '(' + applicationIdentifier + ") " + labelData + getMod10CheckDigit(labelData, includeCheckDigit);
288 }
289
290 /**
291 * Generates a mod 10 check digit for the barcode data (ignoring the app id).
292 * @param data The data to generate the check digit for
293 * @param calculate Whether the check digit should actually be calculated or not
294 * @return The check digit (or "" if not calculated)
295 */
296 static String getMod10CheckDigit(String data, boolean calculate) {
297 if (!calculate) {
298 return "";
299 }
300
301 Accumulator sum = new Accumulator(START_INDICES[C]);
302 Accumulator index = new Accumulator(1);
303 CharBuffer buf = new CharBuffer(BUF_SIZES[C]);
304 StringCharacterIterator iter = new StringCharacterIterator(data);
305 for (char c = iter.first(); c != CharacterIterator.DONE; c = iter.next()) {
306 buf.addChar(c);
307 if (buf.isFull()) {
308 int code = ModuleFactory.getIndex(buf.toString(), C);
309 sum.add(code * index.getValue());
310 index.increment();
311 buf.clear();
312 }
313 }
314 return String.valueOf(sum.getValue() % 10);
315 }
316
317 /**
318 * Generates a UCC/EAN standard check digit for the EAN
319 * barcode element (ignoring the app id).
320 * @param element The data to generate the check digit for
321 * @return The check digit (or "" if not calculated)
322 */
323 static String getGTINCheckDigit(String element)
324 {
325
326
327
328
329
330 int len = element.length();
331 int multiplier = 1;
332 int sum = 0;
333 for (int i=(len-1);i>=0;i--)
334 {
335 if (multiplier == 1)
336 multiplier = 3;
337 else
338 multiplier = 1;
339
340 sum += multiplier * Integer.parseInt(element.substring(i,i+1));
341 }
342
343 int ret = ((sum / 10) + 1) * 10 - sum;
344 return String.valueOf(ret);
345 }}