001/* 002 * $Id: BarcodeCodabar.java 4784 2011-03-15 08:33:00Z blowagie $ 003 * 004 * This file is part of the iText (R) project. 005 * Copyright (c) 1998-2011 1T3XT BVBA 006 * Authors: Bruno Lowagie, Paulo Soares, et al. 007 * 008 * This program is free software; you can redistribute it and/or modify 009 * it under the terms of the GNU Affero General Public License version 3 010 * as published by the Free Software Foundation with the addition of the 011 * following permission added to Section 15 as permitted in Section 7(a): 012 * FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY 1T3XT, 013 * 1T3XT DISCLAIMS THE WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS. 014 * 015 * This program is distributed in the hope that it will be useful, but 016 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 017 * or FITNESS FOR A PARTICULAR PURPOSE. 018 * See the GNU Affero General Public License for more details. 019 * You should have received a copy of the GNU Affero General Public License 020 * along with this program; if not, see http://www.gnu.org/licenses or write to 021 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 022 * Boston, MA, 02110-1301 USA, or download the license from the following URL: 023 * http://itextpdf.com/terms-of-use/ 024 * 025 * The interactive user interfaces in modified source and object code versions 026 * of this program must display Appropriate Legal Notices, as required under 027 * Section 5 of the GNU Affero General Public License. 028 * 029 * In accordance with Section 7(b) of the GNU Affero General Public License, 030 * a covered work must retain the producer line in every PDF that is created 031 * or manipulated using iText. 032 * 033 * You can be released from the requirements of the license by purchasing 034 * a commercial license. Buying such a license is mandatory as soon as you 035 * develop commercial activities involving the iText software without 036 * disclosing the source code of your own applications. 037 * These activities include: offering paid services to customers as an ASP, 038 * serving PDFs on the fly in a web application, shipping iText with a closed 039 * source product. 040 * 041 * For more information, please contact iText Software Corp. at this 042 * address: sales@itextpdf.com 043 */ 044package com.itextpdf.text.pdf; 045 046import java.awt.Canvas; 047import java.awt.Image; 048import java.awt.image.MemoryImageSource; 049import com.itextpdf.text.error_messages.MessageLocalization; 050 051import com.itextpdf.text.Element; 052import com.itextpdf.text.ExceptionConverter; 053import com.itextpdf.text.Rectangle; 054import com.itextpdf.text.BaseColor; 055 056/** Implements the code codabar. The default parameters are: 057 * <pre> 058 *x = 0.8f; 059 *n = 2; 060 *font = BaseFont.createFont("Helvetica", "winansi", false); 061 *size = 8; 062 *baseline = size; 063 *barHeight = size * 3; 064 *textAlignment = Element.ALIGN_CENTER; 065 *generateChecksum = false; 066 *checksumText = false; 067 *startStopText = false; 068 * </pre> 069 * 070 * @author Paulo Soares 071 */ 072public class BarcodeCodabar extends Barcode{ 073 074 /** The bars to generate the code. 075 */ 076 private static final byte BARS[][] = 077 { 078 {0,0,0,0,0,1,1}, // 0 079 {0,0,0,0,1,1,0}, // 1 080 {0,0,0,1,0,0,1}, // 2 081 {1,1,0,0,0,0,0}, // 3 082 {0,0,1,0,0,1,0}, // 4 083 {1,0,0,0,0,1,0}, // 5 084 {0,1,0,0,0,0,1}, // 6 085 {0,1,0,0,1,0,0}, // 7 086 {0,1,1,0,0,0,0}, // 8 087 {1,0,0,1,0,0,0}, // 9 088 {0,0,0,1,1,0,0}, // - 089 {0,0,1,1,0,0,0}, // $ 090 {1,0,0,0,1,0,1}, // : 091 {1,0,1,0,0,0,1}, // / 092 {1,0,1,0,1,0,0}, // . 093 {0,0,1,0,1,0,1}, // + 094 {0,0,1,1,0,1,0}, // a 095 {0,1,0,1,0,0,1}, // b 096 {0,0,0,1,0,1,1}, // c 097 {0,0,0,1,1,1,0} // d 098 }; 099 100 /** The index chars to <CODE>BARS</CODE>. 101 */ 102 private static final String CHARS = "0123456789-$:/.+ABCD"; 103 104 private static final int START_STOP_IDX = 16; 105 /** Creates a new BarcodeCodabar. 106 */ 107 public BarcodeCodabar() { 108 try { 109 x = 0.8f; 110 n = 2; 111 font = BaseFont.createFont("Helvetica", "winansi", false); 112 size = 8; 113 baseline = size; 114 barHeight = size * 3; 115 textAlignment = Element.ALIGN_CENTER; 116 generateChecksum = false; 117 checksumText = false; 118 startStopText = false; 119 codeType = CODABAR; 120 } 121 catch (Exception e) { 122 throw new ExceptionConverter(e); 123 } 124 } 125 126 /** Creates the bars. 127 * @param text the text to create the bars 128 * @return the bars 129 */ 130 public static byte[] getBarsCodabar(String text) { 131 text = text.toUpperCase(); 132 int len = text.length(); 133 if (len < 2) 134 throw new IllegalArgumentException(MessageLocalization.getComposedMessage("codabar.must.have.at.least.a.start.and.stop.character")); 135 if (CHARS.indexOf(text.charAt(0)) < START_STOP_IDX || CHARS.indexOf(text.charAt(len - 1)) < START_STOP_IDX) 136 throw new IllegalArgumentException(MessageLocalization.getComposedMessage("codabar.must.have.one.of.abcd.as.start.stop.character")); 137 byte bars[] = new byte[text.length() * 8 - 1]; 138 for (int k = 0; k < len; ++k) { 139 int idx = CHARS.indexOf(text.charAt(k)); 140 if (idx >= START_STOP_IDX && k > 0 && k < len - 1) 141 throw new IllegalArgumentException(MessageLocalization.getComposedMessage("in.codabar.start.stop.characters.are.only.allowed.at.the.extremes")); 142 if (idx < 0) 143 throw new IllegalArgumentException(MessageLocalization.getComposedMessage("the.character.1.is.illegal.in.codabar", text.charAt(k))); 144 System.arraycopy(BARS[idx], 0, bars, k * 8, 7); 145 } 146 return bars; 147 } 148 149 public static String calculateChecksum(String code) { 150 if (code.length() < 2) 151 return code; 152 String text = code.toUpperCase(); 153 int sum = 0; 154 int len = text.length(); 155 for (int k = 0; k < len; ++k) 156 sum += CHARS.indexOf(text.charAt(k)); 157 sum = (sum + 15) / 16 * 16 - sum; 158 return code.substring(0, len - 1) + CHARS.charAt(sum) + code.substring(len - 1); 159 } 160 161 /** Gets the maximum area that the barcode and the text, if 162 * any, will occupy. The lower left corner is always (0, 0). 163 * @return the size the barcode occupies. 164 */ 165 public Rectangle getBarcodeSize() { 166 float fontX = 0; 167 float fontY = 0; 168 String text = code; 169 if (generateChecksum && checksumText) 170 text = calculateChecksum(code); 171 if (!startStopText) 172 text = text.substring(1, text.length() - 1); 173 if (font != null) { 174 if (baseline > 0) 175 fontY = baseline - font.getFontDescriptor(BaseFont.DESCENT, size); 176 else 177 fontY = -baseline + size; 178 fontX = font.getWidthPoint(altText != null ? altText : text, size); 179 } 180 text = code; 181 if (generateChecksum) 182 text = calculateChecksum(code); 183 byte bars[] = getBarsCodabar(text); 184 int wide = 0; 185 for (int k = 0; k < bars.length; ++k) { 186 wide += bars[k]; 187 } 188 int narrow = bars.length - wide; 189 float fullWidth = x * (narrow + wide * n); 190 fullWidth = Math.max(fullWidth, fontX); 191 float fullHeight = barHeight + fontY; 192 return new Rectangle(fullWidth, fullHeight); 193 } 194 195 /** Places the barcode in a <CODE>PdfContentByte</CODE>. The 196 * barcode is always placed at coordinates (0, 0). Use the 197 * translation matrix to move it elsewhere.<p> 198 * The bars and text are written in the following colors:<p> 199 * <P><TABLE BORDER=1> 200 * <TR> 201 * <TH><P><CODE>barColor</CODE></TH> 202 * <TH><P><CODE>textColor</CODE></TH> 203 * <TH><P>Result</TH> 204 * </TR> 205 * <TR> 206 * <TD><P><CODE>null</CODE></TD> 207 * <TD><P><CODE>null</CODE></TD> 208 * <TD><P>bars and text painted with current fill color</TD> 209 * </TR> 210 * <TR> 211 * <TD><P><CODE>barColor</CODE></TD> 212 * <TD><P><CODE>null</CODE></TD> 213 * <TD><P>bars and text painted with <CODE>barColor</CODE></TD> 214 * </TR> 215 * <TR> 216 * <TD><P><CODE>null</CODE></TD> 217 * <TD><P><CODE>textColor</CODE></TD> 218 * <TD><P>bars painted with current color<br>text painted with <CODE>textColor</CODE></TD> 219 * </TR> 220 * <TR> 221 * <TD><P><CODE>barColor</CODE></TD> 222 * <TD><P><CODE>textColor</CODE></TD> 223 * <TD><P>bars painted with <CODE>barColor</CODE><br>text painted with <CODE>textColor</CODE></TD> 224 * </TR> 225 * </TABLE> 226 * @param cb the <CODE>PdfContentByte</CODE> where the barcode will be placed 227 * @param barColor the color of the bars. It can be <CODE>null</CODE> 228 * @param textColor the color of the text. It can be <CODE>null</CODE> 229 * @return the dimensions the barcode occupies 230 */ 231 public Rectangle placeBarcode(PdfContentByte cb, BaseColor barColor, BaseColor textColor) { 232 String fullCode = code; 233 if (generateChecksum && checksumText) 234 fullCode = calculateChecksum(code); 235 if (!startStopText) 236 fullCode = fullCode.substring(1, fullCode.length() - 1); 237 float fontX = 0; 238 if (font != null) { 239 fontX = font.getWidthPoint(fullCode = altText != null ? altText : fullCode, size); 240 } 241 byte bars[] = getBarsCodabar(generateChecksum ? calculateChecksum(code) : code); 242 int wide = 0; 243 for (int k = 0; k < bars.length; ++k) { 244 wide += bars[k]; 245 } 246 int narrow = bars.length - wide; 247 float fullWidth = x * (narrow + wide * n); 248 float barStartX = 0; 249 float textStartX = 0; 250 switch (textAlignment) { 251 case Element.ALIGN_LEFT: 252 break; 253 case Element.ALIGN_RIGHT: 254 if (fontX > fullWidth) 255 barStartX = fontX - fullWidth; 256 else 257 textStartX = fullWidth - fontX; 258 break; 259 default: 260 if (fontX > fullWidth) 261 barStartX = (fontX - fullWidth) / 2; 262 else 263 textStartX = (fullWidth - fontX) / 2; 264 break; 265 } 266 float barStartY = 0; 267 float textStartY = 0; 268 if (font != null) { 269 if (baseline <= 0) 270 textStartY = barHeight - baseline; 271 else { 272 textStartY = -font.getFontDescriptor(BaseFont.DESCENT, size); 273 barStartY = textStartY + baseline; 274 } 275 } 276 boolean print = true; 277 if (barColor != null) 278 cb.setColorFill(barColor); 279 for (int k = 0; k < bars.length; ++k) { 280 float w = (bars[k] == 0 ? x : x * n); 281 if (print) 282 cb.rectangle(barStartX, barStartY, w - inkSpreading, barHeight); 283 print = !print; 284 barStartX += w; 285 } 286 cb.fill(); 287 if (font != null) { 288 if (textColor != null) 289 cb.setColorFill(textColor); 290 cb.beginText(); 291 cb.setFontAndSize(font, size); 292 cb.setTextMatrix(textStartX, textStartY); 293 cb.showText(fullCode); 294 cb.endText(); 295 } 296 return getBarcodeSize(); 297 } 298 299 /** Creates a <CODE>java.awt.Image</CODE>. This image only 300 * contains the bars without any text. 301 * @param foreground the color of the bars 302 * @param background the color of the background 303 * @return the image 304 */ 305 public java.awt.Image createAwtImage(java.awt.Color foreground, java.awt.Color background) { 306 int f = foreground.getRGB(); 307 int g = background.getRGB(); 308 Canvas canvas = new Canvas(); 309 310 String fullCode = code; 311 if (generateChecksum && checksumText) 312 fullCode = calculateChecksum(code); 313 if (!startStopText) 314 fullCode = fullCode.substring(1, fullCode.length() - 1); 315 byte bars[] = getBarsCodabar(generateChecksum ? calculateChecksum(code) : code); 316 int wide = 0; 317 for (int k = 0; k < bars.length; ++k) { 318 wide += bars[k]; 319 } 320 int narrow = bars.length - wide; 321 int fullWidth = narrow + wide * (int)n; 322 boolean print = true; 323 int ptr = 0; 324 int height = (int)barHeight; 325 int pix[] = new int[fullWidth * height]; 326 for (int k = 0; k < bars.length; ++k) { 327 int w = (bars[k] == 0 ? 1 : (int)n); 328 int c = g; 329 if (print) 330 c = f; 331 print = !print; 332 for (int j = 0; j < w; ++j) 333 pix[ptr++] = c; 334 } 335 for (int k = fullWidth; k < pix.length; k += fullWidth) { 336 System.arraycopy(pix, 0, pix, k, fullWidth); 337 } 338 Image img = canvas.createImage(new MemoryImageSource(fullWidth, height, pix, 0, fullWidth)); 339 340 return img; 341 } 342}