1 /*
2  * $Id: Rectangle.java 3742 2009-03-03 16:42:09Z blowagie $
3  *
4  * Copyright 1999, 2000, 2001, 2002 by Bruno Lowagie.
5  *
6  * The contents of this file are subject to the Mozilla Public License Version 1.1
7  * (the "License"); you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at http://www.mozilla.org/MPL/
9  *
10  * Software distributed under the License is distributed on an "AS IS" basis,
11  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12  * for the specific language governing rights and limitations under the License.
13  *
14  * The Original Code is 'iText, a free JAVA-PDF library'.
15  *
16  * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
17  * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
18  * All Rights Reserved.
19  * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
20  * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
21  *
22  * Contributor(s): all the names of the contributors are added in the source code
23  * where applicable.
24  *
25  * Alternatively, the contents of this file may be used under the terms of the
26  * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
27  * provisions of LGPL are applicable instead of those above.  If you wish to
28  * allow use of your version of this file only under the terms of the LGPL
29  * License and not to allow others to use your version of this file under
30  * the MPL, indicate your decision by deleting the provisions above and
31  * replace them with the notice and other provisions required by the LGPL.
32  * If you do not delete the provisions above, a recipient may use your version
33  * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
34  *
35  * This library is free software; you can redistribute it and/or modify it
36  * under the terms of the MPL as stated above or under the terms of the GNU
37  * Library General Public License as published by the Free Software Foundation;
38  * either version 2 of the License, or any later version.
39  *
40  * This library is distributed in the hope that it will be useful, but WITHOUT
41  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
42  * FOR A PARTICULAR PURPOSE. See the GNU Library general Public License for more
43  * details.
44  *
45  * If you didn't download this code from the following link, you should check if
46  * you aren't using an obsolete version:
47  * http://www.lowagie.com/iText/
48  */

49
50 package com.lowagie.text;
51
52 import java.awt.Color;
53 import java.util.ArrayList;
54
55 import com.lowagie.text.pdf.GrayColor;
56
57 /**
58  * A <CODE>Rectangle</CODE> is the representation of a geometric figure.
59  * 
60  * Rectangles support constant width borders using
61  * {@link #setBorderWidth(float)}and {@link #setBorder(int)}.
62  * They also support borders that vary in width/color on each side using
63  * methods like {@link #setBorderWidthLeft(float)}or
64  * {@link #setBorderColorLeft(java.awt.Color)}.
65  * 
66  * @see Element
67  * @see Table
68  * @see Cell
69  * @see HeaderFooter
70  */

71 public class Rectangle implements Element {
72
73     // CONSTANTS:
74
75     /** This is the value that will be used as <VAR>undefined </VAR>. */
76     public static final int UNDEFINED = -1;
77
78     /** This represents one side of the border of the <CODE>Rectangle</CODE>. */
79     public static final int TOP = 1;
80
81     /** This represents one side of the border of the <CODE>Rectangle</CODE>. */
82     public static final int BOTTOM = 2;
83
84     /** This represents one side of the border of the <CODE>Rectangle</CODE>. */
85     public static final int LEFT = 4;
86
87     /** This represents one side of the border of the <CODE>Rectangle</CODE>. */
88     public static final int RIGHT = 8;
89
90     /** This represents a rectangle without borders. */
91     public static final int NO_BORDER = 0;
92
93     /** This represents a type of border. */
94     public static final int BOX = TOP + BOTTOM + LEFT + RIGHT;
95
96     // MEMBER VARIABLES:
97
98     /** the lower left x-coordinate. */
99     protected float llx;
100
101     /** the lower left y-coordinate. */
102     protected float lly;
103
104     /** the upper right x-coordinate. */
105     protected float urx;
106
107     /** the upper right y-coordinate. */
108     protected float ury;
109
110     /** The rotation of the Rectangle */
111     protected int rotation = 0;
112
113     /** This is the color of the background of this rectangle. */
114     protected Color backgroundColor = null;
115
116     /** This represents the status of the 4 sides of the rectangle. */
117     protected int border = UNDEFINED;
118
119     /** Whether variable width/color borders are used. */
120     protected boolean useVariableBorders = false;
121
122     /** This is the width of the border around this rectangle. */
123     protected float borderWidth = UNDEFINED;
124     
125     /** The width of the left border of this rectangle. */
126     protected float borderWidthLeft = UNDEFINED;
127
128     /** The width of the right border of this rectangle. */
129     protected float borderWidthRight = UNDEFINED;
130
131     /** The width of the top border of this rectangle. */
132     protected float borderWidthTop = UNDEFINED;
133
134     /** The width of the bottom border of this rectangle. */
135     protected float borderWidthBottom = UNDEFINED;
136
137     /** The color of the border of this rectangle. */
138     protected Color borderColor = null;
139
140     /** The color of the left border of this rectangle. */
141     protected Color borderColorLeft = null;
142
143     /** The color of the right border of this rectangle. */
144     protected Color borderColorRight = null;
145
146     /** The color of the top border of this rectangle. */
147     protected Color borderColorTop = null;
148
149     /** The color of the bottom border of this rectangle. */
150     protected Color borderColorBottom = null;
151
152     // CONSTRUCTORS:
153
154     /**
155      * Constructs a <CODE>Rectangle</CODE> -object.
156      * 
157      * @param llx    lower left x
158      * @param lly    lower left y
159      * @param urx    upper right x
160      * @param ury    upper right y
161      */

162     public Rectangle(float llx, float lly, float urx, float ury) {
163         this.llx = llx;
164         this.lly = lly;
165         this.urx = urx;
166         this.ury = ury;
167     }
168
169     /**
170      * Constructs a <CODE>Rectangle</CODE> -object starting from the origin
171      * (0, 0).
172      * 
173      * @param urx    upper right x
174      * @param ury    upper right y
175      */

176     public Rectangle(float urx, float ury) {
177         this(0, 0, urx, ury);
178     }
179
180     /**
181      * Constructs a <CODE>Rectangle</CODE> -object.
182      * 
183      * @param rect    another <CODE>Rectangle</CODE>
184      */

185     public Rectangle(Rectangle rect) {
186         this(rect.llx, rect.lly, rect.urx, rect.ury);
187         cloneNonPositionParameters(rect);
188     }
189
190     // IMPLEMENTATION OF THE ELEMENT INTERFACE:e
191
192     /**
193      * Processes the element by adding it (or the different parts) to an
194      * <CODE>ElementListener</CODE>.
195      * 
196      * @param listener    an <CODE>ElementListener</CODE>
197      * @return <CODE>true</CODE> if the element was processed successfully
198      */

199     public boolean process(ElementListener listener) {
200         try {
201             return listener.add(this);
202         }
203         catch (DocumentException de) {
204             return false;
205         }
206     }
207
208     /**
209      * Gets the type of the text element.
210      * 
211      * @return a type
212      */

213     public int type() {
214         return Element.RECTANGLE;
215     }
216
217     /**
218      * Gets all the chunks in this element.
219      * 
220      * @return an <CODE>ArrayList</CODE>
221      */

222     public ArrayList getChunks() {
223         return new ArrayList();
224     }
225     
226     /**
227      * @see com.lowagie.text.Element#isContent()
228      * @since    iText 2.0.8
229      */

230     public boolean isContent() {
231         return true;
232     }
233
234     /**
235      * @see com.lowagie.text.Element#isNestable()
236      * @since    iText 2.0.8
237      */

238     public boolean isNestable() {
239         return false;
240     }
241
242     // METHODS TO GET/SET THE DIMENSIONS:
243
244     /**
245      * Sets the lower left x-coordinate.
246      * 
247      * @param llx    the new value
248      */

249     public void setLeft(float llx) {
250         this.llx = llx;
251     }
252
253     /**
254      * Returns the lower left x-coordinate.
255      * 
256      * @return the lower left x-coordinate
257      */

258     public float getLeft() {
259         return llx;
260     }
261
262     /**
263      * Returns the lower left x-coordinate, considering a given margin.
264      * 
265      * @param margin    a margin
266      * @return the lower left x-coordinate
267      */

268     public float getLeft(float margin) {
269         return llx + margin;
270     }
271
272     /**
273      * Sets the upper right x-coordinate.
274      * 
275      * @param urx    the new value
276      */

277     public void setRight(float urx) {
278         this.urx = urx;
279     }
280
281     /**
282      * Returns the upper right x-coordinate.
283      * 
284      * @return the upper right x-coordinate
285      */

286     public float getRight() {
287         return urx;
288     }
289
290     /**
291      * Returns the upper right x-coordinate, considering a given margin.
292      * 
293      * @param margin    a margin
294      * @return the upper right x-coordinate
295      */

296     public float getRight(float margin) {
297         return urx - margin;
298     }
299
300     /**
301      * Returns the width of the rectangle.
302      * 
303      * @return    the width
304      */

305     public float getWidth() {
306         return urx - llx;
307     }
308
309     /**
310      * Sets the upper right y-coordinate.
311      * 
312      * @param ury    the new value
313      */

314     public void setTop(float ury) {
315         this.ury = ury;
316     }
317
318     /**
319      * Returns the upper right y-coordinate.
320      * 
321      * @return the upper right y-coordinate
322      */

323     public float getTop() {
324         return ury;
325     }
326
327     /**
328      * Returns the upper right y-coordinate, considering a given margin.
329      * 
330      * @param margin    a margin
331      * @return the upper right y-coordinate
332      */

333     public float getTop(float margin) {
334         return ury - margin;
335     }
336
337     /**
338      * Sets the lower left y-coordinate.
339      * 
340      * @param lly    the new value
341      */

342     public void setBottom(float lly) {
343         this.lly = lly;
344     }
345
346     /**
347      * Returns the lower left y-coordinate.
348      * 
349      * @return the lower left y-coordinate
350      */

351     public float getBottom() {
352         return lly;
353     }
354
355     /**
356      * Returns the lower left y-coordinate, considering a given margin.
357      * 
358      * @param margin    a margin
359      * @return the lower left y-coordinate
360      */

361     public float getBottom(float margin) {
362         return lly + margin;
363     }
364
365     /**
366      * Returns the height of the rectangle.
367      * 
368      * @return the height
369      */

370     public float getHeight() {
371         return ury - lly;
372     }
373
374     /**
375      * Normalizes the rectangle.
376      * Switches lower left with upper right if necessary.
377      */

378     public void normalize() {
379         if (llx > urx) {
380             float a = llx;
381             llx = urx;
382             urx = a;
383         }
384         if (lly > ury) {
385             float a = lly;
386             lly = ury;
387             ury = a;
388         }
389     }
390
391     // METHODS TO GET/SET THE ROTATION:
392
393     /**
394      * Gets the rotation of the rectangle
395      * 
396      * @return a rotation value
397      */

398     public int getRotation() {
399         return rotation;
400     }
401
402     /**
403      * Rotates the rectangle.
404      * Swaps the values of llx and lly and of urx and ury.
405      * 
406      * @return the rotated <CODE>Rectangle</CODE>
407      */

408     public Rectangle rotate() {
409         Rectangle rect = new Rectangle(lly, llx, ury, urx);
410         rect.rotation = rotation + 90;
411         rect.rotation %= 360;
412         return rect;
413     }
414     
415     // METHODS TO GET/SET THE BACKGROUND COLOR:
416
417     /**
418      * Gets the backgroundcolor.
419      * 
420      * @return a <CODE>Color</CODE>
421      */

422     public Color getBackgroundColor() {
423         return backgroundColor;
424     }
425     
426     /**
427      * Sets the backgroundcolor of the rectangle.
428      * 
429      * @param backgroundColor    a <CODE>Color</CODE>
430      */

431
432     public void setBackgroundColor(Color backgroundColor) {
433         this.backgroundColor = backgroundColor;
434     }
435
436     /**
437      * Gets the grayscale.
438      * 
439      * @return the grayscale color of the background
440      * or 0 if the background has no grayscale color.
441      */

442     public float getGrayFill() {
443         if (backgroundColor instanceof GrayColor)
444             return ((GrayColor)backgroundColor).getGray();
445         return 0;
446     }
447
448     /**
449      * Sets the the background color to a grayscale value.
450      * 
451      * @param value    the new grayscale value
452      */

453     public void setGrayFill(float value) {
454         backgroundColor = new GrayColor(value);
455     }
456
457 //     METHODS TO GET/SET THE BORDER:
458     
459     /**
460      * Returns the exact type of the border.
461      * 
462      * @return a value
463      */

464     public int getBorder() {
465         return border;
466     }
467
468     /**
469      * Indicates whether some type of border is set.
470      * 
471      * @return a boolean
472      */

473     public boolean hasBorders() {
474         switch (border) {
475             case UNDEFINED:
476             case NO_BORDER:
477                 return false;
478             default:
479                 return borderWidth > 0 || borderWidthLeft > 0
480                         || borderWidthRight > 0 || borderWidthTop > 0 || borderWidthBottom > 0;
481         }
482     }
483
484     /**
485      * Indicates whether the specified type of border is set.
486      * 
487      * @param type    the type of border
488      * @return a boolean
489      */

490     public boolean hasBorder(int type) {
491         if (border == UNDEFINED)
492             return false;
493         return (border & type) == type;
494     }
495     
496     /**
497      * Enables/Disables the border on the specified sides.
498      * The border is specified as an integer bitwise combination of
499      * the constants: <CODE>LEFT, RIGHT, TOP, BOTTOM</CODE>.
500      * 
501      * @see #enableBorderSide(int)
502      * @see #disableBorderSide(int)
503      * @param border    the new value
504      */

505     public void setBorder(int border) {
506         this.border = border;
507     }
508     
509     /**
510      * Indicates whether variable width borders are being used.
511      * Returns true if <CODE>setBorderWidthLeft, setBorderWidthRight,
512      * setBorderWidthTop, or setBorderWidthBottom</CODE> has been called.
513      * 
514      * @return true if variable width borders are in use
515      */

516     public boolean isUseVariableBorders() {
517         return useVariableBorders;
518     }
519
520     /**
521      * Sets a parameter indicating if the rectangle has variable borders
522      * 
523      * @param useVariableBorders indication if the rectangle has variable borders
524      */

525     public void setUseVariableBorders(boolean useVariableBorders) {
526         this.useVariableBorders = useVariableBorders;
527     }
528
529     /**
530      * Enables the border on the specified side.
531      * 
532      * @param side    the side to enable.
533      * One of <CODE>LEFT, RIGHT, TOP, BOTTOM</CODE>
534      */

535     public void enableBorderSide(int side) {
536         if (border == UNDEFINED)
537             border = 0;
538         border |= side;
539     }
540
541     /**
542      * Disables the border on the specified side.
543      * 
544      * @param side    the side to disable.
545      * One of <CODE>LEFT, RIGHT, TOP, BOTTOM</CODE>
546      */

547     public void disableBorderSide(int side) {
548         if (border == UNDEFINED)
549             border = 0;
550         border &= ~side;
551     }
552
553     // METHODS TO GET/SET THE BORDER WIDTH:
554
555     /**
556      * Gets the borderwidth.
557      * 
558      * @return a value
559      */

560     public float getBorderWidth() {
561         return borderWidth;
562     }
563     
564     /**
565      * Sets the borderwidth of the table.
566      * 
567      * @param borderWidth the new value
568      */

569     public void setBorderWidth(float borderWidth) {
570         this.borderWidth = borderWidth;
571     }
572
573     /**
574      * Helper function returning the border width of a specific side.
575      * 
576      * @param    variableWidthValue    a variable width (could be undefined)
577      * @param    side    the border you want to check
578      * @return    the variableWidthValue if not undefined, otherwise the borderWidth
579      */

580     private float getVariableBorderWidth(float variableWidthValue, int side) {
581         if ((border & side) != 0)
582             return variableWidthValue != UNDEFINED ? variableWidthValue : borderWidth;
583         return 0;
584     }
585
586     /**
587      * Helper function updating the border flag for a side
588      * based on the specified width.
589      * A width of 0 will disable the border on that side.
590      * Any other width enables it.
591      * 
592      * @param width    width of border
593      * @param side    border side constant
594      */

595     private void updateBorderBasedOnWidth(float width, int side) {
596         useVariableBorders = true;
597         if (width > 0)
598             enableBorderSide(side);
599         else
600             disableBorderSide(side);
601     }
602
603     /**
604      * Gets the width of the left border.
605      * 
606      * @return a width
607      */

608     public float getBorderWidthLeft() {
609         return getVariableBorderWidth(borderWidthLeft, LEFT);
610     }
611
612     /**
613      * Sets the width of the left border.
614      * 
615      * @param borderWidthLeft a width
616      */

617     public void setBorderWidthLeft(float borderWidthLeft) {
618         this.borderWidthLeft = borderWidthLeft;
619         updateBorderBasedOnWidth(borderWidthLeft, LEFT);
620     }
621
622     /**
623      * Gets the width of the right border.
624      * 
625      * @return a width
626      */

627     public float getBorderWidthRight() {
628         return getVariableBorderWidth(borderWidthRight, RIGHT);
629     }
630
631     /**
632      * Sets the width of the right border.
633      * 
634      * @param borderWidthRight a width
635      */

636     public void setBorderWidthRight(float borderWidthRight) {
637         this.borderWidthRight = borderWidthRight;
638         updateBorderBasedOnWidth(borderWidthRight, RIGHT);
639     }
640
641     /**
642      * Gets the width of the top border.
643      * 
644      * @return a width
645      */

646     public float getBorderWidthTop() {
647         return getVariableBorderWidth(borderWidthTop, TOP);
648     }
649
650     /**
651      * Sets the width of the top border.
652      * 
653      * @param borderWidthTop a width
654      */

655     public void setBorderWidthTop(float borderWidthTop) {
656         this.borderWidthTop = borderWidthTop;
657         updateBorderBasedOnWidth(borderWidthTop, TOP);
658     }
659
660     /**
661      * Gets the width of the bottom border.
662      * 
663      * @return a width
664      */

665     public float getBorderWidthBottom() {
666         return getVariableBorderWidth(borderWidthBottom, BOTTOM);
667     }
668
669     /**
670      * Sets the width of the bottom border.
671      * 
672      * @param borderWidthBottom a width
673      */

674     public void setBorderWidthBottom(float borderWidthBottom) {
675         this.borderWidthBottom = borderWidthBottom;
676         updateBorderBasedOnWidth(borderWidthBottom, BOTTOM);
677     }
678
679     // METHODS TO GET/SET THE BORDER COLOR:
680     
681     /**
682      * Gets the color of the border.
683      * 
684      * @return    a <CODE>Color</CODE>
685      */

686     public Color getBorderColor() {
687         return borderColor;
688     }
689     
690     /**
691      * Sets the color of the border.
692      * 
693      * @param borderColor a <CODE>Color</CODE>
694      */

695     public void setBorderColor(Color borderColor) {
696         this.borderColor = borderColor;
697     }
698     
699     /**
700      * Gets the color of the left border.
701      * 
702      * @return a <CODE>Color</CODE>
703      */

704     public Color getBorderColorLeft() {
705         if (borderColorLeft == null)
706             return borderColor;
707         return borderColorLeft;
708     }
709
710     /**
711      * Sets the color of the left border.
712      * 
713      * @param borderColorLeft a <CODE>Color</CODE>
714      */

715     public void setBorderColorLeft(Color borderColorLeft) {
716         this.borderColorLeft = borderColorLeft;
717     }
718
719     /**
720      * Gets the color of the right border.
721      * 
722      * @return a <CODE>Color</CODE>
723      */

724     public Color getBorderColorRight() {
725         if (borderColorRight == null)
726             return borderColor;
727         return borderColorRight;
728     }
729
730     /**
731      * Sets the color of the right border.
732      * 
733      * @param borderColorRight a <CODE>Color</CODE>
734      */

735     public void setBorderColorRight(Color borderColorRight) {
736         this.borderColorRight = borderColorRight;
737     }
738
739     /**
740      * Gets the color of the top border.
741      * 
742      * @return a <CODE>Color</CODE>
743      */

744     public Color getBorderColorTop() {
745         if (borderColorTop == null)
746             return borderColor;
747         return borderColorTop;
748     }
749
750     /**
751      * Sets the color of the top border.
752      * 
753      * @param borderColorTop a <CODE>Color</CODE>
754      */

755     public void setBorderColorTop(Color borderColorTop) {
756         this.borderColorTop = borderColorTop;
757     }
758
759     /**
760      * Gets the color of the bottom border.
761      * 
762      * @return a <CODE>Color</CODE>
763      */

764     public Color getBorderColorBottom() {
765         if (borderColorBottom == null)
766             return borderColor;
767         return borderColorBottom;
768     }
769
770     /**
771      * Sets the color of the bottom border.
772      * 
773      * @param borderColorBottom a <CODE>Color</CODE>
774      */

775     public void setBorderColorBottom(Color borderColorBottom) {
776         this.borderColorBottom = borderColorBottom;
777     }
778
779     // SPECIAL METHODS:
780
781     /**
782      * Gets a Rectangle that is altered to fit on the page.
783      * 
784      * @param top        the top position
785      * @param bottom    the bottom position
786      * @return a <CODE>Rectangle</CODE>
787      */

788     public Rectangle rectangle(float top, float bottom) {
789         Rectangle tmp = new Rectangle(this);
790         if (getTop() > top) {
791             tmp.setTop(top);
792             tmp.disableBorderSide(TOP);
793         }
794         if (getBottom() < bottom) {
795             tmp.setBottom(bottom);
796             tmp.disableBorderSide(BOTTOM);
797         }
798         return tmp;
799     }
800
801     /**
802      * Copies each of the parameters, except the position, from a
803      * <CODE>Rectangle</CODE> object
804      * 
805      * @param rect    <CODE>Rectangle</CODE> to copy from
806      */

807     public void cloneNonPositionParameters(Rectangle rect) {
808         this.rotation = rect.rotation;
809         this.backgroundColor = rect.backgroundColor;
810         this.border = rect.border;
811         this.useVariableBorders = rect.useVariableBorders;
812         this.borderWidth = rect.borderWidth;
813         this.borderWidthLeft = rect.borderWidthLeft;
814         this.borderWidthRight = rect.borderWidthRight;
815         this.borderWidthTop = rect.borderWidthTop;
816         this.borderWidthBottom = rect.borderWidthBottom;
817         this.borderColor = rect.borderColor;
818         this.borderColorLeft = rect.borderColorLeft;
819         this.borderColorRight = rect.borderColorRight;
820         this.borderColorTop = rect.borderColorTop;
821         this.borderColorBottom = rect.borderColorBottom;
822     }
823
824     /**
825      * Copies each of the parameters, except the position, from a
826      * <CODE>Rectangle</CODE> object if the value is set there
827      * 
828      * @param rect <CODE>Rectangle</CODE> to copy from
829      */

830     public void softCloneNonPositionParameters(Rectangle rect) {
831         if (rect.rotation != 0)
832             this.rotation = rect.rotation;
833         if (rect.backgroundColor != null)
834             this.backgroundColor = rect.backgroundColor;
835         if (rect.border != UNDEFINED)
836             this.border = rect.border;
837         if (useVariableBorders)
838             this.useVariableBorders = rect.useVariableBorders;
839         if (rect.borderWidth != UNDEFINED)
840             this.borderWidth = rect.borderWidth;
841         if (rect.borderWidthLeft != UNDEFINED)
842             this.borderWidthLeft = rect.borderWidthLeft;
843         if (rect.borderWidthRight != UNDEFINED)
844             this.borderWidthRight = rect.borderWidthRight;
845         if (rect.borderWidthTop != UNDEFINED)
846             this.borderWidthTop = rect.borderWidthTop;
847         if (rect.borderWidthBottom != UNDEFINED)
848             this.borderWidthBottom = rect.borderWidthBottom;
849         if (rect.borderColor != null)
850             this.borderColor = rect.borderColor;
851         if (rect.borderColorLeft != null)
852             this.borderColorLeft = rect.borderColorLeft;
853         if (rect.borderColorRight != null)
854             this.borderColorRight = rect.borderColorRight;
855         if (rect.borderColorTop != null)
856             this.borderColorTop = rect.borderColorTop;
857         if (rect.borderColorBottom != null)
858             this.borderColorBottom = rect.borderColorBottom;
859     }
860     
861     /**
862      * @return    a String representation of the rectangle
863      * @see java.lang.Object#toString()
864      */

865     public String toString() {
866         StringBuffer buf = new StringBuffer("Rectangle: ");
867         buf.append(getWidth());
868         buf.append('x');
869         buf.append(getHeight());
870         buf.append(" (rot: ");
871         buf.append(rotation);
872         buf.append(" degrees)");
873         return buf.toString();
874     }
875
876 }