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     *  This file was contributed to JBarcodeBean by Jose Gaonac'h.
013     *  Copyright (C) 2004 Jose Gaonac'h.
014     */
015    
016    package net.sourceforge.jbarcodebean.model;
017    
018    import net.sourceforge.jbarcodebean.BarcodeException;
019    
020    
021    /**
022     * EAN-13 barcode implementation.
023     * If less than 12 digits are supplied, the symbol is invalid
024     * Only the first 12 digits are considered: the checksum (13th digit) is
025     * always generated (MANDATORY_CHECKSUM).
026     *
027     * @author  Jose Gaonac'h
028     */
029    public class Ean13 extends AbstractBarcodeStrategy implements java.io.Serializable {
030        
031        private static CharacterCode[] codes = {
032            // Left side A values and right side C values
033            new CharacterCode('0', new byte[] {3,2,1,1}, 0),
034            new CharacterCode('1', new byte[] {2,2,2,1}, 1),
035            new CharacterCode('2', new byte[] {2,1,2,2}, 2),
036            new CharacterCode('3', new byte[] {1,4,1,1}, 3),
037            new CharacterCode('4', new byte[] {1,1,3,2}, 4),
038            new CharacterCode('5', new byte[] {1,2,3,1}, 5),
039            new CharacterCode('6', new byte[] {1,1,1,4}, 6),
040            new CharacterCode('7', new byte[] {1,3,1,2}, 7),
041            new CharacterCode('8', new byte[] {1,2,1,3}, 8),
042            new CharacterCode('9', new byte[] {3,1,1,2}, 9),
043            // Left side B values
044            new CharacterCode('a', new byte[] {1,1,2,3}, 0),
045            new CharacterCode('b', new byte[] {1,2,2,2}, 1),
046            new CharacterCode('c', new byte[] {2,2,1,2}, 2),
047            new CharacterCode('d', new byte[] {1,1,4,1}, 3),
048            new CharacterCode('e', new byte[] {2,3,1,1}, 4),
049            new CharacterCode('f', new byte[] {1,3,2,1}, 5),
050            new CharacterCode('g', new byte[] {4,1,1,1}, 6),
051            new CharacterCode('h', new byte[] {2,1,3,1}, 7),
052            new CharacterCode('i', new byte[] {3,1,2,1}, 8),
053            new CharacterCode('j', new byte[] {2,1,1,3}, 9),
054            
055            new CharacterCode('A', new byte[] {1,1,1},     -1),     // Start
056            new CharacterCode('B', new byte[] {1,1,1},     -1),     // Stop
057            new CharacterCode('C', new byte[] {1,1,1,1,1}, -1)      // Center
058        };
059        
060        private static char[] bPattern = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'};
061        
062        protected String checkSum;
063        
064        public int requiresChecksum() {
065            return MANDATORY_CHECKSUM;
066        }
067        
068        protected CharacterCode[] getCodes() {
069            return codes;
070        }
071        
072        // Compute checksum and store it locally.
073        protected void computeChecksum(String text, int len) {
074            int even = 0;
075            int odd = 0;
076            int tot;
077            
078            checkSum = "";
079            
080            if (text.length() == len) {
081                
082                for (int i=len-1; i>=0; i-=2) {
083                    even += Integer.parseInt(text.substring(i, i+1));
084                    if (i <= 0) break;
085                    odd += Integer.parseInt(text.substring(i-1, i));
086                }
087                even *= 3;
088                tot = even + odd;
089                even = tot/10;
090                odd = tot%10;
091                if (odd != 0) even++;
092                
093                checkSum = new Integer(even*10 - tot).toString();
094            }
095        }
096        
097        protected String augmentWithChecksum(String text) throws BarcodeException {
098            // Checksum has been computed before inserting B codes and center bars
099            return text + checkSum;
100        }
101        
102        protected String postprocess(String text) {
103            return text;
104        }
105        
106        /*
107          There are 3 representations of digits in a EAN/UPC symbol: A, B, and C
108          A or B representations are used for the 6 left digits, while C
109          representations are used on the 6 right digits. The C representation is the 
110          opposite of A representations: just exchange white anf black bars. This is
111          done automatically, thanks to the insertion of the center bars code (odd number of bars)
112          which reverses the process: the first module in left-side digits is a space, but it is
113          a bar in right-side digits... Therefore we can use the same 'CharacterCode' for left
114          side 'A's and right side 'C's.
115          The combinations of A and B representations in the left side determines the
116          system digit (SD): this digit, which is the first in the symbol, is not encoded
117          as bar / spaces.
118          The following table list the possible layout of A and B codes in the left
119          part of symbol and the coresponding SD:
120          
121          Layout               --> SD
122          A  A  A  A  A  A         0
123          A  A  B  A  B  B         1
124          A  A  B  B  A  B         2
125          A  A  B  B  B  A         3
126          A  B  A  A  B  B         4
127          A  B  B  A  A  B         5
128          A  B  B  B  A  A         6
129          A  B  A  B  A  B         7
130          A  B  A  B  B  A         8
131          A  B  B  A  B  A         9
132         
133         */
134        protected String preprocess(String text) {
135            if (text.length() < 12) return text;
136            text = text.substring(0, 12);
137            
138            computeChecksum(text, 12);
139            char[] t = (text.substring(1, 7) + "C" + text.substring(7)).toCharArray();
140            switch (text.charAt(0)) {
141                case '0':
142                    // A A A A A A
143                    break;
144                case '1':
145                    // A A B A B B
146                    t[2] = bPattern[getCharacterCode(t[2]).check];
147                    t[4] = bPattern[getCharacterCode(t[4]).check];
148                    t[5] = bPattern[getCharacterCode(t[5]).check];
149                    break;
150                case '2':
151                    // A A B B A B
152                    t[2] = bPattern[getCharacterCode(t[2]).check];
153                    t[3] = bPattern[getCharacterCode(t[3]).check];
154                    t[5] = bPattern[getCharacterCode(t[5]).check];
155                    break;
156                case '3':
157                    // A A B B B A
158                    t[2] = bPattern[getCharacterCode(t[2]).check];
159                    t[3] = bPattern[getCharacterCode(t[3]).check];
160                    t[4] = bPattern[getCharacterCode(t[4]).check];
161                    break;
162                case '4':
163                    // A B A A B B
164                    t[1] = bPattern[getCharacterCode(t[1]).check];
165                    t[4] = bPattern[getCharacterCode(t[4]).check];
166                    t[5] = bPattern[getCharacterCode(t[5]).check];
167                    break;
168                case '5':
169                    // A B B A A B
170                    t[1] = bPattern[getCharacterCode(t[1]).check];
171                    t[2] = bPattern[getCharacterCode(t[2]).check];
172                    t[5] = bPattern[getCharacterCode(t[5]).check];
173                    break;
174                case '6':
175                    // A B B B A A
176                    t[1] = bPattern[getCharacterCode(t[1]).check];
177                    t[2] = bPattern[getCharacterCode(t[2]).check];
178                    t[3] = bPattern[getCharacterCode(t[3]).check];
179                    break;
180                case '7':
181                    // A B A B A B
182                    t[1] = bPattern[getCharacterCode(t[1]).check];
183                    t[3] = bPattern[getCharacterCode(t[3]).check];
184                    t[5] = bPattern[getCharacterCode(t[5]).check];
185                    break;
186                case '8':
187                    // A B A B B A
188                    t[1] = bPattern[getCharacterCode(t[1]).check];
189                    t[3] = bPattern[getCharacterCode(t[3]).check];
190                    t[4] = bPattern[getCharacterCode(t[4]).check];
191                    break;
192                case '9':
193                    // A B B A B A
194                    t[1] = bPattern[getCharacterCode(t[1]).check];
195                    t[2] = bPattern[getCharacterCode(t[2]).check];
196                    t[4] = bPattern[getCharacterCode(t[4]).check];
197                    break;
198            }
199            return new String(t);
200        }
201        
202        protected boolean isInterleaved() {
203            return false;
204        }
205        
206        protected char getStartSentinel() {
207            return 'A';
208        }
209        
210        protected char getStopSentinel() {
211            return 'B';
212        }
213        
214        /**
215         * Always returns 11 (eleven).
216         */
217        protected byte getMarginWidth() {
218            return 11;
219        }
220        
221        protected String getBarcodeLabelText(String text) {
222            if (text.length() < 12) return text;
223            return text.substring(0, 12) + checkSum;
224        }
225        
226    }