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 013 /** 014 * Title: JBarcodeBean 015 * Description: Barcode JavaBeans Component 016 * Copyright: Copyright (C) 2004 017 * Company: Dafydd Walters 018 */ 019 package net.sourceforge.jbarcodebean.model; 020 021 import net.sourceforge.jbarcodebean.BarcodeElement; 022 import net.sourceforge.jbarcodebean.BarcodeException; 023 import net.sourceforge.jbarcodebean.EncodedBarcode; 024 025 /** 026 * This abstract class, which implements the {@link BarcodeStrategy} interface, 027 * provides a basic implementation that is subclassed by all the concrete 028 * classes that provide Code39, Interleaved Code25, Codabar, MSI and all 029 * derivative encoding strategies. 030 */ 031 public abstract class AbstractBarcodeStrategy implements BarcodeStrategy { 032 033 /** 034 * Subclasses implement this method to return an array of 035 * {@link AbstractBarcodeStrategy.CharacterCode CharacterCode} 036 * objects, representing all possible encodings of bars and spaces for 037 * every encodable character. 038 * 039 * @return An array of {@link AbstractBarcodeStrategy.CharacterCode CharacterCode} 040 * objects, one for each possible character 041 * that can be encoded using this strategy. 042 */ 043 protected abstract CharacterCode[] getCodes(); 044 045 /** 046 * Subclasses implement this method to calculate the checksum from the text 047 * to encode, and return a String containing the text with the checksum 048 * included. 049 * 050 * @param text The text to encode (after preprocessing - see 051 * {@link AbstractBarcodeStrategy#preprocess}). 052 * 053 * @return A String containing the text passed into the method, augmented with 054 * the checksum. For barcode types that don't support a checksum, this would 055 * simply be the text passed to the method. 056 */ 057 protected abstract String augmentWithChecksum(String text) throws BarcodeException; 058 059 /** 060 * Subclasses implement this method to perform any preprocessing necessary on the 061 * original text to encode. The result of this method is the string that gets 062 * passed to the {@link AbstractBarcodeStrategy#augmentWithChecksum} method. 063 * 064 * @param text The raw text to encode. 065 * 066 * @return The string after preprocessing. If no preprocessing is required, 067 * the String passed to the method is returned. 068 * 069 * @throws BarcodeException Typically caused by passing in 070 * a String containing illegal characters (characters that cannot be encoded in 071 * this type of barcode). 072 */ 073 protected abstract String preprocess(String text) throws BarcodeException; 074 075 /** 076 * Subclasses must implement this method to return <tt>true</tt> or <tt>false</tt> 077 * depending on whether the barcode type is <i>interleaved</i>. 078 * 079 * @return <tt>true</tt> if barcode type is interleaved, <tt>false</tt> if it is not. 080 */ 081 protected abstract boolean isInterleaved(); 082 083 /** 084 * Subclasses implement this method to return the start sentinel character. 085 * 086 * @return The character, which when encoded into bars and spaces, appears on 087 * the left edge of every barcode (immediately after the left margin). 088 */ 089 protected abstract char getStartSentinel(); 090 091 /** 092 * Subclasses implement this method to return the stop sentinel character. 093 * 094 * @return The character, which when encoded into bars and spaces, appears on 095 * the right edge of every barcode (just before the right margin). 096 */ 097 protected abstract char getStopSentinel(); 098 099 /** 100 * Sublclasses implement this method to return the width of the whitespace 101 * that must appear on each side of the barcode. 102 * 103 * @return The space that must appear on the left and right sides of the barcode 104 * expressed as a multiple of the narrowest bar width. 105 */ 106 protected abstract byte getMarginWidth(); 107 108 /** 109 * Subclasses implement this method to return the text which appears 110 * below the barcode. 111 * 112 * @param text The raw text to encode. 113 * 114 * @return A String containing the text that will appear beneath the barcode. 115 */ 116 protected abstract String getBarcodeLabelText(String text); 117 118 /** 119 * Subclasses implement this method to perform any postprocessing required 120 * to the text after including the checksum. 121 * 122 * @param text String to process (returned by 123 * {@link AbstractBarcodeStrategy#augmentWithChecksum}). 124 * 125 * @return String after postprocessing. If no postprocessing is required, 126 * the String passed to the method is returned. 127 */ 128 protected abstract String postprocess(String text); 129 130 /** 131 * This implementation carries out the following steps: 132 * <ul> 133 * <li>Call {@link AbstractBarcodeStrategy#preprocess}</li> 134 * <li>Call {@link AbstractBarcodeStrategy#augmentWithChecksum} to add in the checksum</li> 135 * <li>Call {@link AbstractBarcodeStrategy#postprocess}</li> 136 * <li>Adds in the start and end sentinels</li> 137 * <li>Use the CharacterCode array returned by getCodes to encode the text into a barcode</li> 138 * <li>Insert left and right margins</li> 139 * <li>Return EncodedBarcode object</li> 140 * </ul> 141 * 142 * @param textToEncode The raw text to encode. 143 * @param checked True if a checksum is to be calculated, False if not. 144 * 145 * @return The fully encoded barcode, represented as bars and spaces, wrapped 146 * in a {@link EncodedBarcode} object. 147 * 148 * @throws BarcodeException Typically caused by passing in 149 * a String containing illegal characters (characters that cannot be encoded in 150 * this type of barcode). 151 */ 152 public EncodedBarcode encode(String textToEncode, boolean checked) throws BarcodeException { 153 154 String text; 155 156 text = preprocess(textToEncode); 157 text = checked ? augmentWithChecksum(text) : text; 158 text = postprocess(text); 159 160 // Simple to check to ensure start and end characters are not present in the 161 // text to be encoded. 162 if (((getStartSentinel() != 0xffff && text.indexOf(getStartSentinel()) >= 0)) || 163 (text.indexOf(getStopSentinel())) >= 0) { 164 throw new BarcodeException("Invalid character in barcode"); 165 } 166 167 if (getStartSentinel() != 0xffff) { 168 text = getStartSentinel() + text; 169 } 170 171 text += getStopSentinel(); 172 173 int size = computeSize(text); 174 BarcodeElement[] elements = new BarcodeElement[size]; 175 176 // Margin 177 elements[0] = new BarcodeElement(BarcodeElement.TYPE_SPACE, getMarginWidth()); 178 179 int len = text.length(); 180 int j = 1; 181 for(int i = 0; i < len; i++) { 182 char ch = text.charAt(i); 183 CharacterCode cc = getCharacterCode(ch); 184 if (cc == null) { 185 throw new BarcodeException("Invalid character in barcode"); 186 } 187 for (int k = 0; k < cc.widths.length; k++) { 188 int width = cc.widths[k]; 189 int type = ((j % 2) == 0) ? BarcodeElement.TYPE_SPACE : BarcodeElement.TYPE_BAR; 190 elements[j] = new BarcodeElement(type, width); 191 if (isInterleaved() && ch != getStartSentinel() && ch != getStopSentinel()) { 192 j += 2; 193 } else { 194 j++; 195 } 196 } 197 if (isInterleaved() && ch != getStartSentinel() && ch != getStopSentinel()) { 198 if (i % 2 == 1) { 199 j -= (cc.widths.length * 2 - 1); 200 } else { 201 j -= 1; 202 } 203 } 204 } 205 206 elements[j] = new BarcodeElement(BarcodeElement.TYPE_SPACE, getMarginWidth()); 207 j++; 208 209 if (j != size) { 210 throw new BarcodeException("Unexpected barcode size"); 211 } 212 213 return new EncodedBarcode(elements, getBarcodeLabelText(textToEncode)); 214 } 215 216 /** 217 * Computes the length of the barcode (in bar/space modules) based on the 218 * text to encode. 219 * 220 * @param text The text to encode including any check digit, 221 * start and end sentinels. 222 * 223 * @return The number of module segments in this barcode (including margins). 224 * 225 * @throws BarcodeException Typically 226 * occurs if attempting to encode invalid characters. 227 */ 228 protected int computeSize(String text) throws BarcodeException { 229 int size = 0; 230 int l = text.length(); 231 for(int i = 0; i < l; i++) { 232 char ch = text.charAt(i); 233 CharacterCode cc = getCharacterCode(ch); 234 if (cc == null) { 235 throw new BarcodeException("Invalid character in barcode"); 236 } 237 size = size + cc.widths.length; 238 } 239 size += 2; // Margins 240 return size; 241 } 242 243 /** 244 * Looks for the specified character to encode in the CharacterCode array 245 * returned by the {@link AbstractBarcodeStrategy#getCodes} method. 246 * 247 * @param character The character to encode. 248 * 249 * @return CharacterCode The element in the CharacterCode array (returned by 250 * getCodes) that corresponds to the character passed to the method. 251 */ 252 protected CharacterCode getCharacterCode(char character) { 253 CharacterCode[] codes = getCodes(); 254 for (int i = 0; i < codes.length; i ++) { 255 if (codes[i].character == character) { 256 return codes[i]; 257 } 258 } 259 return null; 260 } 261 262 /** 263 * Looks for an entry in the CharacterCode array 264 * returned by the {@link AbstractBarcodeStrategy#getCodes} method, 265 * by its <tt>check</tt> attribute. 266 * 267 * @param check The check attribute of the character being sought. 268 * 269 * @return CharacterCode The element in the CharacterCode array (returned by 270 * getCodes) that corresponds to the character whose check attribute was passed 271 * to the method. 272 */ 273 protected CharacterCode getCharacterCode(int check) { 274 CharacterCode[] codes = getCodes(); 275 for (int i = 0; i < codes.length; i ++) { 276 if (codes[i].check == check) { 277 return codes[i]; 278 } 279 } 280 return null; 281 } 282 283 /** 284 * Inner class representing a character and its barcode encoding. 285 */ 286 public static class CharacterCode { 287 288 /** The character that is encoded */ 289 public char character; 290 /** The widths of the modules (bars and spaces) of this encoded character */ 291 public byte[] widths; 292 /** The check digit corresponding to this character, used in checksum calculations */ 293 public int check; 294 295 /** Constructor which fully initializes the properties of the object. */ 296 public CharacterCode(char character, byte[] widths, int check) { 297 this.character = character; 298 this.widths = widths; 299 this.check = check; 300 } 301 } 302 } 303 304 305