001: /*
002: * $Id: Barcode39.java 2737 2007-05-03 19:35:09Z psoares33 $
003: *
004: * Copyright 2002-2006 by Paulo Soares.
005: *
006: * The contents of this file are subject to the Mozilla Public License Version 1.1
007: * (the "License"); you may not use this file except in compliance with the License.
008: * You may obtain a copy of the License at http://www.mozilla.org/MPL/
009: *
010: * Software distributed under the License is distributed on an "AS IS" basis,
011: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
012: * for the specific language governing rights and limitations under the License.
013: *
014: * The Original Code is 'iText, a free JAVA-PDF library'.
015: *
016: * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
017: * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
018: * All Rights Reserved.
019: * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
020: * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
021: *
022: * Contributor(s): all the names of the contributors are added in the source code
023: * where applicable.
024: *
025: * Alternatively, the contents of this file may be used under the terms of the
026: * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
027: * provisions of LGPL are applicable instead of those above. If you wish to
028: * allow use of your version of this file only under the terms of the LGPL
029: * License and not to allow others to use your version of this file under
030: * the MPL, indicate your decision by deleting the provisions above and
031: * replace them with the notice and other provisions required by the LGPL.
032: * If you do not delete the provisions above, a recipient may use your version
033: * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
034: *
035: * This library is free software; you can redistribute it and/or modify it
036: * under the terms of the MPL as stated above or under the terms of the GNU
037: * Library General Public License as published by the Free Software Foundation;
038: * either version 2 of the License, or any later version.
039: *
040: * This library is distributed in the hope that it will be useful, but WITHOUT
041: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
042: * FOR A PARTICULAR PURPOSE. See the GNU Library general Public License for more
043: * details.
044: *
045: * If you didn't download this code from the following link, you should check if
046: * you aren't using an obsolete version:
047: * http://www.lowagie.com/iText/
048: */
049: package com.lowagie.text.pdf;
050:
051: import java.awt.Canvas;
052: import java.awt.Color;
053: import java.awt.Image;
054: import java.awt.image.MemoryImageSource;
055:
056: import com.lowagie.text.Element;
057: import com.lowagie.text.ExceptionConverter;
058: import com.lowagie.text.Rectangle;
059:
060: /** Implements the code 39 and code 39 extended. The default parameters are:
061: * <pre>
062: *x = 0.8f;
063: *n = 2;
064: *font = BaseFont.createFont("Helvetica", "winansi", false);
065: *size = 8;
066: *baseline = size;
067: *barHeight = size * 3;
068: *textAlignment = Element.ALIGN_CENTER;
069: *generateChecksum = false;
070: *checksumText = false;
071: *startStopText = true;
072: *extended = false;
073: * </pre>
074: *
075: * @author Paulo Soares (psoares@consiste.pt)
076: */
077: public class Barcode39 extends Barcode {
078:
079: /** The bars to generate the code.
080: */
081: private static final byte BARS[][] = {
082: { 0, 0, 0, 1, 1, 0, 1, 0, 0 },
083: { 1, 0, 0, 1, 0, 0, 0, 0, 1 },
084: { 0, 0, 1, 1, 0, 0, 0, 0, 1 },
085: { 1, 0, 1, 1, 0, 0, 0, 0, 0 },
086: { 0, 0, 0, 1, 1, 0, 0, 0, 1 },
087: { 1, 0, 0, 1, 1, 0, 0, 0, 0 },
088: { 0, 0, 1, 1, 1, 0, 0, 0, 0 },
089: { 0, 0, 0, 1, 0, 0, 1, 0, 1 },
090: { 1, 0, 0, 1, 0, 0, 1, 0, 0 },
091: { 0, 0, 1, 1, 0, 0, 1, 0, 0 },
092: { 1, 0, 0, 0, 0, 1, 0, 0, 1 },
093: { 0, 0, 1, 0, 0, 1, 0, 0, 1 },
094: { 1, 0, 1, 0, 0, 1, 0, 0, 0 },
095: { 0, 0, 0, 0, 1, 1, 0, 0, 1 },
096: { 1, 0, 0, 0, 1, 1, 0, 0, 0 },
097: { 0, 0, 1, 0, 1, 1, 0, 0, 0 },
098: { 0, 0, 0, 0, 0, 1, 1, 0, 1 },
099: { 1, 0, 0, 0, 0, 1, 1, 0, 0 },
100: { 0, 0, 1, 0, 0, 1, 1, 0, 0 },
101: { 0, 0, 0, 0, 1, 1, 1, 0, 0 },
102: { 1, 0, 0, 0, 0, 0, 0, 1, 1 },
103: { 0, 0, 1, 0, 0, 0, 0, 1, 1 },
104: { 1, 0, 1, 0, 0, 0, 0, 1, 0 },
105: { 0, 0, 0, 0, 1, 0, 0, 1, 1 },
106: { 1, 0, 0, 0, 1, 0, 0, 1, 0 },
107: { 0, 0, 1, 0, 1, 0, 0, 1, 0 },
108: { 0, 0, 0, 0, 0, 0, 1, 1, 1 },
109: { 1, 0, 0, 0, 0, 0, 1, 1, 0 },
110: { 0, 0, 1, 0, 0, 0, 1, 1, 0 },
111: { 0, 0, 0, 0, 1, 0, 1, 1, 0 },
112: { 1, 1, 0, 0, 0, 0, 0, 0, 1 },
113: { 0, 1, 1, 0, 0, 0, 0, 0, 1 },
114: { 1, 1, 1, 0, 0, 0, 0, 0, 0 },
115: { 0, 1, 0, 0, 1, 0, 0, 0, 1 },
116: { 1, 1, 0, 0, 1, 0, 0, 0, 0 },
117: { 0, 1, 1, 0, 1, 0, 0, 0, 0 },
118: { 0, 1, 0, 0, 0, 0, 1, 0, 1 },
119: { 1, 1, 0, 0, 0, 0, 1, 0, 0 },
120: { 0, 1, 1, 0, 0, 0, 1, 0, 0 },
121: { 0, 1, 0, 1, 0, 1, 0, 0, 0 },
122: { 0, 1, 0, 1, 0, 0, 0, 1, 0 },
123: { 0, 1, 0, 0, 0, 1, 0, 1, 0 },
124: { 0, 0, 0, 1, 0, 1, 0, 1, 0 },
125: { 0, 1, 0, 0, 1, 0, 1, 0, 0 } };
126:
127: /** The index chars to <CODE>BARS</CODE>.
128: */
129: private static final String CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. $/+%*";
130:
131: /** The character combinations to make the code 39 extended.
132: */
133: private static final String EXTENDED = "%U"
134: + "$A$B$C$D$E$F$G$H$I$J$K$L$M$N$O$P$Q$R$S$T$U$V$W$X$Y$Z"
135: + "%A%B%C%D%E /A/B/C/D/E/F/G/H/I/J/K/L - ./O"
136: + " 0 1 2 3 4 5 6 7 8 9/Z%F%G%H%I%J%V"
137: + " A B C D E F G H I J K L M N O P Q R S T U V W X Y Z"
138: + "%K%L%M%N%O%W"
139: + "+A+B+C+D+E+F+G+H+I+J+K+L+M+N+O+P+Q+R+S+T+U+V+W+X+Y+Z"
140: + "%P%Q%R%S%T";
141:
142: /** Creates a new Barcode39.
143: */
144: public Barcode39() {
145: try {
146: x = 0.8f;
147: n = 2;
148: font = BaseFont.createFont("Helvetica", "winansi", false);
149: size = 8;
150: baseline = size;
151: barHeight = size * 3;
152: textAlignment = Element.ALIGN_CENTER;
153: generateChecksum = false;
154: checksumText = false;
155: startStopText = true;
156: extended = false;
157: } catch (Exception e) {
158: throw new ExceptionConverter(e);
159: }
160: }
161:
162: /** Creates the bars.
163: * @param text the text to create the bars. This text does not include the start and
164: * stop characters
165: * @return the bars
166: */
167: public static byte[] getBarsCode39(String text) {
168: text = "*" + text + "*";
169: byte bars[] = new byte[text.length() * 10 - 1];
170: for (int k = 0; k < text.length(); ++k) {
171: int idx = CHARS.indexOf(text.charAt(k));
172: if (idx < 0)
173: throw new IllegalArgumentException("The character '"
174: + text.charAt(k) + "' is illegal in code 39.");
175: System.arraycopy(BARS[idx], 0, bars, k * 10, 9);
176: }
177: return bars;
178: }
179:
180: /** Converts the extended text into a normal, escaped text,
181: * ready to generate bars.
182: * @param text the extended text
183: * @return the escaped text
184: */
185: public static String getCode39Ex(String text) {
186: String out = "";
187: for (int k = 0; k < text.length(); ++k) {
188: char c = text.charAt(k);
189: if (c > 127)
190: throw new IllegalArgumentException("The character '"
191: + c + "' is illegal in code 39 extended.");
192: char c1 = EXTENDED.charAt(c * 2);
193: char c2 = EXTENDED.charAt(c * 2 + 1);
194: if (c1 != ' ')
195: out += c1;
196: out += c2;
197: }
198: return out;
199: }
200:
201: /** Calculates the checksum.
202: * @param text the text
203: * @return the checksum
204: */
205: static char getChecksum(String text) {
206: int chk = 0;
207: for (int k = 0; k < text.length(); ++k) {
208: int idx = CHARS.indexOf(text.charAt(k));
209: if (idx < 0)
210: throw new IllegalArgumentException("The character '"
211: + text.charAt(k) + "' is illegal in code 39.");
212: chk += idx;
213: }
214: return CHARS.charAt(chk % 43);
215: }
216:
217: /** Gets the maximum area that the barcode and the text, if
218: * any, will occupy. The lower left corner is always (0, 0).
219: * @return the size the barcode occupies.
220: */
221: public Rectangle getBarcodeSize() {
222: float fontX = 0;
223: float fontY = 0;
224: String fCode = code;
225: if (extended)
226: fCode = getCode39Ex(code);
227: if (font != null) {
228: if (baseline > 0)
229: fontY = baseline
230: - font
231: .getFontDescriptor(BaseFont.DESCENT,
232: size);
233: else
234: fontY = -baseline + size;
235: String fullCode = code;
236: if (generateChecksum && checksumText)
237: fullCode += getChecksum(fCode);
238: if (startStopText)
239: fullCode = "*" + fullCode + "*";
240: fontX = font.getWidthPoint(altText != null ? altText
241: : fullCode, size);
242: }
243: int len = fCode.length() + 2;
244: if (generateChecksum)
245: ++len;
246: float fullWidth = len * (6 * x + 3 * x * n) + (len - 1) * x;
247: fullWidth = Math.max(fullWidth, fontX);
248: float fullHeight = barHeight + fontY;
249: return new Rectangle(fullWidth, fullHeight);
250: }
251:
252: /** Places the barcode in a <CODE>PdfContentByte</CODE>. The
253: * barcode is always placed at coodinates (0, 0). Use the
254: * translation matrix to move it elsewhere.<p>
255: * The bars and text are written in the following colors:<p>
256: * <P><TABLE BORDER=1>
257: * <TR>
258: * <TH><P><CODE>barColor</CODE></TH>
259: * <TH><P><CODE>textColor</CODE></TH>
260: * <TH><P>Result</TH>
261: * </TR>
262: * <TR>
263: * <TD><P><CODE>null</CODE></TD>
264: * <TD><P><CODE>null</CODE></TD>
265: * <TD><P>bars and text painted with current fill color</TD>
266: * </TR>
267: * <TR>
268: * <TD><P><CODE>barColor</CODE></TD>
269: * <TD><P><CODE>null</CODE></TD>
270: * <TD><P>bars and text painted with <CODE>barColor</CODE></TD>
271: * </TR>
272: * <TR>
273: * <TD><P><CODE>null</CODE></TD>
274: * <TD><P><CODE>textColor</CODE></TD>
275: * <TD><P>bars painted with current color<br>text painted with <CODE>textColor</CODE></TD>
276: * </TR>
277: * <TR>
278: * <TD><P><CODE>barColor</CODE></TD>
279: * <TD><P><CODE>textColor</CODE></TD>
280: * <TD><P>bars painted with <CODE>barColor</CODE><br>text painted with <CODE>textColor</CODE></TD>
281: * </TR>
282: * </TABLE>
283: * @param cb the <CODE>PdfContentByte</CODE> where the barcode will be placed
284: * @param barColor the color of the bars. It can be <CODE>null</CODE>
285: * @param textColor the color of the text. It can be <CODE>null</CODE>
286: * @return the dimensions the barcode occupies
287: */
288: public Rectangle placeBarcode(PdfContentByte cb, Color barColor,
289: Color textColor) {
290: String fullCode = code;
291: float fontX = 0;
292: String bCode = code;
293: if (extended)
294: bCode = getCode39Ex(code);
295: if (font != null) {
296: if (generateChecksum && checksumText)
297: fullCode += getChecksum(bCode);
298: if (startStopText)
299: fullCode = "*" + fullCode + "*";
300: fontX = font.getWidthPoint(
301: fullCode = altText != null ? altText : fullCode,
302: size);
303: }
304: if (generateChecksum)
305: bCode += getChecksum(bCode);
306: int len = bCode.length() + 2;
307: float fullWidth = len * (6 * x + 3 * x * n) + (len - 1) * x;
308: float barStartX = 0;
309: float textStartX = 0;
310: switch (textAlignment) {
311: case Element.ALIGN_LEFT:
312: break;
313: case Element.ALIGN_RIGHT:
314: if (fontX > fullWidth)
315: barStartX = fontX - fullWidth;
316: else
317: textStartX = fullWidth - fontX;
318: break;
319: default:
320: if (fontX > fullWidth)
321: barStartX = (fontX - fullWidth) / 2;
322: else
323: textStartX = (fullWidth - fontX) / 2;
324: break;
325: }
326: float barStartY = 0;
327: float textStartY = 0;
328: if (font != null) {
329: if (baseline <= 0)
330: textStartY = barHeight - baseline;
331: else {
332: textStartY = -font.getFontDescriptor(BaseFont.DESCENT,
333: size);
334: barStartY = textStartY + baseline;
335: }
336: }
337: byte bars[] = getBarsCode39(bCode);
338: boolean print = true;
339: if (barColor != null)
340: cb.setColorFill(barColor);
341: for (int k = 0; k < bars.length; ++k) {
342: float w = (bars[k] == 0 ? x : x * n);
343: if (print)
344: cb.rectangle(barStartX, barStartY, w - inkSpreading,
345: barHeight);
346: print = !print;
347: barStartX += w;
348: }
349: cb.fill();
350: if (font != null) {
351: if (textColor != null)
352: cb.setColorFill(textColor);
353: cb.beginText();
354: cb.setFontAndSize(font, size);
355: cb.setTextMatrix(textStartX, textStartY);
356: cb.showText(fullCode);
357: cb.endText();
358: }
359: return getBarcodeSize();
360: }
361:
362: /** Creates a <CODE>java.awt.Image</CODE>. This image only
363: * contains the bars without any text.
364: * @param foreground the color of the bars
365: * @param background the color of the background
366: * @return the image
367: */
368: public java.awt.Image createAwtImage(Color foreground,
369: Color background) {
370: int f = foreground.getRGB();
371: int g = background.getRGB();
372: Canvas canvas = new Canvas();
373:
374: String bCode = code;
375: if (extended)
376: bCode = getCode39Ex(code);
377: if (generateChecksum)
378: bCode += getChecksum(bCode);
379: int len = bCode.length() + 2;
380: int nn = (int) n;
381: int fullWidth = len * (6 + 3 * nn) + (len - 1);
382: byte bars[] = getBarsCode39(bCode);
383: boolean print = true;
384: int ptr = 0;
385: int height = (int) barHeight;
386: int pix[] = new int[fullWidth * height];
387: for (int k = 0; k < bars.length; ++k) {
388: int w = (bars[k] == 0 ? 1 : nn);
389: int c = g;
390: if (print)
391: c = f;
392: print = !print;
393: for (int j = 0; j < w; ++j)
394: pix[ptr++] = c;
395: }
396: for (int k = fullWidth; k < pix.length; k += fullWidth) {
397: System.arraycopy(pix, 0, pix, k, fullWidth);
398: }
399: Image img = canvas.createImage(new MemoryImageSource(fullWidth,
400: height, pix, 0, fullWidth));
401:
402: return img;
403: }
404: }
|