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 }