View Javadoc

1   /**
2    *  This library is free software; you can redistribute it and/or modify it
3    *  under the terms of the GNU Lesser General Public License (LGPL) as
4    *  published by the Free Software Foundation; either version 3.0 of the
5    *  License, or (at your option) any later version.
6    *
7    *  This library is distributed in the hope that it will be useful, but
8    *  WITHOUT ANY WARRANTY; without even the implied warranty of
9    *  MERCHANTABILITY of FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10   *  Lesser General Public License for more details. 
11   */
12  
13  /**
14   * Title:        JBarcodeBean
15   * Description:  Barcode JavaBeans Component
16   * Copyright:    Copyright (C) 2004
17   * Company:      Dafydd Walters
18   */
19  package net.sourceforge.jbarcodebean;
20  
21  import javax.swing.*;
22  import java.awt.*;
23  import java.awt.geom.*;
24  import javax.accessibility.*;
25  
26  import net.sourceforge.jbarcodebean.model.BarcodeStrategy;
27  import net.sourceforge.jbarcodebean.model.Codabar;
28  import net.sourceforge.jbarcodebean.model.Codabar_2to1;
29  import net.sourceforge.jbarcodebean.model.Code128;
30  import net.sourceforge.jbarcodebean.model.Code39;
31  import net.sourceforge.jbarcodebean.model.Code39_2to1;
32  import net.sourceforge.jbarcodebean.model.Ean13;
33  import net.sourceforge.jbarcodebean.model.Ean8;
34  import net.sourceforge.jbarcodebean.model.ExtendedCode39;
35  import net.sourceforge.jbarcodebean.model.ExtendedCode39_2to1;
36  import net.sourceforge.jbarcodebean.model.Interleaved25;
37  import net.sourceforge.jbarcodebean.model.Interleaved25_2to1;
38  import net.sourceforge.jbarcodebean.model.MSI;
39  
40  import java.io.*;
41  import java.net.URL;
42  import java.util.Properties;
43  import java.awt.image.*;
44  
45  /**
46   * <p>
47   * JFC Swing-compatible JavaBeans
48   * <sup><small>TM</small></sup>
49   * component that renders barcodes in a variety of different formats.
50   * <p>
51   * [<a href='#skip_copyright'>skip over license and copyright</a>]
52   * <hr>
53   * <p><b>LICENSE INFORMATION</b></p>
54   * <p>
55   *  This library is free software; you can redistribute it and/or modify it
56   *  under the terms of the GNU Lesser General Public License (LGPL) as
57   *  published by the Free Software Foundation; either version 3.0 of the
58   *  License, or (at your option) any later version.
59   * </p><p>
60   *  This library is distributed in the hope that it will be useful, but
61   *  WITHOUT ANY WARRANTY; without even the implied warranty of
62   *  MERCHANTABILITY of FITNESS FOR A PARTICULAR PURPOSE. See the GNU
63   *  Lesser General Public License for more details.
64   *  </p>
65   *  <p>See <a href='http://www.gnu.org/licenses/lgpl.html'
66   *  >http://www.gnu.org/licenses/lgpl.html</a>.
67   *  </p> 
68   * <hr>
69   * <A NAME="skip_copyright"><!-- --></A>
70   * <p>
71   * <strong>JBarcodeBean</strong> employs the <strong>Strategy</strong> design pattern<b>*</b>
72   * to abstract the encoding algorithm.  The
73   * {@link #setCodeType(BarcodeStrategy) codeType}
74   * property can be set to any class that implements the {@link BarcodeStrategy}
75   * interface to determine how the {@link #setCode(String) code} property is encoded.
76   * This version of JBarcodeBean is bundled with the following concrete implementations
77   * of {@link BarcodeStrategy}:
78   *
79   * <ul>
80   * <li>{@link Code128}
81   * <li>{@link Code39}
82   * <li>{@link Code39_2to1}
83   * <li>{@link ExtendedCode39}
84   * <li>{@link ExtendedCode39_2to1}
85   * <li>{@link Interleaved25}
86   * <li>{@link Interleaved25_2to1}
87   * <li>{@link Codabar}
88   * <li>{@link Codabar_2to1}
89   * <li>{@link MSI}
90   * <li>{@link Ean8}
91   * <li>{@link Ean13}
92   * </ul>
93   * <p>
94   *
95   * <blockquote><b>*</b>See <i>"Design Patterns Elements of Reusable Object-Oriented Software",
96   * Erich Gamma et al.</i> for more information about design patterns.</blockquote>
97   *
98   * @version 1.1
99   */
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