KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > gnu > jpdf > BoundingBox


1 /*
2  * $Id: BoundingBox.java,v 1.8 2002/02/05 19:25:35 ezb Exp $
3  *
4  * $Date: 2002/02/05 19:25:35 $
5  *
6  * Copyright (c) Eric Z. Beard, ericzbeard@hotmail.com
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21  *
22  */

23 package gnu.jpdf;
24
25 import java.awt.*;
26 import java.util.*;
27
28 /**
29  * <p>This class simplifies the placement of Strings within
30  * a canvas area where the placement of objects is absolute</p>
31  *
32  * <p>A <code>BoundingBox</code> is just a Rectangle that knows how to
33  * find the coordinates for a String based on the desired alignment and
34  * <code>FontMetrics</code>. For each new String, a new child
35  * <code>BoundingBox</code> is made that can be subtracted from the
36  * original box so new Strings can be added</p>
37  *
38  * <p>One of the more helpful features of this class is the string wrap
39  * feature of <code>getStringBounds</code>. The box returned by that method
40  * will contain an array of strings that have been broken down to fit the
41  * box. The box's coordinates and size will reflect the size of the
42  * entire group of strings if it is laid out as expected. Using the
43  * returned box and iterating through the array of strings from top to
44  * bottom, getting new bounding boxes for each one (with upper left
45  * alignment and no padding) will result in the correct string wrap.</p>
46  *
47  * <p>Note that you will need to have Xvfb running on a Unix server to
48  * use this class</p>
49  *
50  * @author Eric Z. Beard, ericzbeard@hotmail.com
51  * @author $Author: ezb $
52  * @version $Revision: 1.8 $, $Date: 2002/02/05 19:25:35 $
53  */

54 public class BoundingBox extends Rectangle
55 {
56   /** Percent f line height to space lines */
57   public static final int LINE_SPACING_PERCENTAGE = 20;
58
59   /** Used to a align a String centered vertically */
60   public static final int VERT_ALIGN_CENTER = 0;
61
62   /** Used to align a String at the top of the box */
63   public static final int VERT_ALIGN_TOP = 1;
64
65   /** Used to align a String at the bottom of the box */
66   public static final int VERT_ALIGN_BOTTOM = 2;
67
68   /** Used to align a String horizontally in the center of the box */
69   public static final int HORIZ_ALIGN_CENTER = 3;
70
71   /** Used to align a String to the left in the box */
72   public static final int HORIZ_ALIGN_LEFT = 4;
73
74   /** Used to aling a String to the right in a box */
75   public static final int HORIZ_ALIGN_RIGHT = 5;
76
77   /** Used to subtract a child from a box, *leaving* the top portion */
78   public static final int SUBTRACT_FROM_TOP = 6;
79
80   /** Used to subtract a child from a box, *leaving* the bottom portion */
81   public static final int SUBTRACT_FROM_BOTTOM = 7;
82
83   /** Used to subtract a child from a box, *leaving* the left portion */
84   public static final int SUBTRACT_FROM_LEFT = 8;
85
86   /** Used to subtract a child from a box, *leaving" the right portion */
87   public static final int SUBTRACT_FROM_RIGHT = 9;
88
89   private static final int[] VERT_ALIGNS = {VERT_ALIGN_CENTER,
90                                             VERT_ALIGN_TOP,
91                                             VERT_ALIGN_BOTTOM};
92
93   private static final int[] HORIZ_ALIGNS = {HORIZ_ALIGN_CENTER,
94                                              HORIZ_ALIGN_LEFT,
95                                              HORIZ_ALIGN_RIGHT};
96
97   private static final int[] SUBTRACTS = {SUBTRACT_FROM_TOP,
98                                           SUBTRACT_FROM_BOTTOM,
99                                           SUBTRACT_FROM_LEFT,
100                                           SUBTRACT_FROM_RIGHT};
101
102   /** The point to use for Graphics.drawString() */
103   private Point drawingPoint;
104
105   /** The absolute, world location of the box */
106   private Point absoluteLocation;
107
108   /** Link to parent box */
109   private BoundingBox parent;
110   
111   /**
112    * If this box was the result of a getStringBounds call, this
113    * array will hold the broken strings
114    */

115   private String JavaDoc[] stringArray;
116
117   /** The string specified in getStringBounds */
118   private String JavaDoc fullString;
119   
120   /**
121    * Creates a new <code>BoundingBox</code> instance.
122    *
123    * @param p a <code>Point</code>, upper left coords
124    * @param d a <code>Dimension</code>, used to determine height and width
125    */

126   public BoundingBox(Point p, Dimension d) {
127     super(p, d);
128     this.drawingPoint = this.getLocation();
129     this.absoluteLocation = this.getLocation();
130   }
131
132   
133
134   /**
135    * <p>Returns true if this box has a parent. The 'world', or
136    * enclosing canvas is not considered a parent</p>
137    *
138    * @return a <code>boolean</code> value
139    */

140   public boolean hasParent() {
141     if (parent == null) {
142       return false;
143     }
144     else {
145       return true;
146     }
147   }
148
149   /**
150    * <p>Get this box's parent box</p>
151    *
152    * @return a <code>BoundingBox</code> value
153    */

154   public BoundingBox getParent() {
155     return parent;
156   }
157
158
159
160   /**
161    * <p>Make the specified box this box's child. Equivalent to
162    * <code>child.setParent(parent)</code> where the specified 'parent' is
163    * this instance</p>
164    *
165    * @param child a <code>BoundingBox</code>, any box that can fit inside
166    * this one. The results of calling
167    * <code>getAbsoluteLocation()</code> on the child will be
168    * altered after this to take into account the child's
169    * new location in the 'world'
170    *
171    */

172   public void add(BoundingBox child) {
173     child.setParent(this);
174   }
175
176
177
178   /**
179    * <p>Make the specified box this box's parent</p>
180    *
181    * @param parent a <code>BoundingBox</code> value
182    */

183   public void setParent(BoundingBox parent) {
184     // Prevent infinite recursion
185
if (this == parent) {
186       return;
187     }
188     this.parent = parent;
189
190     // If this box was created empty, without a String inside,
191
// determine its absolute location
192
if (this.getLocation().equals(this.getAbsoluteLocation())) {
193       int ancestorTranslateX = 0;
194       int ancestorTranslateY = 0;
195
196       BoundingBox ancestor = this;
197       while (ancestor.hasParent()) {
198         BoundingBox oldRef = ancestor;
199         ancestor = ancestor.getParent();
200         // Prevent infinite recursion
201
if (ancestor == oldRef) {
202           break;
203         }
204         ancestorTranslateX += (int)ancestor.getLocation().getX();
205         ancestorTranslateY += (int)ancestor.getLocation().getY();
206       }
207
208       this.getAbsoluteLocation().translate(ancestorTranslateX,
209                                            ancestorTranslateY);
210     } // end if
211
} // end setParent
212

213
214
215
216
217   /**
218    * <p>Get the wrapped strings if this box was from a call to getStringBounds,
219    * otherwise this method returns null</p>
220    *
221    * @return a <code>String[]</code> array of strings, top to bottom in layout
222    */

223   public String JavaDoc[] getStringArray() {
224     return stringArray;
225   } // end getStringArray
226

227
228
229   /**
230    * <p>Set the value of the string array</p>
231    *
232    * @param strArray a <code>String</code> array
233    *
234    */

235   public void setStringArray(String JavaDoc[] strArray) {
236     this.stringArray = strArray;
237   }
238
239
240   /**
241    * <p>Set the absolute upper left world location point for this box</p>
242    *
243    * @param point a <code>Point</code> value
244    */

245   public void setAbsoluteLocation(Point point) {
246     this.absoluteLocation = point;
247   }
248
249
250
251   /**
252    * <p>Returns false if for any reason this box has negative dimensions</p>
253    */

254   public boolean boxExists() {
255     if ((this.getHeight() < 0) || (this.getWidth() < 0)) {
256       return false;
257     }
258     return true;
259   } // end boxExists
260

261
262
263
264   /**
265    * <p>Get the absolute upper left location point for this box</p>
266    *
267    * @return a <code>Point</code> value
268    */

269   public Point getAbsoluteLocation() {
270     return absoluteLocation;
271   }
272
273
274   /**
275    * <p>Returns the full string associated with a call to
276    * <code>getStringBounds</code></p>
277    */

278   public String JavaDoc getFullString() {
279     return fullString;
280   }
281
282
283   /**
284    * <p>Sets the full string associated with <code>getStringBounds</code></p>
285    *
286    * @param string a <code>String</code>
287    */

288   public void setFullString(String JavaDoc string) {
289     this.fullString = string;
290   }
291
292   /**
293    * <p>Gets the location of a String after it is adjusted for
294    * alignment within this box. The point's coordinates are
295    * either within this box or within the enclosing area.</p>
296    *
297    * @param string a <code>String</code>, the String to be placed
298    * @param hAlign an <code>int</code>, HORIZ_ALIGN_CENTER,
299    * HORIZ_ALIGN_LEFT, HORIX_ALIGN_RIGHT
300    * @param vAlign an <code>int</code>, VERT_ALIGN_CENTER,
301    * VERT_ALIGN_TOP, VERT_ALIGN_BOTTOM
302    * @param fm a <code>FontMetrics</code> object for this String
303    * @param padding an <code>int</code>, the padding around the String
304    * @param enforce a <code>boolean</code>, if true the method will throw
305    * an exception when the string is too big, if not true it will break
306    * the string down and overrun the bottom of the box. If the box
307    * is too small for even one word, the exception will be thrown
308    * @return a <code>Point</code>, the coords to use in drawString()
309    * @see #HORIZ_ALIGN_LEFT
310    * @see #HORIZ_ALIGN_CENTER
311    * @see #HORIZ_ALIGN_RIGHT
312    * @see #VERT_ALIGN_TOP
313    * @see #VERT_ALIGN_CENTER
314    * @see #VERT_ALIGN_BOTTOM
315    * @throws <code>IllegalArgumentException</code> if the args are invalid
316    * @throws <code>StringTooLongException</code> if the string won't fit
317    * and enforce is set to true. The exception can still be thrown
318    * if enforce is false, but only in cases such as the box having
319    * no height or width
320    */

321   public BoundingBox getStringBounds(String JavaDoc string,
322                                      int hAlign,
323                                      int vAlign,
324                                      FontMetrics fm,
325                                      int padding,
326                                      boolean enforce)
327     throws IllegalArgumentException JavaDoc, StringTooLongException {
328     // Check to make sure the values passed in are valid
329
if (!checkHAlign(hAlign)) {
330       throw new IllegalArgumentException JavaDoc("BoundingBox.getStringBounds, " +
331                         "hAlign invalid : " + hAlign);
332     }
333     if (!checkVAlign(vAlign)) {
334       throw new IllegalArgumentException JavaDoc("BoundingBox.getStringBounds, " +
335                        "vAlign invalid : " + hAlign);
336     }
337     if (fm == null) {
338       throw new IllegalArgumentException JavaDoc("BoundingBox.getStringBounds, " +
339                     "FontMetrics null");
340     }
341     if (string == null) {
342       throw new IllegalArgumentException JavaDoc("BoundingBox.getStringBounds, " +
343                       "String null");
344     }
345
346     // NOTE: For this portion of the method, parent refers
347
// to this object and child refers to the object about
348
// to be created. When the absolute point for drawing the
349
// String is determined, this object's ancestors are checked.
350
Point parentLocation = this.getLocation();
351     Dimension parentSize = this.getSize();
352
353     Point childLocation;
354     Dimension childSize;
355
356     // String ascent, width, height, parent, child width, height
357
int sa, sw, sh, pw, ph, cw, ch;
358
359     // Child, parent x, y coords for upper left
360
int cx, cy, px, py;
361
362     sa = fm.getMaxAscent();
363     sw = fm.stringWidth(string);
364     sh = sa + fm.getMaxDescent();
365     pw = (int)parentSize.getWidth();
366     ph = (int)parentSize.getHeight();
367     if (pw < 0) {
368       throw new StringTooLongException("The parent box has a negative width " +
369                           " (" + pw + ")");
370     }
371     if (ph < 0) {
372       throw new StringTooLongException("The parent box has a negative height"+
373                           " (" + ph + ")");
374     }
375     cw = sw + padding*2;
376     ch = sh + padding*2;
377     px = (int)this.getX();
378     py = (int)this.getY();
379
380     String JavaDoc[] childStrArray = null;
381
382     if ((cw > pw) || (string.indexOf("\n") != -1)) {
383       cw = pw - (padding * 2);
384       childStrArray = createStringArray(string, fm, padding, pw);
385       ch = getWrappedHeight(childStrArray, fm, padding);
386       if (ch > ph) {
387         // If enforce is not true, it means we want the box to
388
// be returned anyway (along with the strings in the array)
389
// so we can chop them manually and try again
390
if (enforce) {
391           throw new StringTooLongException("The wrapped strings do not " +
392                       "fit into the parent box, pw=" + pw +
393                       ", ph=" + ph + ", ch=" + ch + ", cw=" + cw +
394                       ", string: " + string);
395         }
396       }
397     }
398
399     // Need to have child width and height, and string array set
400

401     // Child location is relative to this (parent) box, not the world
402
if (vAlign == VERT_ALIGN_TOP) {
403       cy = 0;
404     }
405     else if (vAlign == VERT_ALIGN_CENTER) {
406       cy = (ph/2) - (ch/2);
407     }
408     else {
409       cy = ph - ch;
410     }
411
412     if (hAlign == HORIZ_ALIGN_LEFT) {
413       cx = 0;
414     }
415     else if (hAlign == HORIZ_ALIGN_CENTER) {
416       cx = (pw/2) - (cw/2);
417     }
418     else {
419       cx = pw - cw;
420     }
421
422     childLocation = new Point(cx, cy);
423     childSize = new Dimension(cw, ch);
424     
425     // Drawing location is based on the baseline of the String, and
426
// relative to the world, not this box. The drawing point differs
427
// from the absolute box location because of padding and ascent
428
int dpx, dpy, abx, aby;
429
430     // If this object also has a parent (maybe grandparents), iterate
431
// through them and find the absolute 'world' location
432
int ancestorTranslateX = 0;
433     int ancestorTranslateY = 0;
434
435     BoundingBox ancestor = this;
436     while (ancestor.hasParent()) {
437       BoundingBox oldRef = ancestor;
438       ancestor = ancestor.getParent();
439       // Prevent infinite recursion
440
if (ancestor == oldRef) {
441         break;
442       }
443       ancestorTranslateX += (int)ancestor.getLocation().getX();
444       ancestorTranslateY += (int)ancestor.getLocation().getY();
445     }
446
447     // Determine the absolute location for the box
448
abx = px + cx + ancestorTranslateX;
449     aby = py + cy + ancestorTranslateY;
450
451     // Determine the absolute drawing point for the String
452
dpx = abx + padding;
453     dpy = aby + padding + sa;
454
455     Point drawingPoint = new Point(dpx, dpy);
456     BoundingBox returnChild = new BoundingBox(childLocation,
457                                               childSize,
458                                               drawingPoint,
459                                               new Point(abx, aby));
460     this.add(returnChild);
461     returnChild.setFullString(string);
462     returnChild.setStringArray(childStrArray);
463     return returnChild;
464
465   } // end getStringBounds
466

467
468   /**
469    * <p>Gets the location of a String after it is adjusted for
470    * alignment within this box. The point's coordinates are
471    * either within this box or within the enclosing area.</p>
472    *
473    * <p>By default, this method enforces string length and throws the
474    * exception if it is too long</p>
475    *
476    * @param string a <code>String</code>, the String to be placed
477    * @param hAlign an <code>int</code>, HORIZ_ALIGN_CENTER,
478    * HORIZ_ALIGN_LEFT, HORIX_ALIGN_RIGHT
479    * @param vAlign an <code>int</code>, VERT_ALIGN_CENTER,
480    * VERT_ALIGN_TOP, VERT_ALIGN_BOTTOM
481    * @param fm a <code>FontMetrics</code> object for this String
482    * @param padding an <code>int</code>, the padding around the String
483    * @return a <code>Point</code>, the coords to use in drawString()
484    * @throws <code>IllegalArgumentException</code> if the args are invalid
485    * @throws <code>StringTooLongException</code> if the string won't fit
486    */

487   public BoundingBox getStringBounds(String JavaDoc string,
488                                      int hAlign,
489                                      int vAlign,
490                                      FontMetrics fm,
491                                      int padding)
492     throws StringTooLongException, IllegalArgumentException JavaDoc {
493     return getStringBounds(string, hAlign, vAlign, fm, padding, true);
494   } // end getStringBounds (enforce true by default)
495

496   
497
498   /**
499    * <p>This method is called after getting the box by calling
500    * <code>getStringBounds</code> on the parent. Wraps the string at
501    * word boundaries and draws it to the specified <code>Graphics</code>
502    * context. Make sure padding is the same as specified for the
503    * <code>getStringBounds</code> call, or you may get an unexpected
504    * {@link gnu.jpdf.StringTooLongException}</p>
505    *
506    * @param g the <code>Graphics</code> object
507    * @param fm the <code>FontMetrics</code> to use for sizing
508    * @param padding an int, the padding around the strings
509    * @param hAlign the <code>int</code> horizontal alignment
510    * @throws <code>IllegalArgumentException</code> if the args are invalid
511    * @throws <code>StringTooLongException</code> if the string
512    * won't fit this will only happen if the fm or padding has
513    * been changed since getStringBounds was called succesfully
514    */

515   public void drawWrappedString(Graphics g,
516                                 FontMetrics fm,
517                                 int padding,
518                                 int hAlign)
519     throws IllegalArgumentException JavaDoc, StringTooLongException {
520     if (getStringArray() == null) {
521       Point p = getDrawingPoint();
522       int xx = (int)p.getX();
523       int yy = (int)p.getY();
524       g.drawString(getFullString(), xx, yy);
525     }
526     else {
527       int len = stringArray.length;
528       for (int i = 0; i < len; i++) {
529         BoundingBox wrappedBox = null;
530         wrappedBox = getStringBounds(stringArray[i],
531                                          hAlign,
532                                          BoundingBox.VERT_ALIGN_TOP,
533                                          fm,
534                                          0);
535         Point pp = wrappedBox.getDrawingPoint();
536         int xx = (int)pp.getX();
537         if (hAlign == BoundingBox.HORIZ_ALIGN_RIGHT) {
538           xx -= padding;
539         }
540         if (hAlign == BoundingBox.HORIZ_ALIGN_LEFT) {
541           xx += padding;
542         }
543         int yy = (int)pp.getY() + padding;
544         g.drawString(stringArray[i], xx, yy);
545         subtract(wrappedBox, BoundingBox.SUBTRACT_FROM_BOTTOM);
546       }
547     }
548   } // end drawWrappedString
549

550
551   /**
552    * <p>Draws lines from the wrapped string until there is no more room and
553    * then stops. If there is no string or the box is too small for
554    * anything to be drawn, does nothing</p>
555    *
556    * @param g the <code>Graphics</code> object to draw to
557    * @param fm the <code>FontMetrics</code> object to use for string sizing
558    * @param padding the <code>int</code> amount of padding around the string
559    * @param hAlign the <code>int</code> horizontal alignment
560    *
561    */

562   public void drawWrappedStringTruncate(Graphics g,
563                                         FontMetrics fm,
564                                         int padding,
565                                         int hAlign) {
566     
567     if (getStringArray() == null) {
568       Point p = getDrawingPoint();
569       int xx = (int)p.getX();
570       int yy = (int)p.getY();
571       if (getFullString() != null) {
572         g.drawString(getFullString(), xx, yy);
573       }
574       else {
575         System.err.println("getStringArray and getFullString are null");
576       }
577     }
578     else {
579       int totalHeight = 0;
580       int len = stringArray.length;
581       for (int i = 0; i < len; i++) {
582         BoundingBox wrappedBox = null;
583         try {
584           wrappedBox = getStringBounds(stringArray[i],
585                                          hAlign,
586                                          BoundingBox.VERT_ALIGN_TOP,
587                                          fm,
588                                          0,
589                                          false);
590           totalHeight += (int)wrappedBox.getHeight();
591           if (getParent() != null) {
592             if (totalHeight > (int)(getParent().getHeight())) {
593               return;
594             }
595           }
596         }
597         catch (StringTooLongException stle) {
598           stle.printStackTrace();
599           return;
600         }
601         wrappedBox.drawChoppedString(g, fm, padding, hAlign);
602         subtract(wrappedBox, BoundingBox.SUBTRACT_FROM_BOTTOM);
603       }
604     }
605   } // end drawWrappedStringTruncate
606

607
608   /**
609    * <p>Take the first line of the string (if it is wrapped, otherwise just
610    * take the whole string) and chop the end of it off to make it fit in the
611    * box. If the box is smaller than one letter, draw nothing</p>
612    *
613    * @param g the <code>Graphics</code> object to draw to
614    * @param fm the <code>FontMetrics</code> object to use for string sizing
615    * @param padding the <code>int</code> amount of padding around the string
616    * @param hAlign the <code>int</code> horizontal alignment
617    */

618   public void drawChoppedString(Graphics g,
619                                 FontMetrics fm,
620                                 int padding,
621                                 int hAlign) {
622
623     String JavaDoc string = "";
624     if (getStringArray() != null) {
625       string = new String JavaDoc(getStringArray()[0]);
626     }
627     else {
628       string = new String JavaDoc(getFullString());
629     }
630     BoundingBox choppedBox = null;
631     try {
632       choppedBox = getStringBounds(string,
633                                    hAlign,
634                                    VERT_ALIGN_TOP,
635                                    fm,
636                                    padding);
637       Point p = choppedBox.getDrawingPoint();
638       int x = (int)p.getX();
639       int y = (int)p.getY();
640       g.drawString(string, x, y);
641     }
642     catch (StringTooLongException stle) {
643       // Doesn't fit - start cutting from the end until it does
644
StringBuffer JavaDoc buf = new StringBuffer JavaDoc().append(string);
645       if (buf.length() == 0) {
646         System.out.println("BoundingBox.drawChoppedString, buf len 0 ??");
647         //return;
648
throw new RuntimeException JavaDoc();
649       }
650       buf.deleteCharAt(buf.length()-1);
651       while ((fm.stringWidth(buf.toString()) > (int)getWidth()) &&
652              (buf.length() > 0)) {
653         buf.deleteCharAt(buf.length()-1);
654       }
655       
656       try {
657         choppedBox = getStringBounds(buf.toString(),
658                                      hAlign,
659                                      VERT_ALIGN_TOP,
660                                      fm,
661                                      padding);
662       Point pp = choppedBox.getDrawingPoint();
663       int xx = (int)pp.getX();
664       int yy = (int)pp.getY();
665       g.drawString(string, xx, yy);
666       }
667       catch (StringTooLongException sstle) {
668         // Must be a really small box!
669
sstle.printStackTrace();
670       }
671     }
672   } // end drawChoppedString
673

674
675
676
677
678
679
680
681
682   /**
683    * <p>Get the total height of the box needed to contain the strings in
684    * the specified array</p>
685    */

686   private int getWrappedHeight(String JavaDoc[] strings, FontMetrics fm, int padding) {
687     int ma = fm.getMaxAscent();
688     int md = fm.getMaxDescent();
689     int sh = ma + md;
690     int hPad = sh / LINE_SPACING_PERCENTAGE;
691     sh += hPad;
692     int total = sh * strings.length;
693
694     return total + (padding*2);
695   } // end getWrappedHeight
696

697
698
699   /**
700    *
701    * <p>Make a string array from a string, wrapped to fit the box</p>
702    *
703    * <p>If the line width is too short, the array is just a
704    * tokenized version of the string</p>
705    *
706    * @param string - the <code>String</code> to convert to an array
707    * @param
708    */

709   private String JavaDoc[] createStringArray(String JavaDoc string,
710                                      FontMetrics fm,
711                                      int padding,
712                                      int pw) {
713     if (string == null) {
714       System.err.println("Tried createStringArray with null String");
715       return null;
716     }
717     if (fm == null) {
718       System.err.println("Tried createStringArray with null FontMetrics");
719     }
720
721     int sw = fm.stringWidth(string);
722     int lw = pw - (padding*2);
723
724    
725
726     Vector returnVector = new Vector();
727     // Return delimiters as tokens
728
StringTokenizer st = new StringTokenizer(string, " \t\n\r\f", true);
729     StringBuffer JavaDoc tempBuffer = new StringBuffer JavaDoc();
730     StringBuffer JavaDoc finalBuffer = new StringBuffer JavaDoc();
731
732     while(st.hasMoreTokens()) {
733       // Get the next word and add a space after it
734
String JavaDoc tempString = st.nextToken();
735       tempBuffer.append(tempString);
736
737       // If we haven't reached the width with our current
738
// line, keep adding tokens. Also, check for hard returns
739
if ((fm.stringWidth(tempBuffer.toString()) < lw) &&
740           (tempBuffer.toString()
741               .charAt(tempBuffer.toString().length() - 1) != '\n') &&
742           (tempBuffer.toString()
743               .charAt(tempBuffer.toString().length() - 1) != '\r')) {
744        finalBuffer.append(tempString);
745        continue;
746       }
747       else {
748         returnVector.addElement(finalBuffer.toString());
749         finalBuffer.delete(0, finalBuffer.length());
750         tempBuffer.delete(0, tempBuffer.length());
751         if ((tempString.charAt(0) != '\n') &&
752             (tempString.charAt(0) != '\r')) {
753           tempBuffer.append(tempString);
754           finalBuffer.append(tempString);
755         }
756         continue;
757       }
758                            
759     } // end while
760
returnVector.addElement(finalBuffer.toString());
761
762     int len = returnVector.size();
763     // Init the class member field stringArray
764
String JavaDoc[] childStrArray = new String JavaDoc[len];
765     for (int i = 0; i < len; i++) {
766       String JavaDoc curStr = (String JavaDoc)returnVector.get(i);
767       childStrArray[i] = curStr;
768     }
769
770     return childStrArray;
771     
772   } // end createStringArray
773

774
775
776   /**
777    * <p>Removes the child box from this parent box. The child must
778    * have this object as its parent or the method does nothing.
779    * The BoundingBox returned will be cut by an area equal to
780    * the child area plus the horizontal or vertical strip in
781    * which it sits, depending on the 'subtractFrom' value passed
782    * in</p>
783    *
784    * @param child a <code>BoundingBox</code> value
785    * @param int an <code>int</code>, SUBTRACT_FROM_LEFT,
786             SUBTRACT_FROM_RIGHT, SUBTRACT_FROM_TOP,
787             SUBTRACT_FROM_BOTTOM
788    * @return a <code>BoundingBox</code> value
789    * @see #SUBTRACT_FROM_LEFT
790    * @see #SUBTRACT_FROM_RIGHT
791    * @see #SUBTRACT_FROM_TOP
792    * @see #SUBTRACT_FROM_BOTTOM
793    */

794   public BoundingBox subtract(BoundingBox child, int subtractFrom) {
795     // First, check to see if the params are valid
796
if (child == null) {
797       throw new IllegalArgumentException JavaDoc("BoundingBox.subtract, " +
798                       "BoundingBox child is null");
799     }
800     if (!child.hasParent()) {
801       throw new IllegalArgumentException JavaDoc("BoundingBox.subtract, " +
802                          "BoundingBox child has no parent");
803     }
804     else {
805       if (!(child.getParent() == this)) {
806         throw new IllegalArgumentException JavaDoc("BoundingBox.subtract, " +
807                            "this is not BoundingBox child's parent");
808       }
809       else {
810         // Now that we know the child is this object's child, we continue
811
// and check the subtractFrom param
812
int len = SUBTRACTS.length;
813         boolean valid = false;
814         for (int i = 0; i < len; i++) {
815           if (subtractFrom == SUBTRACTS[i]) {
816             valid = true;
817           }
818         }
819         if (!valid) {
820           throw new IllegalArgumentException JavaDoc("BoundingBox.subtract, " +
821                              "subtractFrom invalid: " + subtractFrom);
822         }
823
824         // Now we know the child is valid, and if the subtractFrom
825
// preference was invalid, we subtract from the bottom
826

827         // The child should no longer be used, since the parent
828
// reference will be invalid
829
child.setParent(null);
830
831         int cx = (int)child.getLocation().getX();
832         int cy = (int)child.getLocation().getY();
833         int cw = (int)child.getSize().getWidth();
834         int ch = (int)child.getSize().getHeight();
835         int px = (int)this.getLocation().getX();
836         int py = (int)this.getLocation().getY();
837         int pw = (int)this.getSize().getWidth();
838         int ph = (int)this.getSize().getHeight();
839
840         switch (subtractFrom) {
841         case SUBTRACT_FROM_LEFT:
842           // This will be useful for right-justified Strings in tables
843
pw = cx;
844           this.setSize(new Dimension(pw, ph));
845           return this;
846
847         case SUBTRACT_FROM_RIGHT:
848           // This will be useful for left justified Strings in tables
849
px = px + cw + cx;
850           pw = pw - cw - cx;
851           this.setLocation(new Point(px, py));
852           this.setSize(new Dimension(pw, ph));
853           return this;
854
855         case SUBTRACT_FROM_BOTTOM:
856           py = py + ch + cy;
857           ph = ph - ch - cy;
858           this.setLocation(new Point(px, py));
859           this.setSize(new Dimension(pw, ph));
860           return this;
861
862         case SUBTRACT_FROM_TOP:
863           ph = cy;
864           this.setSize(new Dimension(pw, ph));
865           return this;
866
867         default: // Should never happen
868
break;
869         } // end switch
870
}
871     }
872     return this;
873   } // end subtract
874

875
876
877
878   /**
879    * <p>Gets the drawing point to use in Graphics drawing
880    * methods. After getting a new BoundingBox with getStringBounds(),
881    * calling this method will give you an absolute point, accounting
882    * for alignment and padding, etc, from which to start drawing the
883    * String</p>
884    *
885    * <p>If getStringBounds was not called (this is a parent box), the
886    * upper left coordinates will be returned (this.getLocation())</p>
887    *
888    * @return a <code>Point</code>
889    */

890   public Point getDrawingPoint() {
891     return drawingPoint;
892   }
893
894
895
896
897
898   // main method is for testing /////////////////
899

900
901
902     /**
903      * For testing
904      *
905      * @param args a <code>String[]</code> value
906      */

907     public static void main(String JavaDoc[] args) {
908     Point upperLeft = new Point(5, 5);
909     Dimension bounds = new Dimension(100, 100);
910     BoundingBox parent = new BoundingBox(upperLeft, bounds);
911     String JavaDoc string = "Hello World!";
912     Font font = new Font("SansSerif", Font.PLAIN, 12);
913     Frame frame = new Frame();
914     frame.addNotify();
915     try {
916       Image image = frame.createImage(100, 100);
917       if (image == null) {
918         System.err.println("image is null");
919       }
920       Graphics graphics = image.getGraphics();
921       FontMetrics fm = graphics.getFontMetrics(font);
922       BoundingBox child = parent
923                             .getStringBounds(string,
924                                              BoundingBox.HORIZ_ALIGN_LEFT,
925                                              BoundingBox.VERT_ALIGN_TOP,
926                                              fm,
927                                              5);
928       System.out.println("Drawing Point: " +
929                          child.getDrawingPoint().toString());
930       System.out.println("Now testing subtract() method...");
931
932       parent = new BoundingBox(new Point(10, 10), new Dimension(300, 300));
933       System.out.println("parent: " + parent.toString());
934       child = new BoundingBox(new Point(90, 110), new Dimension(100, 100));
935       parent.add(child);
936       System.out.println("child: " + child.toString());
937       System.out.println();
938       System.out.println("subtracting the child from the parent");
939       System.out.println("SUBTRACT_FROM_TOP: ");
940       parent = parent.subtract(child, SUBTRACT_FROM_TOP);
941       System.out.println("new parent: " + parent.toString());
942       System.out.println();
943       System.out.println("Resetting parent");
944       parent = new BoundingBox(new Point(10, 10), new Dimension(300, 300));
945       parent.add(child);
946       System.out.println("SUBTRACT_FROM_BOTTOM");
947       parent.subtract(child, SUBTRACT_FROM_BOTTOM);
948       System.out.println("new parent: " + parent.toString());
949       System.out.println();
950       System.out.println("Resetting parent");
951       parent = new BoundingBox(new Point(10, 10), new Dimension(300, 300));
952       parent.add(child);
953       System.out.println("SUBTRACT_FROM_LEFT");
954       parent.subtract(child, SUBTRACT_FROM_LEFT);
955       System.out.println("new parent: " + parent.toString());
956       System.out.println();
957       System.out.println("Resetting parent");
958       parent = new BoundingBox(new Point(10, 10), new Dimension(300, 300));
959       parent.add(child);
960       System.out.println("SUBTRACT_FROM_RIGHT");
961       parent.subtract(child, SUBTRACT_FROM_RIGHT);
962       System.out.println("new parent: " + parent.toString());
963       System.out.println();
964
965
966
967       System.exit(0);
968     }
969     catch (Exception JavaDoc e) {
970       e.printStackTrace();
971       System.exit(1);
972     }
973   }
974
975
976   // Private methods /////////////////////////////
977

978   /**
979    * Creates a new <code>BoundingBox</code> instance.
980    *
981    * @param p a <code>Point</code> value
982    * @param d a <code>Dimension</code> value
983    * @param drawingPoint a <code>Point</code> value
984    */

985   private BoundingBox(Point p,
986                       Dimension d,
987                       Point drawingPoint,
988                       Point absolute) {
989     super(p, d);
990     this.drawingPoint = drawingPoint;
991     this.absoluteLocation = absolute;
992   }
993
994
995   /**
996    * <p>Checks the horizontal alignment passed into a
997    * method to make sure it is one of the valid values</p>
998    *
999    * @param hAlign an <code>int</code> value
1000   * @return a <code>boolean</code> value
1001   */

1002  private boolean checkHAlign(int hAlign) {
1003    int len = HORIZ_ALIGNS.length;
1004    for (int i = 0; i < len; i++) {
1005      if (hAlign == HORIZ_ALIGNS[i]) {
1006        return true;
1007      }
1008    }
1009    return false;
1010  }
1011
1012
1013
1014  /**
1015   * <p>Checks the vertical alignment passed into a
1016   * method to make sure it is one of the valid values</p>
1017   *
1018   * @param vAlign an <code>int</code> value
1019   * @return a <code>boolean</code> value
1020   */

1021  private boolean checkVAlign(int vAlign) {
1022    int len = VERT_ALIGNS.length;
1023    for (int i = 0; i < len; i++) {
1024      if (vAlign == VERT_ALIGNS[i]) {
1025        return true;
1026      }
1027    }
1028    return false;
1029  }
1030
1031} // end class BoundingBox
1032

1033
1034
1035
1036
Popular Tags