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;
020    
021    import javax.swing.*;
022    import java.awt.*;
023    import java.awt.geom.*;
024    import javax.accessibility.*;
025    
026    import net.sourceforge.jbarcodebean.model.BarcodeStrategy;
027    import net.sourceforge.jbarcodebean.model.Codabar;
028    import net.sourceforge.jbarcodebean.model.Codabar_2to1;
029    import net.sourceforge.jbarcodebean.model.Code128;
030    import net.sourceforge.jbarcodebean.model.Code39;
031    import net.sourceforge.jbarcodebean.model.Code39_2to1;
032    import net.sourceforge.jbarcodebean.model.Ean13;
033    import net.sourceforge.jbarcodebean.model.Ean8;
034    import net.sourceforge.jbarcodebean.model.ExtendedCode39;
035    import net.sourceforge.jbarcodebean.model.ExtendedCode39_2to1;
036    import net.sourceforge.jbarcodebean.model.Interleaved25;
037    import net.sourceforge.jbarcodebean.model.Interleaved25_2to1;
038    import net.sourceforge.jbarcodebean.model.MSI;
039    
040    import java.io.*;
041    import java.net.URL;
042    import java.util.Properties;
043    import java.awt.image.*;
044    
045    /**
046     * <p>
047     * JFC Swing-compatible JavaBeans
048     * <sup><small>TM</small></sup>
049     * component that renders barcodes in a variety of different formats.
050     * <p>
051     * [<a href='#skip_copyright'>skip over license and copyright</a>]
052     * <hr>
053     * <p><b>LICENSE INFORMATION</b></p>
054     * <p>
055     *  This library is free software; you can redistribute it and/or modify it
056     *  under the terms of the GNU Lesser General Public License (LGPL) as
057     *  published by the Free Software Foundation; either version 3.0 of the
058     *  License, or (at your option) any later version.
059     * </p><p>
060     *  This library is distributed in the hope that it will be useful, but
061     *  WITHOUT ANY WARRANTY; without even the implied warranty of
062     *  MERCHANTABILITY of FITNESS FOR A PARTICULAR PURPOSE. See the GNU
063     *  Lesser General Public License for more details.
064     *  </p>
065     *  <p>See <a href='http://www.gnu.org/licenses/lgpl.html'
066     *  >http://www.gnu.org/licenses/lgpl.html</a>.
067     *  </p> 
068     * <hr>
069     * <A NAME="skip_copyright"><!-- --></A>
070     * <p>
071     * <strong>JBarcodeBean</strong> employs the <strong>Strategy</strong> design pattern<b>*</b>
072     * to abstract the encoding algorithm.  The
073     * {@link #setCodeType(BarcodeStrategy) codeType}
074     * property can be set to any class that implements the {@link BarcodeStrategy}
075     * interface to determine how the {@link #setCode(String) code} property is encoded.
076     * This version of JBarcodeBean is bundled with the following concrete implementations
077     * of {@link BarcodeStrategy}:
078     *
079     * <ul>
080     * <li>{@link Code128}
081     * <li>{@link Code39}
082     * <li>{@link Code39_2to1}
083     * <li>{@link ExtendedCode39}
084     * <li>{@link ExtendedCode39_2to1}
085     * <li>{@link Interleaved25}
086     * <li>{@link Interleaved25_2to1}
087     * <li>{@link Codabar}
088     * <li>{@link Codabar_2to1}
089     * <li>{@link MSI}
090     * <li>{@link Ean8}
091     * <li>{@link Ean13}
092     * </ul>
093     * <p>
094     *
095     * <blockquote><b>*</b>See <i>"Design Patterns Elements of Reusable Object-Oriented Software",
096     * Erich Gamma et al.</i> for more information about design patterns.</blockquote>
097     *
098     * @version 1.1
099     */
100    public class JBarcodeBean extends JComponent implements java.io.Serializable, Accessible {
101    
102        // Property change constants
103    
104        /** Identifies a change in the <b>showText</b> property. */
105        private static final String SHOW_TEXT_CHANGED_PROPERTY = "showText";
106        /** Identifies a change in the <b>code</b> property. */
107        private static final String CODE_CHANGED_PROPERTY = "code";
108        /** Identifies a change in the <b>codeType</b> property. */
109        private static final String CODE_TYPE_CHANGED_PROPERTY = "codeType";
110        /** Identifies a change in the <b>checkDigit</b> property. */
111        private static final String CHECK_DIGIT_CHANGED_PROPERTY = "checkDigit";
112        /** Identifies a change in the <b>narrowestBarWidth</b> property. */
113        private static final String NARROWEST_BAR_WIDTH_CHANGED_PROPERTY = "narrowestBarWidth";
114        /** Identifies a change in the <b>barcodeHeight</b> property. */
115        private static final String BARCODE_HEIGHT_CHANGED_PROPERTY = "barcodeHeight";
116        /** Identifies a change in the <b>barcodeBackground</b> property. */
117        private static final String BARCODE_BACKGROUND_CHANGED_PROPERTY = "barcodeBackground";
118        /** Identifies a change in the <b>angleDegrees</b> property. */
119        private static final String ANGLE_DEGREES_CHANGED_PROPERTY = "angleDegrees";
120        
121        /**
122         * Identifies a change of the horizontal alignment property.
123         */
124        private static final String HORIZONTAL_ALIGNMENR_PROPERTY ="horizontalAlignment";
125        
126        /**
127         * Constant indicating that the barcode should be left aligned along the
128         * x-axis.
129         */
130        public static final int ALIGN_LEFT      =1;
131    
132        /**
133         * Constant indicating that the barcode should be centered along the x-axis.
134         * (This is the default behaviour)
135         */
136        public static final int ALIGN_CENTER    =2;
137    
138        /**
139         * Constant indicating that the barcode should be right aligned along the
140         * x-axis.
141         */
142        public static final int ALIGN_RIGHT     =3;
143    
144        
145        /**
146         * Identifies a change of the label position property.
147         */
148        private static final String LABEL_POSITION_PROPERTY ="labelPosition";
149        
150        /**
151         * Constant indicating that the human readable barcode text should be
152         * printed at the top of the barcode.
153         */
154        public static final int LABEL_TOP       = 1;
155    
156        /**
157         * Constant indicating that the human readable barcode text should be
158         * printed at the bottom of the barcode. (This is the default behaviour)
159         */
160        public static final int LABEL_BOTTOM    = 2;
161        
162        /**
163         * Constant indicating that the human readable barcode text should not be
164         * printed at all.
165         */
166        public static final int LABEL_NONE      = 0;
167        
168        
169        
170        // Private fields.
171        private String code;
172        private BarcodeStrategy codeType;
173        private boolean checkDigit = false;
174        private EncodedBarcode encoded = null;
175        private int narrowestBarWidth = 1;
176        private int barcodeHeight = 40;
177        private Color barcodeBackground = Color.white;
178        private Dimension minimumSize;
179        private Dimension preferredSize;
180        private double angleDegrees;
181        private int labelHeight;          // calculated
182        private int labelWidth;           // calculated
183        private int barcodeWidth;         // calculated
184        private String encodeError = "";
185        private int horizontalAlignment = ALIGN_CENTER;
186        private int labelPosition = LABEL_BOTTOM;
187    
188        /**
189         * <p>Contructor that allows an initial barcode and code type to be specified.
190         *
191         * @param code The text to encode.
192         * @param codeType The type of barcode.
193         */
194        public JBarcodeBean(String code, BarcodeStrategy codeType) {
195    
196            // Set some Component and JComponent properties.
197            setForeground(Color.black);
198            setFont(new Font("Monospaced", Font.PLAIN, 12));
199    
200            // Set bean properties - don't fire property changes here since this is c'tor
201            this.code = code;
202            this.codeType = codeType;
203            if (codeType.requiresChecksum() == BarcodeStrategy.MANDATORY_CHECKSUM) {
204                checkDigit = true;
205            } else if (codeType.requiresChecksum() == BarcodeStrategy.NO_CHECKSUM) {
206                checkDigit = false;
207            }
208    
209            // Disable double buffering, so that Java-2 printing is hi-res
210            setDoubleBuffered(false);
211    
212            // Render the bean.
213            encode();
214            recalculateSizes();
215            repaint();
216        }
217    
218        /**
219         * <p>
220         * Parameterless constructor. Creates a <tt>JBarcodeBean</tt> object with default
221         * property values, so that the bean will render as a typical barcode
222         * when initially added to a GUI with a GUI Builder.
223         * <p>
224         * Defaulat values are:<b>
225         * <p><code>code = "1234"</code><br>
226         * <code>codeType = new Code39()</code></b><br>
227         * <p>
228         * @see Code39
229         *
230         */
231        public JBarcodeBean() {
232            this("1234", new Code39());
233        }
234    
235        /**
236         * Accessor method for the <tt><b>angleDegrees</b></tt> property,
237         * which determines the angle (from horizontal) at which the barcode
238         * is rendered.
239         */
240        public double getAngleDegrees() {
241            return angleDegrees;
242        }
243    
244        /**
245         * Mutator method for the <tt><b>angleDegrees</b></tt> property,
246         * which determines the angle (from horizontal) at which the barcode
247         * is rendered.
248         */
249        public void setAngleDegrees(double angleDegrees) {
250            double oldValue = this.angleDegrees;
251            this.angleDegrees = angleDegrees;
252            firePropertyChange(ANGLE_DEGREES_CHANGED_PROPERTY, oldValue, angleDegrees);
253            recalculateSizes();
254            repaint();
255        }
256    
257        /**
258         * Accessor method for the <tt><b>showText</b></tt> property, which
259         * determines whether or not the text caption is visible below the
260         * barcode. <tt>true</tt> = visible, <tt>false</tt> = not visible.
261         * @deprecated As of 1.2.0, replaced by {@link #getLabelPosition()}.
262         * This will return <tt>true</tt> if label position is {@link #LABEL_BOTTOM}
263         * and <tt>false</tt> otherwise.
264         */
265        public boolean isShowText() {
266            return this.labelPosition==LABEL_BOTTOM;
267        }
268    
269        /**
270         * Mutator method for the <tt><b>showText</b></tt> property, which
271         * determines whether or not the text caption is visible below the
272         * barcode. <tt>true</tt> = visible, <tt>false</tt> = not visible.
273         * @deprecated As of 1.2.0, replaced by {@link #setLabelPosition(int)}. When
274         * setting this property to <tt>true</tt> the label position will be set to
275         * {@link #LABEL_BOTTOM}, setting the property to <tt>false</tt> will set
276         * the label position to {@link #LABEL_NONE}. 
277         */
278        public void setShowText(boolean showText) {
279            boolean oldValue = isShowText();
280            setLabelPosition(showText?LABEL_BOTTOM:LABEL_NONE);
281            firePropertyChange(SHOW_TEXT_CHANGED_PROPERTY, oldValue, showText);
282        }
283    
284        /**
285         * Always returns <tt>true</tt>, as this component renders its entire
286         * drawing area.
287         */
288        public boolean isOpaque() {
289            return true;
290        }
291    
292        /**
293         * Accessor method for the <tt><b>foreground</b></tt> property, which
294         * determines the color of the bars and caption text (typically black).
295         */
296        public Color getForeground() {
297            return super.getForeground();
298        }
299    
300        /**
301         * Mutator method for the <tt><b>foreground</b></tt> property, which
302         * determines the color of the bars and caption text (typically black).
303         */
304        public void setForeground(Color c) {
305            super.setForeground(c);
306            repaint();
307        }
308    
309        /**
310         * Accessor method for the <tt><b>background</b></tt> property, which
311         * determines the control background color (the space around the barcode,
312         * and behind the text caption).  Note that this is not the
313         * same as the {@link #getBarcodeBackground barcodeBackground} property
314         * (which is the color of the spaces between the bars in the barcode).
315         */
316        public Color getBackground() {
317            return super.getBackground();
318        }
319    
320        /**
321         * Mutator method for the <tt><b>background</b></tt> property, which
322         * determines the control background color (the space around the barcode,
323         * and behind the text caption).  Note that this is not the
324         * same as the {@link #setBarcodeBackground barcodeBackground} property
325         * (which is the color of the spaces between the bars in the barcode).
326         */
327        public void setBackground(Color c) {
328            super.setBackground(c);
329            repaint();
330        }
331    
332        /**
333         * Accessor method for the <tt><b>barcodeBackground</b></tt> property, which
334         * determines the color of the spaces between the bars in the barcode
335         * (typically white).
336         *
337         * @see #setBackground
338         */
339        public Color getBarcodeBackground() {
340            return barcodeBackground;
341        }
342    
343        /**
344         * Mutator method for the <tt><b>barcodeBackground</b></tt> property, which
345         * determines the color of the spaces between the bars in the barcode
346         * (typically white).
347         *
348         * @see #setBackground
349         */
350        public void setBarcodeBackground(Color barcodeBackground) {
351            Color oldValue = this.barcodeBackground;
352            this.barcodeBackground = barcodeBackground;
353            firePropertyChange(BARCODE_BACKGROUND_CHANGED_PROPERTY, oldValue, barcodeBackground);
354            repaint();
355        }
356    
357        /**
358         * Accessor methor for the <tt><b>border</b></tt> property.
359         */
360        public javax.swing.border.Border getBorder() {
361            return super.getBorder();
362        }
363    
364        /**
365         * Mutator methor for the <tt><b>border</b></tt> property.
366         */
367        public void setBorder(javax.swing.border.Border border) {
368            super.setBorder(border);
369            recalculateSizes();
370            repaint();
371        }
372    
373        /**
374         * Accessor method for the <tt><b>preferredSize</b></tt> property.
375         */
376        public Dimension getPreferredSize() {
377            return preferredSize();
378        }
379    
380        /**
381         * Mutator method for the <tt><b>preferredSize</b></tt> property.
382         */
383        public void setPreferredSize(Dimension preferredSize) {
384            Dimension oldValue = this.preferredSize;
385            this.preferredSize = preferredSize;
386            firePropertyChange("preferredSize", oldValue, preferredSize);
387        }
388    
389        /**
390         * Returns the preferred size of the component.
391         *
392         * @deprecated Use {@link #getPreferredSize}.
393         */
394        public Dimension preferredSize() {
395            if (preferredSize != null) {
396                return preferredSize;
397            } else {
398                return new Dimension(getWidth(), getHeight());
399            }
400        }
401    
402        /**
403         * Accessor method for <tt><b>minimumSize</b></tt> property.
404         */
405        public Dimension getMinimumSize() {
406            return minimumSize();
407        }
408    
409        /**
410         * Mutator method for the <tt><b>minimumSize</b></tt> property.
411         */
412        public void setMinimumSize(Dimension minimumSize) {
413            Dimension oldValue = this.minimumSize;
414            this.minimumSize = minimumSize;
415            firePropertyChange("minimumSize", oldValue, minimumSize);
416        }
417    
418        /**
419         * Returns the minimum size of the component.
420         *
421         * @deprecated Use {@link #getMinimumSize}.
422         */
423        public Dimension minimumSize() {
424            if (minimumSize != null) {
425                return minimumSize;
426            } else {
427                return new Dimension(getWidth(), getHeight());
428            }
429        }
430    
431        /**
432         * Accessor method for the <tt><b>font</b></tt> property.
433         */
434        public Font getFont() {
435            return super.getFont();
436        }
437    
438        /**
439         * Mutator method for the <tt><b>font</b></tt> property.
440         */
441        public void setFont(Font font) {
442            super.setFont(font);
443            recalculateSizes();
444            repaint();
445        }
446    
447        /**
448         * Accessor method for the <tt><b>barcodeHeight</b></tt> property, which
449         * determines the height of the barcode (excluding caption text) in pixels.
450         */
451        public int getBarcodeHeight() {
452            return barcodeHeight;
453        }
454    
455        /**
456         * Mutator method for the <tt><b>barcodeHeight</b></tt> property, which
457         * determines the height of the barcode (excluding caption text) in pixels.
458         */
459        public void setBarcodeHeight(int barcodeHeight) {
460            int oldValue = this.barcodeHeight;
461            this.barcodeHeight = barcodeHeight;
462            firePropertyChange(BARCODE_HEIGHT_CHANGED_PROPERTY, oldValue, barcodeHeight);
463            recalculateSizes();
464            repaint();
465        }
466    
467        /**
468         * Accessor method for the <tt><b>narrowestBarWidth</b></tt> property,
469         * which determines the width (in pixels) of the narrowest bar in the barcode.
470         */
471        public int getNarrowestBarWidth() {
472            return narrowestBarWidth;
473        }
474    
475        /**
476         * Mutator method for the <tt><b>narrowestBarWidth</b></tt> property,
477         * which determines the width (in pixels) of the narrowest bar in the barcode.
478         */
479        public void setNarrowestBarWidth(int narrowestBarWidth) {
480            int oldValue = this.narrowestBarWidth;
481            this.narrowestBarWidth = narrowestBarWidth;
482            firePropertyChange(NARROWEST_BAR_WIDTH_CHANGED_PROPERTY, oldValue, narrowestBarWidth);
483            recalculateSizes();
484            repaint();
485        }
486    
487        /**
488         * Always returns <tt>false</tt>, as this control cannot receive focus.
489         */
490        public boolean isFocusTraversable() {
491            return false;
492        }
493    
494        /**
495         * Always returns the value of the {@link #getCode code} property.
496         */
497        protected String paramString() {
498            return code;
499        }
500    
501        /**
502         * Always returns the value of the {@link #getCode code} property.
503         */
504        public String toString() {
505            return code;
506        }
507    
508        /**
509         * Accessor method for the <tt><b>code</b></tt> property,
510         * which is the text encoded in the barcode.
511         */
512        public String getCode() {
513            return code;
514        }
515    
516        /**
517         * Mutator method for the <tt><b>code</b></tt> property,
518         * which is the text encoded in the barcode.
519         */
520        public void setCode(String code) {
521            String oldValue = this.code;
522            this.code = code;
523            firePropertyChange(CODE_CHANGED_PROPERTY, oldValue, code);
524            encode();
525            recalculateSizes();
526            repaint();
527        }
528    
529        /**
530         * Accessor method for the <tt><b>codeType</b></tt> property,
531         * which is the barcode type.
532         */
533        public BarcodeStrategy getCodeType() {
534            return codeType;
535        }
536    
537        /**
538         * Mutator method for the <tt><b>codeType</b></tt> property,
539         * which is the barcode type.
540         */
541        public void setCodeType(BarcodeStrategy codeType) {
542            BarcodeStrategy oldValue = this.codeType;
543            this.codeType = codeType;
544            firePropertyChange(CODE_TYPE_CHANGED_PROPERTY, oldValue, codeType);
545            if (codeType != null && codeType.requiresChecksum() == BarcodeStrategy.MANDATORY_CHECKSUM && checkDigit == false) {
546                setCheckDigit(true);
547                // encode(), recalculateSizes(), and repaint() will be done in setChecked().
548            } else if (codeType != null && codeType.requiresChecksum() == BarcodeStrategy.NO_CHECKSUM && checkDigit == true) {
549                setCheckDigit(false);
550                // encode(), recalculateSizes(), and repaint() will be done in setChecked().
551            } else {
552                encode();
553                recalculateSizes();
554                repaint();
555            }
556        }
557    
558        /**
559         * Accessor method for the <tt><b>checkDigit</b></tt> property,
560         * which determines whether a check digit is encoded in the
561         * barcode.  <tt>true</tt> = check digit is encoded, <tt>false</tt>
562         * = check digit is not encoded.
563         */
564        public boolean isCheckDigit() {
565            return checkDigit;
566        }
567    
568        /**
569         * Mutator method for the <tt><b>checkDigit</b></tt> property,
570         * which determines whether a check digit is encoded in the
571         * barcode.  <tt>true</tt> = check digit is encoded, <tt>false</tt>
572         * = check digit is not encoded.
573         */
574        public void setCheckDigit(boolean checkDigit){
575            if (codeType != null) {
576                if (codeType.requiresChecksum() == BarcodeStrategy.MANDATORY_CHECKSUM  && checkDigit == false) {
577                    // Cannot disable checksum
578                    // although the checksum cannot be disabled there should be no exception
579                    // the call should simply be ignored (requestid 1329396)
580                    return;
581                } else if (codeType.requiresChecksum() == BarcodeStrategy.NO_CHECKSUM  && checkDigit == true) {
582                    // Cannot enable checksum
583                    // although the checksum cannot be enabled there should be no exception
584                    // the call should simply be ignored (requestid 1329396)
585                    return;
586                }
587            }
588            boolean oldValue = this.checkDigit;
589            this.checkDigit = checkDigit;
590            firePropertyChange(CHECK_DIGIT_CHANGED_PROPERTY, oldValue, checkDigit);
591            encode();
592            recalculateSizes();
593            repaint();
594        }
595    
596        /**
597         * Component paint method.
598         */
599        protected void paintComponent(Graphics graphics) {
600            doPaint(graphics, getSize(), getInsets());
601        }
602    
603        private void doPaint(Graphics graphics, Dimension d, Insets insets) {
604    
605            Graphics2D g = (Graphics2D) graphics;
606    
607            // Save graphics properties
608            AffineTransform tran = g.getTransform();
609            Shape oldClip = g.getClip();
610            Color oldColor = g.getColor();
611            Font oldFont = g.getFont();
612    
613            // Fill control background
614            g.setColor(getBackground());
615            g.fillRect(0, 0, d.width, d.height);
616    
617            // Set clip rectangle to prevent barcode overwriting border
618    
619            // also regard the clip the graphics object already has to avoid paint
620            // outside the clip of the graphics. therefore we make an intersect of both
621            // clips
622            Area areaOldClip = null;
623            Rectangle newClip = new Rectangle(insets.left, insets.top,
624                    d.width - insets.left - insets.right, d.height - insets.top - insets.bottom);
625            Area areaNewClip = new Area(newClip);
626            if (oldClip != null)
627            {
628                areaOldClip = new Area(oldClip);
629                areaOldClip.intersect(areaNewClip);
630            }
631            else
632            {
633                areaOldClip = areaNewClip;
634            }
635            g.setClip(areaOldClip);
636    
637            // Apply rotate and transale transform
638            g.rotate(angleDegrees / 180 * Math.PI, d.width / 2.0, d.height / 2.0);
639    
640            int barcodeTop = (d.height - (barcodeHeight + labelHeight)) / 2;
641            if(labelPosition!=LABEL_TOP){
642                barcodeTop=(d.height - (barcodeHeight + labelHeight)) / 2;
643            } else{
644                barcodeTop=(d.height - (barcodeHeight + labelHeight)) / 2+labelHeight;
645            }
646    
647            // Draw barcode
648            if (encoded != null) {
649                int x;
650                if(horizontalAlignment==ALIGN_LEFT){
651                    x=0;
652                } else if (horizontalAlignment==ALIGN_RIGHT){
653                    x=d.width-barcodeWidth;
654                } else {
655                    x=(d.width-barcodeWidth)/2;
656                }
657                for(int i = 0; i < encoded.elements.length; i++) {
658                    if (encoded.elements[i].getType()==BarcodeElement.TYPE_BAR) {
659                        // bar
660                        g.setColor(getForeground());
661                    } else {
662                        // space
663                        g.setColor(barcodeBackground);
664                    }
665                    int barWidth = encoded.elements[i].getWidth() * narrowestBarWidth;
666                    g.fillRect(x, barcodeTop, barWidth, barcodeHeight);
667                    x += barWidth;
668                }
669    
670                // Draw text
671                if (labelPosition!=LABEL_NONE) {
672                    g.setFont(this.getFont());
673                    g.setColor(getForeground());
674                    FontMetrics fm = getFontMetrics(g.getFont());
675                    int labelTop;
676                    if(labelPosition==LABEL_BOTTOM) {
677                        labelTop = barcodeTop + barcodeHeight + fm.getAscent();
678                    } else {
679                        labelTop = barcodeTop - labelHeight + fm.getAscent();
680                    }
681                    g.drawString(encoded.barcodeLabelText,
682                            (d.width - labelWidth) / 2, labelTop);
683                }
684            } else if (!encodeError.equals("")) {
685                // Display error
686                g.setFont(new Font("Monospaced", Font.PLAIN, 10));
687                FontMetrics fm = getFontMetrics(g.getFont());
688                int labelWidth = fm.stringWidth(encodeError);
689                g.setColor(getBarcodeBackground());
690                g.fillRect((d.width - labelWidth) / 2, barcodeTop,
691                        labelWidth, fm.getHeight());
692                g.setColor(getForeground());
693                g.drawString(encodeError,
694                        (d.width - labelWidth) / 2,
695                        barcodeTop + fm.getAscent()
696                );
697            }
698    
699            // Restore graphics properties
700            g.setTransform(tran);
701            g.setClip(oldClip);
702            g.setColor(oldColor);
703            g.setFont(oldFont);
704        }
705    
706        private void recalculateSizes() {
707    
708            if (labelPosition!=LABEL_NONE) {
709                FontMetrics fm = getFontMetrics(getFont());
710                labelHeight = fm.getAscent() + fm.getDescent();
711            } else {
712                labelHeight = 0;
713            }
714    
715            Dimension d = calculateControlSize(getRequiredWidth(), barcodeHeight + labelHeight);
716            setPreferredSize(d);
717            setMinimumSize(d);
718            revalidate();
719        }
720    
721        private Dimension calculateControlSize(int rectWidth, int rectHeight) {
722    
723            Insets insets = getInsets();
724            Dimension d = new Dimension();
725            double angleRadians = angleDegrees / 180.0 * Math.PI;
726    
727            d.height = (int) (Math.abs(rectWidth * Math.sin(angleRadians)) + Math.abs(rectHeight * Math.cos(angleRadians)))
728            + insets.top + insets.bottom;
729    
730            d.width = (int) (Math.abs(rectWidth * Math.cos(angleRadians)) + Math.abs(rectHeight * Math.sin(angleRadians)))
731            + insets.left + insets.right;
732    
733            return d;
734        }
735    
736        private int getRequiredWidth() {
737    
738            this.barcodeWidth = 0;
739            this.labelWidth = 0;
740    
741            if (encoded != null) {
742    
743                FontMetrics fm = getFontMetrics(getFont());
744                labelWidth = fm.stringWidth(encoded.barcodeLabelText);
745    
746                for(int i = 0; i < encoded.elements.length; i++) {
747                    barcodeWidth += encoded.elements[i].getWidth() * narrowestBarWidth;
748                }
749            } else if (!encodeError.equals("")) {
750                // error message only
751                FontMetrics fm = getFontMetrics(getFont());
752                labelWidth = fm.stringWidth(encodeError);
753            }
754    
755            int width = (barcodeWidth > labelWidth) ? barcodeWidth : labelWidth;
756            return width;
757        }
758    
759        private void encode() {
760            encodeError = "";
761            if ((codeType != null) && (!code.equals(""))) {
762                try {
763                    encoded = codeType.encode(code, checkDigit);
764                } catch (BarcodeException e) {
765                    encoded = null;
766                    encodeError = e.getMessage();
767                }
768            } else {
769                encoded = null;
770            }
771        }
772    
773        /**
774         * Returns a simple subclass of <tt>AccessibleContext</tt>.
775         */
776        public AccessibleContext getAccessibleContext() {
777            return new JComponent.AccessibleJComponent() {
778                public String getAccessibleName() {
779                    return "barcode " + code;
780                }
781                public AccessibleRole getAccessibleRole() {
782                    return AccessibleRole.LABEL;
783                }
784            };
785        }
786    
787        /**
788         * Returns the version of this JBarcodeBean implementation.
789         * @return The version string in format: <i>major.minor[.micro]</i>
790         */
791        public static String getVersion() {
792            
793            Class cl=JBarcodeBean.class;
794            URL url = cl.getResource("/META-INF/maven/net.sourceforge.jbarcodebean/jbarcodebean/pom.properties");
795            Properties props=new Properties();
796            try {
797                if(url==null){
798                    return "unknown";
799                }
800                InputStream in=url.openStream();
801                props.load(in);
802                in.close();
803            } catch (IOException e) {
804                return "unknown";
805            }
806            return props.getProperty("version");
807        }
808    
809        /**
810         * Draws the current barcode into the given image and returns it. 
811         * @param image The image the barcode will be drawn into or
812         * <code>null</code> if a new image should be created using the preferred
813         * size of the barcode.
814         * @return The image containing the barcode.
815         */
816        public BufferedImage draw(BufferedImage image) {
817            if(image==null) {
818                Dimension pref=getPreferredSize();
819                image=(BufferedImage) createImage(pref.width, pref.height);
820            }
821            encode();
822            recalculateSizes();
823            Graphics2D g = image.createGraphics();
824            g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
825            doPaint(g, new Dimension(image.getWidth(), image.getHeight()), new Insets(0, 0, 0, 0));
826            return image;
827        }
828    
829        /**
830         * Returns the alignment of the barcode along the X axis. 
831         * @return The value of the horizontalAlignment property, one of the
832         * following constants: {@link #ALIGN_LEFT}, {@link #ALIGN_RIGHT},
833         * {@link #ALIGN_CENTER}
834         * @see #setHorizontalAlignment(int)
835         * @since 1.2.0
836         */
837        public int getHorizontalAlignment() {
838            return horizontalAlignment;
839        }
840    
841        /**
842         * Sets the alignment of the barcode along the X axis.
843         * This is a JavaBeans bound property. 
844         * @param horizontalAlignment One of the following constants : 
845         * {@link #ALIGN_LEFT}, {@link #ALIGN_RIGHT},
846         * {@link #ALIGN_CENTER} (the default)
847         * @see #getHorizontalAlignment()
848         * @since 1.2.0
849         */
850        public void setHorizontalAlignment(int horizontalAlignment) {
851            int oldValue=this.horizontalAlignment;
852            this.horizontalAlignment = horizontalAlignment;
853            firePropertyChange(HORIZONTAL_ALIGNMENR_PROPERTY , oldValue, horizontalAlignment);
854            recalculateSizes();
855            repaint();
856        }
857    
858        /**
859         * Returns the label position of the human readable barcode text.
860         * @return The value of the labelPosition property, one of the following
861         * constants: {@link #LABEL_BOTTOM}, {@link #LABEL_TOP} or
862         * {@link #LABEL_NONE}.
863         * @see #setLabelPosition(int)
864         * @since 1.2.0
865         */
866        public int getLabelPosition() {
867            return labelPosition;
868        }
869    
870        /**
871         * Sets the label position of the human readable bacode text.
872         * This is a JavaBeans bound property.
873         * @param labelPosition One of the following constants:
874         * {@link #LABEL_BOTTOM}, {@link #LABEL_TOP} or {@link #LABEL_NONE}.
875         * @see #getLabelPosition()
876         * @since 1.2.0
877         */
878        public void setLabelPosition(int labelPosition) {
879            int oldValue=this.labelPosition;
880            this.labelPosition = labelPosition;
881            firePropertyChange(LABEL_POSITION_PROPERTY, oldValue, labelPosition);
882            recalculateSizes();
883            repaint();
884        }
885        
886    }
887