View Javadoc

1   /***********************************************************************************************************************
2    * Copyright (c) 2003, International Barcode Consortium
3    * All rights reserved.
4    *
5    * Redistribution and use in source and binary forms, with or without modification,
6    * are permitted provided that the following conditions are met:
7    *
8    * Redistributions of source code must retain the above copyright notice, this list of
9    * conditions and the following disclaimer.
10   * Redistributions in binary form must reproduce the above copyright notice, this list of
11   * conditions and the following disclaimer in the documentation and/or other materials
12   * provided with the distribution.
13   * Neither the name of the International Barcode Consortium nor the names of any contributors may be used to endorse
14   * or promote products derived from this software without specific prior written permission.
15   *
16   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
17   * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
18   * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
19   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22   * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23   * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24   * POSSIBILITY OF SUCH DAMAGE.
25   ***********************************************************************************************************************/
26  
27  package net.sourceforge.barbecue;
28  
29  import net.sourceforge.barbecue.env.EnvironmentFactory;
30  import net.sourceforge.barbecue.env.HeadlessEnvironment;
31  import net.sourceforge.barbecue.output.GraphicsOutput;
32  import net.sourceforge.barbecue.output.Output;
33  import net.sourceforge.barbecue.output.OutputException;
34  import net.sourceforge.barbecue.output.SizingOutput;
35  
36  import javax.swing.*;
37  
38  import java.awt.*;
39  import java.awt.event.ActionEvent;
40  import java.awt.event.ActionListener;
41  import java.awt.event.MouseAdapter;
42  import java.awt.event.MouseListener;
43  import java.awt.event.MouseEvent;
44  import java.awt.print.PageFormat;
45  import java.awt.print.Printable;
46  import java.awt.print.PrinterException;
47  import java.awt.print.PrinterJob;
48  import java.text.CharacterIterator;
49  import java.text.StringCharacterIterator;
50  
51  /**
52   * Abstract barcode class that provides functionality that is common to
53   * all barcodes. Specific barcode implementations must subclass
54   * this and provide information specific to the barcode type they are implementing.
55   *
56   * @author <a href="mailto:opensource@ianbourke.com">Ian Bourke</a>
57   */
58  public abstract class Barcode extends JComponent 
59  	implements Printable {
60  
61      private static final int DEFAULT_BAR_HEIGHT = 50;
62  
63      protected String data;
64      protected String label;
65      protected boolean drawingText;
66      protected boolean drawingQuietSection = true;
67      protected int barWidth = 2;
68      protected int barHeight;
69      private Font font;
70      private Dimension size;
71      private int x;
72      private int y;
73      private int resolution = -1;
74  
75  	protected Barcode(String data) throws BarcodeException {
76          if (data == null || data.length() == 0) {
77              throw new BarcodeException("Data to encode cannot be empty");
78          }
79          this.data = data;
80          int minHeight = calculateMinimumBarHeight(getResolution());
81          if (minHeight > 0) {
82              this.barHeight = minHeight;
83          } else {
84              this.barHeight = Barcode.DEFAULT_BAR_HEIGHT;
85          }
86          this.font = EnvironmentFactory.getEnvironment().getDefaultFont();
87          this.drawingText = true;
88          setBackground(Color.white);
89          setForeground(Color.black);
90          setOpaque(true);
91  
92          
93          invalidateSize();
94      }
95  
96      /**
97       * Returns the data that the barcode is coding for.
98       *
99       * @return The barcode raw data
100      */
101     public String getData() {
102         return data;
103     }
104 
105     /**
106      * Sets the font to use when drawing the barcode data string underneath the barcode.
107      * <p/> Note that changing this setting after a barcode has been drawn will invalidate the
108      * component and may force a refresh.
109      *
110      * @param font The font to use
111      */
112     public void setFont(Font font) {
113         this.font = font;
114         invalidateSize();
115     }
116 
117     /**
118      * Indicates whether the barcode data should be shown as a string underneath the
119      * barcode or not.
120      * <p/> Note that changing this setting after a barcode has been drawn will invalidate the
121      * component and may force a refresh.
122      *
123      * @param drawingText True if the text should be shown, false if not
124      */
125     public void setDrawingText(boolean drawingText) {
126         this.drawingText = drawingText;
127         invalidateSize();
128     }
129 
130     /**
131      * Indicates whether the barcode is drawing a text label underneath the barcode or not.
132      *
133      * @return True if the text is drawn, false otherwise
134      */
135     public boolean isDrawingText() {
136         return drawingText;
137     }
138 
139     /**
140      * Indicates whether the leading and trailing white space should be rendered.
141      * <p/> Note that changing this setting after a barcode has been drawn will invalidate the
142      * component and may force a refresh.
143      *
144      * @param drawingQuietSection True if the quiet area/white space should be shown, false if not
145      */
146     public void setDrawingQuietSection(boolean drawingQuietSection) {
147         this.drawingQuietSection = drawingQuietSection;
148         invalidateSize();
149     }
150 
151     /**
152      * Indicates whether the barcode is drawing leading and trailing white space/quiet area.
153      *
154      * @return True if the quiet area/white space is drawn, false otherwise
155      */
156     public boolean isDrawingQuietSection() {
157         return drawingQuietSection;
158     }
159 
160     /**
161      * Sets the desired bar width for the barcode. This is the width (in pixels) of the
162      * thinnest bar in the barcode. Other bars will change their size relative to this.
163      * <p/> Note that changing this setting after a barcode has been drawn will invalidate the
164      * component and may force a refresh.
165      *
166      * @param barWidth The desired width of the thinnest bar in pixels
167      */
168     public void setBarWidth(int barWidth) {
169         if (barWidth >= 1) {
170             this.barWidth = barWidth;
171         } else {
172             this.barWidth = 1;
173         }
174         invalidateSize();
175     }
176 
177     /**
178      * Sets the desired height for the bars in the barcode (in pixels). Note that some
179      * barcode implementations will not allow the height to go below a minimum size. This
180      * is not the height of the component as a whole, as it does not specify the height of
181      * any text that may be drawn and does not include borders.
182      * <p/> Note that changing this setting after a barcode has been drawn will invalidate the
183      * component and may force a refresh.
184      *
185      * @param barHeight The desired height of the barcode bars in pixels
186      */
187     public void setBarHeight(int barHeight) {
188         // There is a minimum bar height that we must enforce
189         if (barHeight > calculateMinimumBarHeight(getResolution())) {
190             this.barHeight = barHeight;
191             invalidateSize();
192         }
193     }
194 
195     /**
196      * Sets the desired output resolution for the barcode. This method should
197      * be used in cases where the barcode is either being outputted to a device
198      * other than the screen, or the barcode is being generated on a headless
199      * machine (e.g. a rack mounted server) and the screen resolution cannot be
200      * determined. Note that is the barcode is generated in either of these situations
201      * and this method has not been called, the resolution is assumed to be 72 dots
202      * per inch.
203      *
204      * @param resolution The desired output resolution (in dots per inch)
205      */
206     public void setResolution(int resolution) {
207         if (resolution > 0) {
208             this.resolution = resolution;
209             int newHeight = calculateMinimumBarHeight(getResolution());
210             if (newHeight > this.barHeight) {
211                 this.barHeight = newHeight;
212             }
213             invalidateSize();
214         }
215     }
216 
217     /**
218      * From {@link javax.swing.JComponent JComponent}.
219      *
220      * @return The X co-ordinate of the component's origin
221      */
222     public int getX() {
223         return x;
224     }
225 
226     /**
227      * From {@link javax.swing.JComponent JComponent}.
228      *
229      * @return The Y co-ordinate of the component's origin
230      */
231     public int getY() {
232         return y;
233     }
234 
235     /**
236      * From {@link javax.swing.JComponent JComponent}.
237      *
238      * @return The width of this component
239      */
240     public int getWidth() {
241         return (int) getActualSize().getWidth();
242     }
243 
244     /**
245      * From {@link javax.swing.JComponent JComponent}.
246      *
247      * @return The height of this component
248      */
249     public int getHeight() {
250         return (int) getActualSize().getHeight();
251     }
252 
253     /**
254      * From {@link javax.swing.JComponent JComponent}.
255      *
256      * @return The bounds of this component
257      */
258     public Rectangle getBounds() {
259         return getBounds(new Rectangle());
260     }
261 
262     /**
263      * From {@link javax.swing.JComponent JComponent}.
264      *
265      * @param rv The rectangle to set the bounds on
266      * @return The updated rv
267      */
268     public Rectangle getBounds(Rectangle rv) {
269         rv.setBounds(getX(), getY(), (int) getActualSize().getWidth() + getX(),
270                 (int) getActualSize().getHeight() + getY());
271         return rv;
272     }
273 
274     /**
275      * From {@link javax.swing.JComponent JComponent}.
276      *
277      * @return The preferred size of this component
278      */
279     public Dimension getPreferredSize() {
280         return getActualSize();
281     }
282 
283     /**
284      * From {@link javax.swing.JComponent JComponent}.
285      *
286      * @return The minimum size of this component
287      */
288     public Dimension getMinimumSize() {
289         return getActualSize();
290     }
291 
292     /**
293      * From {@link javax.swing.JComponent JComponent}.
294      *
295      * @return The maximum size of this component
296      */
297     public Dimension getMaximumSize() {
298         return getActualSize();
299     }
300 
301     /**
302      * From {@link javax.swing.JComponent JComponent}.
303      *
304      * @return The actual size of this component
305      */
306     public Dimension getSize() {
307         return getActualSize();
308     }
309 
310     /**
311      * Renders this <code>Barcode</code> at the specified location in
312      * the specified {@link java.awt.Graphics2D Graphics2D} context.
313      * The origin of the layout is placed at x,&nbsp;y.  Rendering may touch
314      * any point within <code>getBounds()</code> of this position.  This
315      * leaves the <code>g2</code> unchanged.
316      *
317      * @param g The graphics context
318      * @param x The horizontal value of the upper left co-ordinate of the bounding box
319      * @param y The vertical value of the upper left co-ordinate of the bounding box
320      */
321     public void draw(Graphics2D g, int x, int y) throws OutputException {
322         this.x = x;
323         this.y = y;
324 
325         Output output = new GraphicsOutput(g, font, getForeground(), getBackground());
326         size = draw(output, x, y, barWidth, barHeight);
327     }
328 
329     public void output(Output output) throws OutputException {
330         draw(output, 0, 0, barWidth, barHeight);
331     }
332 
333     protected abstract Module[] encodeData();
334 
335     protected abstract Module calculateChecksum();
336 
337     protected abstract Module getPreAmble();
338 
339     protected abstract Module getPostAmble();
340 
341     protected abstract Dimension draw(Output output, int x, int y, int barWidth, int barHeight) throws OutputException;
342 
343     /**
344      * Returns the text that will be displayed underneath the barcode (if requested).
345      *
346      * @return The text label for the barcode
347      */
348     public String getLabel() {
349         if (label != null) {
350             return label;
351         } else {
352             return beautify(data);
353         }
354     }
355 
356     /**
357      * Sets the human readable text to be displayed underneath the barcode.
358      * If set to null then the text will automaticaly be generated.
359      *
360      * @param label the human readable barcode text
361      * @see #getLabel()
362      */
363     public void setLabel(String label) {
364         this.label = label;
365     }
366 
367     protected int calculateMinimumBarHeight(int resolution) {
368         return 0;
369     }
370 
371     /**
372      * From {@link javax.swing.JComponent JComponent}.
373      *
374      * @param g The graphics to paint the component onto
375      */
376     protected void paintComponent(Graphics g) {
377         super.paintComponent(g);
378         Insets insets = getInsets();
379         try {
380             draw((Graphics2D) g, insets.left, insets.top);
381         } catch (OutputException e) {
382             // Don't draw anything
383         }
384     }
385 
386     // TODO: Move this to the output
387     protected int getResolution() {
388         if (resolution > 0) {
389             return resolution;
390         }
391         return EnvironmentFactory.getEnvironment().getResolution();
392     }
393 
394     protected int drawModule(Module module, Output output, int x, int y, int barWidth, int barHeight) throws OutputException {
395         if (module == null) {
396             return 0;
397         }
398         return module.draw(output, x, y, barWidth, barHeight);
399     }
400 
401     protected String beautify(String s) {
402         StringBuffer buf = new StringBuffer();
403         StringCharacterIterator iter = new StringCharacterIterator(s);
404         for (char c = iter.first(); c != CharacterIterator.DONE; c = iter.next()) {
405             if (Character.isDefined(c) && !Character.isISOControl(c)) {
406                 buf.append(c);
407             }
408         }
409         return buf.toString();
410     }
411 
412     private void invalidateSize() {
413         size = null;
414     }
415 
416     private Dimension getActualSize() {
417         if (size == null) {
418             size = calculateSize();
419         }
420         return size;
421     }
422 
423     private Dimension calculateSize() {
424         Dimension d = new Dimension();
425         if (EnvironmentFactory.getEnvironment() instanceof HeadlessEnvironment) {
426             try {
427                 d = draw(new SizingOutput(font, getForeground(), getBackground()), 0, 0, barWidth, barHeight);
428             } catch (OutputException e) {
429             }
430         } else {
431             try {
432                 FontMetrics fontMetrics = null;
433                 if (font != null) {
434                     fontMetrics = getFontMetrics(font);
435                 }
436                 d = draw(new SizingOutput(font, fontMetrics, getForeground(), getBackground()), 0, 0, barWidth, barHeight);
437             } catch (OutputException e) {
438             }
439         }
440 
441         return d;
442     }
443     
444     public int print(Graphics g, PageFormat pageFormat, int pageIndex)
445     				throws PrinterException {
446     	
447     	if (pageIndex >= 1) {
448             return Printable.NO_SUCH_PAGE;
449         }
450     	
451     	try
452     	{
453     		this.draw( (Graphics2D) g, 0, 0);
454             return Printable.PAGE_EXISTS;    	
455     	}
456     	catch (OutputException ex)
457     	{
458     		throw new PrinterException(ex.getMessage()); 
459     	}
460         
461     }
462     
463     public String toString()
464     {
465     	return this.getData();
466     }
467     
468 
469     
470 }