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