001/*
002 * $Id: BarcodeEAN.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.MemoryImageSource;
048import java.util.Arrays;
049import com.itextpdf.text.error_messages.MessageLocalization;
050
051import com.itextpdf.text.ExceptionConverter;
052import com.itextpdf.text.Rectangle;
053import com.itextpdf.text.BaseColor;
054
055/** Generates barcodes in several formats: EAN13, EAN8, UPCA, UPCE,
056 * supplemental 2 and 5. The default parameters are:
057 * <pre>
058 *x = 0.8f;
059 *font = BaseFont.createFont("Helvetica", "winansi", false);
060 *size = 8;
061 *baseline = size;
062 *barHeight = size * 3;
063 *guardBars = true;
064 *codeType = EAN13;
065 *code = "";
066 * </pre>
067 *
068 * @author Paulo Soares
069 */
070public class BarcodeEAN extends Barcode{
071        
072    /** The bar positions that are guard bars.*/    
073        private static final int GUARD_EMPTY[] = {};
074    /** The bar positions that are guard bars.*/    
075        private static final int GUARD_UPCA[] = {0, 2, 4, 6, 28, 30, 52, 54, 56, 58};
076    /** The bar positions that are guard bars.*/    
077        private static final int GUARD_EAN13[] = {0, 2, 28, 30, 56, 58};
078    /** The bar positions that are guard bars.*/    
079        private static final int GUARD_EAN8[] = {0, 2, 20, 22, 40, 42};
080    /** The bar positions that are guard bars.*/    
081        private static final int GUARD_UPCE[] = {0, 2, 28, 30, 32};
082    /** The x coordinates to place the text.*/
083        private static final float TEXTPOS_EAN13[] = {6.5f, 13.5f, 20.5f, 27.5f, 34.5f, 41.5f, 53.5f, 60.5f, 67.5f, 74.5f, 81.5f, 88.5f};
084    /** The x coordinates to place the text.*/
085        private static final float TEXTPOS_EAN8[] = {6.5f, 13.5f, 20.5f, 27.5f, 39.5f, 46.5f, 53.5f, 60.5f};
086    /** The basic bar widths.*/
087        private static final byte BARS[][] = 
088    {
089        {3, 2, 1, 1}, // 0
090        {2, 2, 2, 1}, // 1
091        {2, 1, 2, 2}, // 2
092        {1, 4, 1, 1}, // 3
093        {1, 1, 3, 2}, // 4
094        {1, 2, 3, 1}, // 5
095        {1, 1, 1, 4}, // 6
096        {1, 3, 1, 2}, // 7
097        {1, 2, 1, 3}, // 8
098        {3, 1, 1, 2}  // 9
099    };
100    
101    /** The total number of bars for EAN13.*/
102        private static final int TOTALBARS_EAN13 = 11 + 12 * 4;
103    /** The total number of bars for EAN8.*/
104        private static final int TOTALBARS_EAN8 = 11 + 8 * 4;
105    /** The total number of bars for UPCE.*/
106        private static final int TOTALBARS_UPCE = 9 + 6 * 4;
107    /** The total number of bars for supplemental 2.*/
108        private static final int TOTALBARS_SUPP2 = 13;
109    /** The total number of bars for supplemental 5.*/
110        private static final int TOTALBARS_SUPP5 = 31;
111    /** Marker for odd parity.*/
112        private static final int ODD = 0;
113    /** Marker for even parity.*/
114        private static final int EVEN = 1;
115    
116    /** Sequence of parities to be used with EAN13.*/
117    private static final byte PARITY13[][] =
118    {
119        {ODD, ODD,  ODD,  ODD,  ODD,  ODD},  // 0
120        {ODD, ODD,  EVEN, ODD,  EVEN, EVEN}, // 1
121        {ODD, ODD,  EVEN, EVEN, ODD,  EVEN}, // 2
122        {ODD, ODD,  EVEN, EVEN, EVEN, ODD},  // 3
123        {ODD, EVEN, ODD,  ODD,  EVEN, EVEN}, // 4
124        {ODD, EVEN, EVEN, ODD,  ODD,  EVEN}, // 5
125        {ODD, EVEN, EVEN, EVEN, ODD,  ODD},  // 6
126        {ODD, EVEN, ODD,  EVEN, ODD,  EVEN}, // 7
127        {ODD, EVEN, ODD,  EVEN, EVEN, ODD},  // 8
128        {ODD, EVEN, EVEN, ODD,  EVEN, ODD}   // 9
129    };
130    
131    /** Sequence of parities to be used with supplemental 2.*/
132    private static final byte PARITY2[][] =
133    {
134        {ODD,  ODD},   // 0
135        {ODD,  EVEN},  // 1
136        {EVEN, ODD},   // 2
137        {EVEN, EVEN}   // 3
138    };
139    
140    /** Sequence of parities to be used with supplemental 2.*/
141    private static final byte PARITY5[][] =
142    {
143        {EVEN, EVEN, ODD,  ODD,  ODD},  // 0
144        {EVEN, ODD,  EVEN, ODD,  ODD},  // 1
145        {EVEN, ODD,  ODD,  EVEN, ODD},  // 2
146        {EVEN, ODD,  ODD,  ODD,  EVEN}, // 3
147        {ODD,  EVEN, EVEN, ODD,  ODD},  // 4
148        {ODD,  ODD,  EVEN, EVEN, ODD},  // 5
149        {ODD,  ODD,  ODD,  EVEN, EVEN}, // 6
150        {ODD,  EVEN, ODD,  EVEN, ODD},  // 7
151        {ODD,  EVEN, ODD,  ODD,  EVEN}, // 8
152        {ODD,  ODD,  EVEN, ODD,  EVEN}  // 9
153    };
154    
155    /** Sequence of parities to be used with UPCE.*/
156    private static final byte PARITYE[][] =
157    {
158        {EVEN, EVEN, EVEN, ODD,  ODD,  ODD},  // 0
159        {EVEN, EVEN, ODD,  EVEN, ODD,  ODD},  // 1
160        {EVEN, EVEN, ODD,  ODD,  EVEN, ODD},  // 2
161        {EVEN, EVEN, ODD,  ODD,  ODD,  EVEN}, // 3
162        {EVEN, ODD,  EVEN, EVEN, ODD,  ODD},  // 4
163        {EVEN, ODD,  ODD,  EVEN, EVEN, ODD},  // 5
164        {EVEN, ODD,  ODD,  ODD,  EVEN, EVEN}, // 6
165        {EVEN, ODD,  EVEN, ODD,  EVEN, ODD},  // 7
166        {EVEN, ODD,  EVEN, ODD,  ODD,  EVEN}, // 8
167        {EVEN, ODD,  ODD,  EVEN, ODD,  EVEN}  // 9
168    };
169    
170    /** Creates new BarcodeEAN */
171    public BarcodeEAN() {
172        try {
173            x = 0.8f;
174            font = BaseFont.createFont("Helvetica", "winansi", false);
175            size = 8;
176            baseline = size;
177            barHeight = size * 3;
178            guardBars = true;
179            codeType = EAN13;
180            code = "";
181        }
182        catch (Exception e) {
183            throw new ExceptionConverter(e);
184        }
185    }
186    
187    /** Calculates the EAN parity character.
188     * @param code the code
189     * @return the parity character
190     */    
191    public static int calculateEANParity(String code) {
192        int mul = 3;
193        int total = 0;
194        for (int k = code.length() - 1; k >= 0; --k) {
195            int n = code.charAt(k) - '0';
196            total += mul * n;
197            mul ^= 2;
198        }
199        return (10 - (total % 10)) % 10;
200    }
201    
202    /** Converts an UPCA code into an UPCE code. If the code can not
203     * be converted a <CODE>null</CODE> is returned.
204     * @param text the code to convert. It must have 12 numeric characters
205     * @return the 8 converted digits or <CODE>null</CODE> if the
206     * code could not be converted
207     */    
208    static public String convertUPCAtoUPCE(String text) {
209        if (text.length() != 12 || !(text.startsWith("0") || text.startsWith("1")))
210            return null;
211        if (text.substring(3, 6).equals("000") || text.substring(3, 6).equals("100")
212            || text.substring(3, 6).equals("200")) {
213                if (text.substring(6, 8).equals("00"))
214                    return text.substring(0, 1) + text.substring(1, 3) + text.substring(8, 11) + text.substring(3, 4) + text.substring(11);
215        }
216        else if (text.substring(4, 6).equals("00")) {
217            if (text.substring(6, 9).equals("000"))
218                return text.substring(0, 1) + text.substring(1, 4) + text.substring(9, 11) + "3" + text.substring(11);
219        }
220        else if (text.substring(5, 6).equals("0")) {
221            if (text.substring(6, 10).equals("0000"))
222                return text.substring(0, 1) + text.substring(1, 5) + text.substring(10, 11) + "4" + text.substring(11);
223        }
224        else if (text.charAt(10) >= '5') {
225            if (text.substring(6, 10).equals("0000"))
226                return text.substring(0, 1) + text.substring(1, 6) + text.substring(10, 11) + text.substring(11);
227        }
228        return null;
229    }
230    
231    /** Creates the bars for the barcode EAN13 and UPCA.
232     * @param _code the text with 13 digits
233     * @return the barcode
234     */    
235    public static byte[] getBarsEAN13(String _code) {
236        int code[] = new int[_code.length()];
237        for (int k = 0; k < code.length; ++k)
238            code[k] = _code.charAt(k) - '0';
239        byte bars[] = new byte[TOTALBARS_EAN13];
240        int pb = 0;
241        bars[pb++] = 1;
242        bars[pb++] = 1;
243        bars[pb++] = 1;
244        byte sequence[] = PARITY13[code[0]];
245        for (int k = 0; k < sequence.length; ++k) {
246            int c = code[k + 1];
247            byte stripes[] = BARS[c];
248            if (sequence[k] == ODD) {
249                bars[pb++] = stripes[0];
250                bars[pb++] = stripes[1];
251                bars[pb++] = stripes[2];
252                bars[pb++] = stripes[3];
253            }
254            else {
255                bars[pb++] = stripes[3];
256                bars[pb++] = stripes[2];
257                bars[pb++] = stripes[1];
258                bars[pb++] = stripes[0];
259            }
260        }
261        bars[pb++] = 1;
262        bars[pb++] = 1;
263        bars[pb++] = 1;
264        bars[pb++] = 1;
265        bars[pb++] = 1;
266        for (int k = 7; k < 13; ++k) {
267            int c = code[k];
268            byte stripes[] = BARS[c];
269            bars[pb++] = stripes[0];
270            bars[pb++] = stripes[1];
271            bars[pb++] = stripes[2];
272            bars[pb++] = stripes[3];
273        }
274        bars[pb++] = 1;
275        bars[pb++] = 1;
276        bars[pb++] = 1;
277        return bars;
278    }
279    
280    /** Creates the bars for the barcode EAN8.
281     * @param _code the text with 8 digits
282     * @return the barcode
283     */    
284    public static byte[] getBarsEAN8(String _code) {
285        int code[] = new int[_code.length()];
286        for (int k = 0; k < code.length; ++k)
287            code[k] = _code.charAt(k) - '0';
288        byte bars[] = new byte[TOTALBARS_EAN8];
289        int pb = 0;
290        bars[pb++] = 1;
291        bars[pb++] = 1;
292        bars[pb++] = 1;
293        for (int k = 0; k < 4; ++k) {
294            int c = code[k];
295            byte stripes[] = BARS[c];
296            bars[pb++] = stripes[0];
297            bars[pb++] = stripes[1];
298            bars[pb++] = stripes[2];
299            bars[pb++] = stripes[3];
300        }
301        bars[pb++] = 1;
302        bars[pb++] = 1;
303        bars[pb++] = 1;
304        bars[pb++] = 1;
305        bars[pb++] = 1;
306        for (int k = 4; k < 8; ++k) {
307            int c = code[k];
308            byte stripes[] = BARS[c];
309            bars[pb++] = stripes[0];
310            bars[pb++] = stripes[1];
311            bars[pb++] = stripes[2];
312            bars[pb++] = stripes[3];
313        }
314        bars[pb++] = 1;
315        bars[pb++] = 1;
316        bars[pb++] = 1;
317        return bars;
318    }
319    
320    /** Creates the bars for the barcode UPCE.
321     * @param _code the text with 8 digits
322     * @return the barcode
323     */    
324    public static byte[] getBarsUPCE(String _code) {
325        int code[] = new int[_code.length()];
326        for (int k = 0; k < code.length; ++k)
327            code[k] = _code.charAt(k) - '0';
328        byte bars[] = new byte[TOTALBARS_UPCE];
329        boolean flip = (code[0] != 0);
330        int pb = 0;
331        bars[pb++] = 1;
332        bars[pb++] = 1;
333        bars[pb++] = 1;
334        byte sequence[] = PARITYE[code[code.length - 1]];
335        for (int k = 1; k < code.length - 1; ++k) {
336            int c = code[k];
337            byte stripes[] = BARS[c];
338            if (sequence[k - 1] == (flip ? EVEN : ODD)) {
339                bars[pb++] = stripes[0];
340                bars[pb++] = stripes[1];
341                bars[pb++] = stripes[2];
342                bars[pb++] = stripes[3];
343            }
344            else {
345                bars[pb++] = stripes[3];
346                bars[pb++] = stripes[2];
347                bars[pb++] = stripes[1];
348                bars[pb++] = stripes[0];
349            }
350        }
351        bars[pb++] = 1;
352        bars[pb++] = 1;
353        bars[pb++] = 1;
354        bars[pb++] = 1;
355        bars[pb++] = 1;
356        bars[pb++] = 1;
357        return bars;
358    }
359
360    /** Creates the bars for the barcode supplemental 2.
361     * @param _code the text with 2 digits
362     * @return the barcode
363     */    
364    public static byte[] getBarsSupplemental2(String _code) {
365        int code[] = new int[2];
366        for (int k = 0; k < code.length; ++k)
367            code[k] = _code.charAt(k) - '0';
368        byte bars[] = new byte[TOTALBARS_SUPP2];
369        int pb = 0;
370        int parity = (code[0] * 10 + code[1]) % 4;
371        bars[pb++] = 1;
372        bars[pb++] = 1;
373        bars[pb++] = 2;
374        byte sequence[] = PARITY2[parity];
375        for (int k = 0; k < sequence.length; ++k) {
376            if (k == 1) {
377                bars[pb++] = 1;
378                bars[pb++] = 1;
379            }
380            int c = code[k];
381            byte stripes[] = BARS[c];
382            if (sequence[k] == ODD) {
383                bars[pb++] = stripes[0];
384                bars[pb++] = stripes[1];
385                bars[pb++] = stripes[2];
386                bars[pb++] = stripes[3];
387            }
388            else {
389                bars[pb++] = stripes[3];
390                bars[pb++] = stripes[2];
391                bars[pb++] = stripes[1];
392                bars[pb++] = stripes[0];
393            }
394        }
395        return bars;
396    }   
397
398    /** Creates the bars for the barcode supplemental 5.
399     * @param _code the text with 5 digits
400     * @return the barcode
401     */    
402    public static byte[] getBarsSupplemental5(String _code) {
403        int code[] = new int[5];
404        for (int k = 0; k < code.length; ++k)
405            code[k] = _code.charAt(k) - '0';
406        byte bars[] = new byte[TOTALBARS_SUPP5];
407        int pb = 0;
408        int parity = (((code[0] + code[2] + code[4]) * 3) + ((code[1] + code[3]) * 9)) % 10;
409        bars[pb++] = 1;
410        bars[pb++] = 1;
411        bars[pb++] = 2;
412        byte sequence[] = PARITY5[parity];
413        for (int k = 0; k < sequence.length; ++k) {
414            if (k != 0) {
415                bars[pb++] = 1;
416                bars[pb++] = 1;
417            }
418            int c = code[k];
419            byte stripes[] = BARS[c];
420            if (sequence[k] == ODD) {
421                bars[pb++] = stripes[0];
422                bars[pb++] = stripes[1];
423                bars[pb++] = stripes[2];
424                bars[pb++] = stripes[3];
425            }
426            else {
427                bars[pb++] = stripes[3];
428                bars[pb++] = stripes[2];
429                bars[pb++] = stripes[1];
430                bars[pb++] = stripes[0];
431            }
432        }
433        return bars;
434    }   
435    
436    /** Gets the maximum area that the barcode and the text, if
437     * any, will occupy. The lower left corner is always (0, 0).
438     * @return the size the barcode occupies.
439     */    
440    public Rectangle getBarcodeSize() {
441        float width = 0;
442        float height = barHeight;
443        if (font != null) {
444            if (baseline <= 0)
445                height += -baseline + size;
446            else
447                height += baseline - font.getFontDescriptor(BaseFont.DESCENT, size);
448        }
449        switch (codeType) {
450            case EAN13:
451                width = x * (11 + 12 * 7);
452                if (font != null) {
453                    width += font.getWidthPoint(code.charAt(0), size);
454                }
455                break;
456            case EAN8:
457                width = x * (11 + 8 * 7);
458                break;
459            case UPCA:
460                width = x * (11 + 12 * 7);
461                if (font != null) {
462                    width += font.getWidthPoint(code.charAt(0), size) + font.getWidthPoint(code.charAt(11), size);
463                }
464                break;
465            case UPCE:
466                width = x * (9 + 6 * 7);
467                if (font != null) {
468                    width += font.getWidthPoint(code.charAt(0), size) + font.getWidthPoint(code.charAt(7), size);
469                }
470                break;
471            case SUPP2:
472                width = x * (6 + 2 * 7);
473                break;
474            case SUPP5:
475                width = x * (4 + 5 * 7 + 4 * 2);
476                break;
477            default:
478                throw new RuntimeException(MessageLocalization.getComposedMessage("invalid.code.type"));
479        }
480        return new Rectangle(width, height);
481    }
482    
483    /** Places the barcode in a <CODE>PdfContentByte</CODE>. The
484     * barcode is always placed at coordinates (0, 0). Use the
485     * translation matrix to move it elsewhere.<p>
486     * The bars and text are written in the following colors:<p>
487     * <P><TABLE BORDER=1>
488     * <TR>
489     *    <TH><P><CODE>barColor</CODE></TH>
490     *    <TH><P><CODE>textColor</CODE></TH>
491     *    <TH><P>Result</TH>
492     *    </TR>
493     * <TR>
494     *    <TD><P><CODE>null</CODE></TD>
495     *    <TD><P><CODE>null</CODE></TD>
496     *    <TD><P>bars and text painted with current fill color</TD>
497     *    </TR>
498     * <TR>
499     *    <TD><P><CODE>barColor</CODE></TD>
500     *    <TD><P><CODE>null</CODE></TD>
501     *    <TD><P>bars and text painted with <CODE>barColor</CODE></TD>
502     *    </TR>
503     * <TR>
504     *    <TD><P><CODE>null</CODE></TD>
505     *    <TD><P><CODE>textColor</CODE></TD>
506     *    <TD><P>bars painted with current color<br>text painted with <CODE>textColor</CODE></TD>
507     *    </TR>
508     * <TR>
509     *    <TD><P><CODE>barColor</CODE></TD>
510     *    <TD><P><CODE>textColor</CODE></TD>
511     *    <TD><P>bars painted with <CODE>barColor</CODE><br>text painted with <CODE>textColor</CODE></TD>
512     *    </TR>
513     * </TABLE>
514     * @param cb the <CODE>PdfContentByte</CODE> where the barcode will be placed
515     * @param barColor the color of the bars. It can be <CODE>null</CODE>
516     * @param textColor the color of the text. It can be <CODE>null</CODE>
517     * @return the dimensions the barcode occupies
518     */    
519    public Rectangle placeBarcode(PdfContentByte cb, BaseColor barColor, BaseColor textColor) {
520        Rectangle rect = getBarcodeSize();
521        float barStartX = 0;
522        float barStartY = 0;
523        float textStartY = 0;
524        if (font != null) {
525            if (baseline <= 0)
526                textStartY = barHeight - baseline;
527            else {
528                textStartY = -font.getFontDescriptor(BaseFont.DESCENT, size);
529                barStartY = textStartY + baseline;
530            }
531        }
532        switch (codeType) {
533            case EAN13:
534            case UPCA:
535            case UPCE:
536                if (font != null)
537                    barStartX += font.getWidthPoint(code.charAt(0), size);
538                break;
539        }
540        byte bars[] = null;
541        int guard[] = GUARD_EMPTY;
542        switch (codeType) {
543            case EAN13:
544                bars = getBarsEAN13(code);
545                guard = GUARD_EAN13;
546                break;
547            case EAN8:
548                bars = getBarsEAN8(code);
549                guard = GUARD_EAN8;
550                break;
551            case UPCA:
552                bars = getBarsEAN13("0" + code);
553                guard = GUARD_UPCA;
554                break;
555            case UPCE:
556                bars = getBarsUPCE(code);
557                guard = GUARD_UPCE;
558                break;
559            case SUPP2:
560                bars = getBarsSupplemental2(code);
561                break;
562            case SUPP5:
563                bars = getBarsSupplemental5(code);
564                break;
565        }
566        float keepBarX = barStartX;
567        boolean print = true;
568        float gd = 0;
569        if (font != null && baseline > 0 && guardBars) {
570            gd = baseline / 2;
571        }
572        if (barColor != null)
573            cb.setColorFill(barColor);
574        for (int k = 0; k < bars.length; ++k) {
575            float w = bars[k] * x;
576            if (print) {
577                if (Arrays.binarySearch(guard, k) >= 0)
578                    cb.rectangle(barStartX, barStartY - gd, w - inkSpreading, barHeight + gd);
579                else
580                    cb.rectangle(barStartX, barStartY, w - inkSpreading, barHeight);
581            }
582            print = !print;
583            barStartX += w;
584        }
585        cb.fill();
586        if (font != null) {
587            if (textColor != null)
588                cb.setColorFill(textColor);
589            cb.beginText();
590            cb.setFontAndSize(font, size);
591            switch (codeType) {
592                case EAN13:
593                    cb.setTextMatrix(0, textStartY);
594                    cb.showText(code.substring(0, 1));
595                    for (int k = 1; k < 13; ++k) {
596                        String c = code.substring(k, k + 1);
597                        float len = font.getWidthPoint(c, size);
598                        float pX = keepBarX + TEXTPOS_EAN13[k - 1] * x - len / 2;
599                        cb.setTextMatrix(pX, textStartY);
600                        cb.showText(c);
601                    }
602                    break;
603                case EAN8:
604                    for (int k = 0; k < 8; ++k) {
605                        String c = code.substring(k, k + 1);
606                        float len = font.getWidthPoint(c, size);
607                        float pX = TEXTPOS_EAN8[k] * x - len / 2;
608                        cb.setTextMatrix(pX, textStartY);
609                        cb.showText(c);
610                    }
611                    break;
612                case UPCA:
613                    cb.setTextMatrix(0, textStartY);
614                    cb.showText(code.substring(0, 1));
615                    for (int k = 1; k < 11; ++k) {
616                        String c = code.substring(k, k + 1);
617                        float len = font.getWidthPoint(c, size);
618                        float pX = keepBarX + TEXTPOS_EAN13[k] * x - len / 2;
619                        cb.setTextMatrix(pX, textStartY);
620                        cb.showText(c);
621                    }
622                    cb.setTextMatrix(keepBarX + x * (11 + 12 * 7), textStartY);
623                    cb.showText(code.substring(11, 12));
624                    break;
625                case UPCE:
626                    cb.setTextMatrix(0, textStartY);
627                    cb.showText(code.substring(0, 1));
628                    for (int k = 1; k < 7; ++k) {
629                        String c = code.substring(k, k + 1);
630                        float len = font.getWidthPoint(c, size);
631                        float pX = keepBarX + TEXTPOS_EAN13[k - 1] * x - len / 2;
632                        cb.setTextMatrix(pX, textStartY);
633                        cb.showText(c);
634                    }
635                    cb.setTextMatrix(keepBarX + x * (9 + 6 * 7), textStartY);
636                    cb.showText(code.substring(7, 8));
637                    break;
638                case SUPP2:
639                case SUPP5:
640                    for (int k = 0; k < code.length(); ++k) {
641                        String c = code.substring(k, k + 1);
642                        float len = font.getWidthPoint(c, size);
643                        float pX = (7.5f + (9 * k)) * x - len / 2;
644                        cb.setTextMatrix(pX, textStartY);
645                        cb.showText(c);
646                    }
647                    break;
648            }
649            cb.endText();
650        }
651        return rect;
652    }
653    
654    /** Creates a <CODE>java.awt.Image</CODE>. This image only
655     * contains the bars without any text.
656     * @param foreground the color of the bars
657     * @param background the color of the background
658     * @return the image
659     */    
660    public java.awt.Image createAwtImage(java.awt.Color foreground, java.awt.Color background) {
661        int f = foreground.getRGB();
662        int g = background.getRGB();
663        Canvas canvas = new Canvas();
664
665        int width = 0;
666        byte bars[] = null;
667        switch (codeType) {
668            case EAN13:
669                bars = getBarsEAN13(code);
670                width = 11 + 12 * 7;
671                break;
672            case EAN8:
673                bars = getBarsEAN8(code);
674                width = 11 + 8 * 7;
675                break;
676            case UPCA:
677                bars = getBarsEAN13("0" + code);
678                width = 11 + 12 * 7;
679                break;
680            case UPCE:
681                bars = getBarsUPCE(code);
682                width = 9 + 6 * 7;
683                break;
684            case SUPP2:
685                bars = getBarsSupplemental2(code);
686                width = 6 + 2 * 7;
687                break;
688            case SUPP5:
689                bars = getBarsSupplemental5(code);
690                width = 4 + 5 * 7 + 4 * 2;
691                break;
692            default:
693                throw new RuntimeException(MessageLocalization.getComposedMessage("invalid.code.type"));
694        }
695
696        boolean print = true;
697        int ptr = 0;
698        int height = (int)barHeight;
699        int pix[] = new int[width * height];
700        for (int k = 0; k < bars.length; ++k) {
701            int w = bars[k];
702            int c = g;
703            if (print)
704                c = f;
705            print = !print;
706            for (int j = 0; j < w; ++j)
707                pix[ptr++] = c;
708        }
709        for (int k = width; k < pix.length; k += width) {
710            System.arraycopy(pix, 0, pix, k, width); 
711        }
712        java.awt.Image img = canvas.createImage(new MemoryImageSource(width, height, pix, 0, width));
713        
714        return img;
715    }    
716}