View Javadoc

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(); // get rid of the last ")"
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 				// The check digit should be in the human readable label
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) // return 0 if variable
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 		/*	Note: The other Check digit calculation appears wrong.
326 			This one is taken from the EAN SPEC Section 3.A.1 Appendix 1: Check Digit Calculations
327 			IT ONLY APPLIES TO GTIN and possibly others!
328 			NOTE: Price and Weight elements MUST to use a different method.
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 		// Step 3: Subtract sum from nearest equal or higher multiple of ten (110)  =  Check Digit (9)
343 		int ret = ((sum / 10) + 1) * 10 - sum;
344 		return String.valueOf(ret);
345 	}}