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.code39; 28 29 import net.sourceforge.barbecue.BarcodeException; 30 import net.sourceforge.barbecue.CompositeModule; 31 import net.sourceforge.barbecue.Module; 32 import net.sourceforge.barbecue.SeparatorModule; 33 import net.sourceforge.barbecue.linear.LinearBarcode; 34 35 import java.text.CharacterIterator; 36 import java.text.StringCharacterIterator; 37 import java.util.ArrayList; 38 import java.util.List; 39 40 /** 41 * This is a concrete implementation of the Code 39 barcode, AKA 3of9, 42 * USD-3. 43 * 44 * @author <a href="mailto:opensource@ianbourke.com">Ian Bourke</a> 45 */ 46 public class Code39Barcode extends LinearBarcode { 47 /** 48 * A list of type identifiers for the Code39 barcode format 49 */ 50 public static final String[] TYPES = new String[]{ 51 "Code39", "USD3", "3of9" 52 }; 53 private final boolean requiresChecksum; 54 private final String label; 55 56 /** 57 * Constructs a basic mode Code 39 barcode with the specified data and an optional 58 * checksum. 59 * 60 * @param data The data to encode 61 * @param requiresChecksum A flag indicating whether a checksum is required or not 62 * @throws BarcodeException If the data to be encoded is invalid 63 */ 64 public Code39Barcode(String data, boolean requiresChecksum) throws BarcodeException { 65 this(data, requiresChecksum, false); 66 } 67 68 /** 69 * Constructs an extended mode Code 39 barcode with the specified data and an optional 70 * checksum. The extended mode encodes all 128 ASCII characters using two character pairs 71 * from the basic Code 39 character set. Note that most barcode scanners will need to 72 * be configured to accept extended Code 39. 73 * 74 * @param data The data to encode 75 * @param requiresChecksum A flag indicating whether a checksum is required or not 76 * @param extendedMode Puts the barcode into extended mode, where all 128 ASCII characters can be encoded 77 * @throws BarcodeException If the data to be encoded is invalid 78 */ 79 public Code39Barcode(String data, boolean requiresChecksum, boolean extendedMode) throws BarcodeException { 80 super(extendedMode ? encodeExtendedChars(data) : validateBasicChars(data)); 81 this.requiresChecksum = requiresChecksum; 82 this.label = data; 83 } 84 85 /** 86 * Returns the text that will be displayed underneath the barcode (if requested). 87 * 88 * @return The text label for the barcode 89 */ 90 public String getLabel() { 91 return label; 92 } 93 94 /** 95 * Returns the encoded data for the barcode. 96 * 97 * @return An array of modules that represent the data as a barcode 98 */ 99 protected Module[] encodeData() { 100 List modules = new ArrayList(); 101 for (int i = 0; i < data.length(); i++) { 102 char c = data.charAt(i); 103 modules.add(new SeparatorModule(1)); 104 Module module = ModuleFactory.getModule(String.valueOf(c)); 105 modules.add(module); 106 } 107 modules.add(new SeparatorModule(1)); 108 return (Module[]) modules.toArray(new Module[0]); 109 } 110 111 /** 112 * Returns the checksum for the barcode, pre-encoded as a Module. 113 * 114 * @return Null if no checksum is required, a Mod-43 calculated checksum otherwise 115 */ 116 protected Module calculateChecksum() { 117 if (requiresChecksum) { 118 int checkIndex = calculateMod43(data); 119 CompositeModule compositeModule = new CompositeModule(); 120 compositeModule.add(ModuleFactory.getModuleForIndex(checkIndex)); 121 compositeModule.add(new SeparatorModule(1)); 122 return compositeModule; 123 } 124 return null; 125 } 126 127 /** 128 * Returns the for the Mod-43 checkIndex for the barcode as an int 129 * 130 * @return Mod-43 checkIndex for the given data String 131 */ 132 public static int calculateMod43(final String givenData) { 133 int sum = 0; 134 StringCharacterIterator iter = new StringCharacterIterator(givenData); 135 for (char c = iter.first(); c != CharacterIterator.DONE; c = iter.next()) { 136 sum += ModuleFactory.getIndex(String.valueOf(c)); 137 } 138 int checkIndex = sum % 43; 139 return checkIndex; 140 } 141 142 /** 143 * Returns the pre-amble for the barcode. 144 * 145 * @return ModuleFactory.START_STOP 146 */ 147 protected Module getPreAmble() { 148 return ModuleFactory.START_STOP; 149 } 150 151 /** 152 * Returns the post-amble for the barcode. 153 * 154 * @return ModuleFactory.START_STOP 155 */ 156 protected Module getPostAmble() { 157 return ModuleFactory.START_STOP; 158 } 159 160 private static String validateBasicChars(String data) throws BarcodeException { 161 StringCharacterIterator iter = new StringCharacterIterator(data); 162 for (char c = iter.first(); c != CharacterIterator.DONE; c = iter.next()) { 163 if (!ModuleFactory.hasModule(String.valueOf(c), false)) { 164 throw new BarcodeException("Illegal character - try using extended mode if you need " 165 + "to encode the full ASCII character set"); 166 } 167 } 168 return data; 169 } 170 171 private static String encodeExtendedChars(String data) { 172 StringBuffer buf = new StringBuffer(); 173 StringCharacterIterator iter = new StringCharacterIterator(data); 174 for (char c = iter.first(); c != CharacterIterator.DONE; c = iter.next()) { 175 if (!ModuleFactory.hasModule(String.valueOf(c), true)) { 176 buf.append(ModuleFactory.getExtendedCharacter(c)); 177 } else { 178 buf.append(c); 179 } 180 } 181 return buf.toString(); 182 } 183 }