View Javadoc

1   /***********************************************************************************************************************
2    * Copyright (c) 2004, 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.Module;
31  import net.sourceforge.barbecue.linear.upc.UPCABarcode;
32  import net.sourceforge.barbecue.output.LabelLayoutFactory;
33  import net.sourceforge.barbecue.output.Output;
34  import net.sourceforge.barbecue.output.OutputException;
35  
36  import java.awt.*;
37  import java.util.ArrayList;
38  import java.util.List;
39  
40  /**
41   * This is a concrete implementation of the EAN13 barcode.
42   *
43   * @author <a href="mailto:james@metalskin.com">James Jenner</a>
44   */
45  public class EAN13Barcode extends UPCABarcode {
46      /**
47       * A list of type identifiers for the EAN13 barcode format
48       */
49      public static final String[] TYPES = new String[]{
50          "EAN-13", "EAN13"
51      };
52  
53      public final static int LEFT_ODD = 0;
54      public final static int LEFT_EVEN = 1;
55      public final static int RIGHT = 2;
56  
57      public final static int PARITY_EVEN = 0;
58      public final static int PARITY_ODD = 1;
59  
60      public final static int BARCODE_LENGTH = 12;
61  
62      public final static String ISBN_NUMBER_SYSTEM = "978";
63      public final static int ISBN_SIZE = 10;
64  
65      /**
66       * Constructs a basic mode EAN13 barcode with the specified data and an optional
67       * checksum.  The length of the barcode is 11, 12 with a checksum.  If the length
68       * passed is only 11, then a checksum will be automaticaly added.  If the length
69       * is not 11 or 12 then a barcode exception will be thrown.
70       *
71       * @param data The data to encode
72       * @throws BarcodeException If the data to be encoded is invalid
73       */
74      public EAN13Barcode(String data) throws BarcodeException {
75          super(data);
76      }
77  
78      protected int getBarcodeLength() {
79          return BARCODE_LENGTH;
80      }
81  
82      protected int getGuardCharSize() {
83          return EAN13ModuleFactory.GUARD_CHAR_SIZE;
84      }
85  
86      protected int getLeftWidth() {
87          return EAN13ModuleFactory.LEFT_WIDTH;
88      }
89  
90      protected Module getRightMargin() {
91          return EAN13ModuleFactory.RIGHT_MARGIN;
92      }
93  
94      /* TODO: The following is very close to the parent (UPCABarcode), 
95       * should change parent so can handle both.  
96       * 
97       * note that the following code uses member functions to get static 
98       * values from the ModuleFactory class, instead of referencing the 
99       * values directly.  This is so sub-classes can override the 
100      * member functions and thus change the static values.
101      */
102     protected Dimension draw(Output output, int x, int y, int barWidth, int barHeight) throws OutputException {
103         int currentX = x;
104 
105         output.beginDraw();
106         
107         // need to change the output.barHeight value, appears to be no means to do so
108         int guardBarHeight = 0;
109         int shortBarHeight = barHeight;
110         int textHeight = 10 * barWidth;
111 
112         if (drawingText) {
113             shortBarHeight = barHeight - (11 * barWidth);
114             guardBarHeight = shortBarHeight + (6 * barWidth);
115         } else {
116             shortBarHeight = barHeight - (6 * barWidth);
117             guardBarHeight = barHeight;
118         }
119 
120         String text = getLabel();
121         int currentY = this.barHeight + y;
122 
123         Module[] modules = encodeData();
124 
125         String leadChar = String.valueOf(text.charAt(0));
126         String firstSet = text.substring(1, getLeftWidth());
127         String lastSet = text.substring(getLeftWidth());
128 
129         if (requiresChecksum) {
130             lastSet = lastSet + calculateChecksum().getSymbol();
131         }
132 
133         int startTextPos = 0;
134         int firstTextPos = 0;
135         int secondTextPos = 0;
136 
137         int startTextW = x;
138         int firstTextW = 0;
139         int secondTextW = 0;
140         int width = 0;
141         Module preAmble = getPreAmble();
142         Module postAmble = getPostAmble();
143         startTextW = 0;
144         
145         // draw leading white space
146         if (super.drawingQuietSection) {
147             currentX += drawModule(getLeftMargin(), output, currentX, y, barWidth, shortBarHeight + textHeight);
148         }
149         startTextPos = x;
150         startTextW = currentX - startTextPos;
151         width = currentX;
152         int guardCharSize = getGuardCharSize();
153         int leftWidth = getLeftWidth() - 1;
154         
155         // draw the left guard
156         if (preAmble != null) {
157             currentX += drawModule(preAmble, output, currentX, y, barWidth, guardBarHeight);
158         }
159         
160         // draw first char in left side
161         for (int i = 0; i < guardCharSize; i++) {
162             currentX += drawModule(modules[0], output, currentX, y, barWidth, guardBarHeight);
163         }
164         firstTextPos = currentX;
165         
166         // draw the blank space below the guard
167         width = currentX - width;
168         output.paintBackground(currentX - width, guardBarHeight + currentY, width, ((shortBarHeight + textHeight) - guardBarHeight));
169 
170         for (int i = guardCharSize; i < leftWidth; i++) {
171             currentX += drawModule(modules[i], output, currentX, y, barWidth, shortBarHeight);
172         }
173 
174         firstTextW = currentX - firstTextPos;
175 
176         width = currentX;
177         // draw the centre guard
178         currentX += drawModule(getCentreGuard(), output, currentX, y, barWidth, guardBarHeight);
179         secondTextPos = currentX;
180         
181         // draw the blank space below the guard
182         width = currentX - width;
183         output.paintBackground(currentX - width, guardBarHeight + currentY, width, ((shortBarHeight + textHeight) - guardBarHeight));
184 
185         for (int i = leftWidth; i < modules.length; i++) {
186             currentX += drawModule(modules[i], output, currentX, y, barWidth, shortBarHeight);
187         }
188 
189         secondTextW = currentX - secondTextPos;
190         width = currentX;
191         
192         // draw the right guard
193         if (postAmble != null) {
194             currentX += drawModule(postAmble, output, currentX, y, barWidth, guardBarHeight);
195         }
196         
197         // draw the blank space below the guard
198         width = currentX - width;
199         output.paintBackground(currentX - width, guardBarHeight + currentY, width, ((shortBarHeight + textHeight) - guardBarHeight));
200         
201         // draw trailing white space
202         currentX += drawModule(getRightMargin(), output, currentX, y, barWidth, shortBarHeight + textHeight);
203 
204         if (drawingText) {
205             output.drawText(leadChar, LabelLayoutFactory.createMarginLayout(startTextPos, shortBarHeight, startTextW, textHeight));
206             output.drawText(firstSet, LabelLayoutFactory.createMarginLayout(firstTextPos, shortBarHeight, firstTextW, textHeight));
207             output.drawText(lastSet, LabelLayoutFactory.createMarginLayout(secondTextPos, shortBarHeight, secondTextW, textHeight));
208         }
209 
210         Dimension size = new Dimension((int) (currentX - x), (int) (currentY) - y);
211 
212         output.endDraw((int) size.getWidth(), (int) size.getHeight());
213 
214         return size;
215     }
216 
217 
218     /**
219      * Returns the encoded data for the barcode.
220      *
221      * @return An array of modules that represent the data as a barcode
222      */
223     protected Module[] encodeData() {
224         List modules = new ArrayList();
225         Module module = null;
226         int len = data.length();
227         char c;
228 
229         String firstChar = data.substring(0, 1);
230         
231         /* EAN13 is a funny one.  The first digit actualy isn't 
232          * encoded, even though its displayed on the human readable.
233          *
234          * go figure.
235          */
236         for (int i = 1; i < len; i++) {
237             c = data.charAt(i);
238 
239             module = EAN13ModuleFactory.getModule(firstChar, String.valueOf(c), i);
240             width += module.widthInBars();
241             modules.add(module);
242         }
243 
244         if (requiresChecksum) {
245             module = EAN13ModuleFactory.getModule(firstChar, calculateChecksum().getSymbol(), modules.size() - 1);
246             width += module.widthInBars();
247             modules.add(module);
248         }
249 
250         return (Module[]) modules.toArray(new Module[0]);
251     }
252 
253     /**
254      * Returns the checksum for the barcode, pre-encoded as a Module.
255      *
256      * @return a Mod-10 caclulated checksum, if no checksum is required Null
257      */
258     protected Module calculateChecksum() {
259         if (requiresChecksum) {
260             return EAN13ModuleFactory.getModuleForIndex(data.substring(0, 1), getMod10CheckDigit(data));
261         }
262 
263         return null;
264     }
265 
266     public static int getMod10CheckDigit(String data) {
267         int sum = 0;
268         int len = data.length();
269         int value;
270         
271         /*
272          * note that the for loop is from 0, as indexing for data is from 0,
273          * but the modolo 10 considers the first character position to be 1.
274          * in UPCA 0 is odd, as the first position (being 1) is considered 
275          * an odd number.  However EAN13 is one extra character in size. To
276          * be backward compatible EAN13 has a '0' added to the front for 
277          * UPCA numbers and as such the first position is now even.
278          * So the following code must compare against 0, not 1 as is in
279          * the UPCA getMod10CheckDigit code.
280          */
281         for (int i = 0; i < len; i++) {
282             try {
283                 value = Integer.parseInt(String.valueOf(data.charAt(i)));
284                 sum += calculateChecksum(value, (i % 2) == 0);
285             } catch (java.lang.NumberFormatException e) {
286             }
287         }
288 
289         return 10 - (sum % 10);
290     }
291 }