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 }