001/*
002 * $Id: BarcodeInter25.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 interleaved 2 of 5. The text can include
057 * non numeric characters that are printed but do not generate bars.
058 * The default parameters are:
059 * <pre>
060 *x = 0.8f;
061 *n = 2;
062 *font = BaseFont.createFont("Helvetica", "winansi", false);
063 *size = 8;
064 *baseline = size;
065 *barHeight = size * 3;
066 *textAlignment = Element.ALIGN_CENTER;
067 *generateChecksum = false;
068 *checksumText = false;
069 * </pre>
070 *
071 * @author Paulo Soares
072 */
073public class BarcodeInter25 extends Barcode{
074
075    /** The bars to generate the code.
076     */    
077        private static final byte BARS[][] =
078    {
079        {0,0,1,1,0},
080        {1,0,0,0,1},
081        {0,1,0,0,1},
082        {1,1,0,0,0},
083        {0,0,1,0,1},
084        {1,0,1,0,0},
085        {0,1,1,0,0},
086        {0,0,0,1,1},
087        {1,0,0,1,0},
088        {0,1,0,1,0}
089    };
090
091    /** Creates new BarcodeInter25 */
092    public BarcodeInter25() {
093        try {
094            x = 0.8f;
095            n = 2;
096            font = BaseFont.createFont("Helvetica", "winansi", false);
097            size = 8;
098            baseline = size;
099            barHeight = size * 3;
100            textAlignment = Element.ALIGN_CENTER;
101            generateChecksum = false;
102            checksumText = false;
103        }
104        catch (Exception e) {
105            throw new ExceptionConverter(e);
106        }
107    }
108    
109    /** Deletes all the non numeric characters from <CODE>text</CODE>.
110     * @param text the text
111     * @return a <CODE>String</CODE> with only numeric characters
112     */    
113    public static String keepNumbers(String text) {
114        StringBuffer sb = new StringBuffer();
115        for (int k = 0; k < text.length(); ++k) {
116            char c = text.charAt(k);
117            if (c >= '0' && c <= '9')
118                sb.append(c);
119        }
120        return sb.toString();
121    }
122    
123    /** Calculates the checksum.
124     * @param text the numeric text
125     * @return the checksum
126     */    
127    public static char getChecksum(String text) {
128        int mul = 3;
129        int total = 0;
130        for (int k = text.length() - 1; k >= 0; --k) {
131            int n = text.charAt(k) - '0';
132            total += mul * n;
133            mul ^= 2;
134        }
135        return (char)(((10 - (total % 10)) % 10) + '0');
136    }
137
138    /** Creates the bars for the barcode.
139     * @param text the text. It can contain non numeric characters
140     * @return the barcode
141     */    
142    public static byte[] getBarsInter25(String text) {
143        text = keepNumbers(text);
144        if ((text.length() & 1) != 0)
145            throw new IllegalArgumentException(MessageLocalization.getComposedMessage("the.text.length.must.be.even"));
146        byte bars[] = new byte[text.length() * 5 + 7];
147        int pb = 0;
148        bars[pb++] = 0;
149        bars[pb++] = 0;
150        bars[pb++] = 0;
151        bars[pb++] = 0;
152        int len = text.length() / 2;
153        for (int k = 0; k < len; ++k) {
154            int c1 = text.charAt(k * 2) - '0';
155            int c2 = text.charAt(k * 2 + 1) - '0';
156            byte b1[] = BARS[c1];
157            byte b2[] = BARS[c2];
158            for (int j = 0; j < 5; ++j) {
159                bars[pb++] = b1[j];
160                bars[pb++] = b2[j];
161            }
162        }
163        bars[pb++] = 1;
164        bars[pb++] = 0;
165        bars[pb++] = 0;
166        return bars;
167    }
168
169    /** Gets the maximum area that the barcode and the text, if
170     * any, will occupy. The lower left corner is always (0, 0).
171     * @return the size the barcode occupies.
172     */    
173    public Rectangle getBarcodeSize() {
174        float fontX = 0;
175        float fontY = 0;
176        if (font != null) {
177            if (baseline > 0)
178                fontY = baseline - font.getFontDescriptor(BaseFont.DESCENT, size);
179            else
180                fontY = -baseline + size;
181            String fullCode = code;
182            if (generateChecksum && checksumText)
183                fullCode += getChecksum(fullCode);
184            fontX = font.getWidthPoint(altText != null ? altText : fullCode, size);
185        }
186        String fullCode = keepNumbers(code);
187        int len = fullCode.length();
188        if (generateChecksum)
189            ++len;
190        float fullWidth = len * (3 * x + 2 * x * n) + (6 + n ) * x;
191        fullWidth = Math.max(fullWidth, fontX);
192        float fullHeight = barHeight + fontY;
193        return new Rectangle(fullWidth, fullHeight);
194    }
195    
196    /** Places the barcode in a <CODE>PdfContentByte</CODE>. The
197     * barcode is always placed at coordinates (0, 0). Use the
198     * translation matrix to move it elsewhere.<p>
199     * The bars and text are written in the following colors:<p>
200     * <P><TABLE BORDER=1>
201     * <TR>
202     *    <TH><P><CODE>barColor</CODE></TH>
203     *    <TH><P><CODE>textColor</CODE></TH>
204     *    <TH><P>Result</TH>
205     *    </TR>
206     * <TR>
207     *    <TD><P><CODE>null</CODE></TD>
208     *    <TD><P><CODE>null</CODE></TD>
209     *    <TD><P>bars and text painted with current fill color</TD>
210     *    </TR>
211     * <TR>
212     *    <TD><P><CODE>barColor</CODE></TD>
213     *    <TD><P><CODE>null</CODE></TD>
214     *    <TD><P>bars and text painted with <CODE>barColor</CODE></TD>
215     *    </TR>
216     * <TR>
217     *    <TD><P><CODE>null</CODE></TD>
218     *    <TD><P><CODE>textColor</CODE></TD>
219     *    <TD><P>bars painted with current color<br>text painted with <CODE>textColor</CODE></TD>
220     *    </TR>
221     * <TR>
222     *    <TD><P><CODE>barColor</CODE></TD>
223     *    <TD><P><CODE>textColor</CODE></TD>
224     *    <TD><P>bars painted with <CODE>barColor</CODE><br>text painted with <CODE>textColor</CODE></TD>
225     *    </TR>
226     * </TABLE>
227     * @param cb the <CODE>PdfContentByte</CODE> where the barcode will be placed
228     * @param barColor the color of the bars. It can be <CODE>null</CODE>
229     * @param textColor the color of the text. It can be <CODE>null</CODE>
230     * @return the dimensions the barcode occupies
231     */    
232    public Rectangle placeBarcode(PdfContentByte cb, BaseColor barColor, BaseColor textColor) {
233        String fullCode = code;
234        float fontX = 0;
235        if (font != null) {
236            if (generateChecksum && checksumText)
237                fullCode += getChecksum(fullCode);
238            fontX = font.getWidthPoint(fullCode = altText != null ? altText : fullCode, size);
239        }
240        String bCode = keepNumbers(code);
241        if (generateChecksum)
242            bCode += getChecksum(bCode);
243        int len = bCode.length();
244        float fullWidth = len * (3 * x + 2 * x * n) + (6 + n ) * x;
245        float barStartX = 0;
246        float textStartX = 0;
247        switch (textAlignment) {
248            case Element.ALIGN_LEFT:
249                break;
250            case Element.ALIGN_RIGHT:
251                if (fontX > fullWidth)
252                    barStartX = fontX - fullWidth;
253                else
254                    textStartX = fullWidth - fontX;
255                break;
256            default:
257                if (fontX > fullWidth)
258                    barStartX = (fontX - fullWidth) / 2;
259                else
260                    textStartX = (fullWidth - fontX) / 2;
261                break;
262        }
263        float barStartY = 0;
264        float textStartY = 0;
265        if (font != null) {
266            if (baseline <= 0)
267                textStartY = barHeight - baseline;
268            else {
269                textStartY = -font.getFontDescriptor(BaseFont.DESCENT, size);
270                barStartY = textStartY + baseline;
271            }
272        }
273        byte bars[] = getBarsInter25(bCode);
274        boolean print = true;
275        if (barColor != null)
276            cb.setColorFill(barColor);
277        for (int k = 0; k < bars.length; ++k) {
278            float w = (bars[k] == 0 ? x : x * n);
279            if (print)
280                cb.rectangle(barStartX, barStartY, w - inkSpreading, barHeight);
281            print = !print;
282            barStartX += w;
283        }
284        cb.fill();
285        if (font != null) {
286            if (textColor != null)
287                cb.setColorFill(textColor);
288            cb.beginText();
289            cb.setFontAndSize(font, size);
290            cb.setTextMatrix(textStartX, textStartY);
291            cb.showText(fullCode);
292            cb.endText();
293        }
294        return getBarcodeSize();
295    }   
296    
297    /** Creates a <CODE>java.awt.Image</CODE>. This image only
298     * contains the bars without any text.
299     * @param foreground the color of the bars
300     * @param background the color of the background
301     * @return the image
302     */    
303    public java.awt.Image createAwtImage(java.awt.Color foreground, java.awt.Color background) {
304        int f = foreground.getRGB();
305        int g = background.getRGB();
306        Canvas canvas = new Canvas();
307
308        String bCode = keepNumbers(code);
309        if (generateChecksum)
310            bCode += getChecksum(bCode);
311        int len = bCode.length();
312        int nn = (int)n;
313        int fullWidth = len * (3 + 2 * nn) + (6 + nn );
314        byte bars[] = getBarsInter25(bCode);
315        boolean print = true;
316        int ptr = 0;
317        int height = (int)barHeight;
318        int pix[] = new int[fullWidth * height];
319        for (int k = 0; k < bars.length; ++k) {
320            int w = (bars[k] == 0 ? 1 : nn);
321            int c = g;
322            if (print)
323                c = f;
324            print = !print;
325            for (int j = 0; j < w; ++j)
326                pix[ptr++] = c;
327        }
328        for (int k = fullWidth; k < pix.length; k += fullWidth) {
329            System.arraycopy(pix, 0, pix, k, fullWidth); 
330        }
331        Image img = canvas.createImage(new MemoryImageSource(fullWidth, height, pix, 0, fullWidth));
332        
333        return img;
334    }    
335}