    001    /* ========================================================================
    002     * JCommon : a free general purpose class library for the Java(tm) platform
    003     * ========================================================================
    004     *
    005     * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors.
    006     *
    007     * Project Info:  http://www.jfree.org/jcommon/index.html
    008     *
    009     * This library is free software; you can redistribute it and/or modify it
    010     * under the terms of the GNU Lesser General Public License as published by
    011     * the Free Software Foundation; either version 2.1 of the License, or
    012     * (at your option) any later version.
    013     *
    014     * This library is distributed in the hope that it will be useful, but
    015     * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
    016     * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
    017     * License for more details.
    018     *
    019     * You should have received a copy of the GNU Lesser General Public
    020     * License along with this library; if not, write to the Free Software
    021     * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
    022     * USA.
    023     *
    024     * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
    025     * in the United States and other countries.]
    026     *
    027     * -----------------
    028     * TextFragment.java
    029     * -----------------
    030     * (C) Copyright 2003-2005, by Object Refinery Limited and Contributors.
    031     *
    032     * Original Author:  David Gilbert (for Object Refinery Limited);
    033     * Contributor(s):   -;
    034     *
    035     * $Id: TextFragment.java,v 1.12 2005/10/18 13:17:16 mungady Exp $
    036     *
    037     * Changes
    038     * -------
    039     * 07-Nov-2003 : Version 1 (DG);
    040     * 25-Nov-2003 : Fixed bug in the dimension calculation (DG);
    041     * 22-Dec-2003 : Added workaround for Java bug 4245442 (DG);
    042     * 29-Jan-2004 : Added paint attribute (DG);
    043     * 22-Mar-2004 : Added equals() method and implemented Serializable (DG);
    044     * 01-Apr-2004 : Changed java.awt.geom.Dimension2D to org.jfree.ui.Size2D
    045     *               because of JDK bug 4976448 which persists on JDK 1.3.1 (DG);
    046     * 30-Sep-2004 : Moved drawRotatedString() from RefineryUtilities
    047     *               --> TextUtilities (DG);
    048     *
    049     */
    050
    051    package org.jfree.text;
    052
    053    import java.awt.Color;
    054    import java.awt.Font;
    055    import java.awt.FontMetrics;
    056    import java.awt.Graphics2D;
    057    import java.awt.Paint;
    058    import java.awt.font.LineMetrics;
    059    import java.awt.geom.Rectangle2D;
    060    import java.io.IOException;
    061    import java.io.ObjectInputStream;
    062    import java.io.ObjectOutputStream;
    063    import java.io.Serializable;
    064
    065    import org.jfree.io.SerialUtilities;
    066    import org.jfree.ui.Size2D;
    067    import org.jfree.ui.TextAnchor;
    068    import org.jfree.util.Log;
    069    import org.jfree.util.LogContext;
    070
    071    /**
    072     * A text item, with an associated font, that fits on a single line (see
    073     * {@link TextLine}).  Instances of the class are immutable.
    074     *
    075     * @author David Gilbert
    076     */
    077    public class TextFragment implements Serializable {
    078
    079        /** For serialization. */
    080        private static final long serialVersionUID = 4465945952903143262L;
    081
    082        /** The default font. */
    083        public static final Font DEFAULT_FONT = new Font("Serif", Font.PLAIN, 12);
    084
    085        /** The default text color. */
    086        public static final Paint DEFAULT_PAINT = Color.black;
    087
    088        /** The text. */
    089        private String text;
    090
    091        /** The font. */
    092        private Font font;
    093
    094        /** The text color. */
    095        private Paint paint;
    096
    097        /**
    098         * The baseline offset (can be used to simulate subscripts and
    099         * superscripts).
    100         */
    101        private float baselineOffset;
    102
    103        /** Access to logging facilities. */
    104        protected static final LogContext logger
    105            = Log.createContext(TextFragment.class);
    106
    107        /**
    108         * Creates a new text fragment.
    109         *
    110         * @param text  the text (<code>null</code> not permitted).
    111         */
    112        public TextFragment(final String text) {
    113            this(text, DEFAULT_FONT, DEFAULT_PAINT);
    114        }
    115
    116        /**
    117         * Creates a new text fragment.
    118         *
    119         * @param text  the text (<code>null</code> not permitted).
    120         * @param font  the font (<code>null</code> not permitted).
    121         */
    122        public TextFragment(final String text, final Font font) {
    123            this(text, font, DEFAULT_PAINT);
    124        }
    125
    126        /**
    127         * Creates a new text fragment.
    128         *
    129         * @param text  the text (<code>null</code> not permitted).
    130         * @param font  the font (<code>null</code> not permitted).
    131         * @param paint  the text color (<code>null</code> not permitted).
    132         */
    133        public TextFragment(final String text, final Font font, final Paint paint) {
    134            this(text, font, paint, 0.0f);
    135        }
    136
    137        /**
    138         * Creates a new text fragment.
    139         *
    140         * @param text  the text (<code>null</code> not permitted).
    141         * @param font  the font (<code>null</code> not permitted).
    142         * @param paint  the text color (<code>null</code> not permitted).
    143         * @param baselineOffset  the baseline offset.
    144         */
    145        public TextFragment(final String text, final Font font, final Paint paint,
    146                            final float baselineOffset) {
    147            if (text == null) {
    148                throw new IllegalArgumentException("Null 'text' argument.");
    149            }
    150            if (font == null) {
    151                throw new IllegalArgumentException("Null 'font' argument.");
    152            }
    153            if (paint == null) {
    154                throw new IllegalArgumentException("Null 'paint' argument.");
    155            }
    156            this.text = text;
    157            this.font = font;
    158            this.paint = paint;
    159            this.baselineOffset = baselineOffset;
    160        }
    161
    162        /**
    163         * Returns the text.
    164         *
    165         * @return The text (possibly <code>null</code>).
    166         */
    167        public String getText() {
    168            return this.text;
    169        }
    170
    171        /**
    172         * Returns the font.
    173         *
    174         * @return The font (never <code>null</code>).
    175         */
    176        public Font getFont() {
    177            return this.font;
    178        }
    179
    180        /**
    181         * Returns the text paint.
    182         *
    183         * @return The text paint (never <code>null</code>).
    184         */
    185        public Paint getPaint() {
    186            return this.paint;
    187        }
    188
    189        public float getBaselineOffset() {
    190            return this.baselineOffset;
    191        }
    192
    193        /**
    194         * Draws the text fragment.
    195         *
    196         * @param g2  the graphics device.
    197         * @param anchorX  the x-coordinate of the anchor point.
    198         * @param anchorY  the y-coordinate of the anchor point.
    199         * @param anchor  the location of the text that is aligned to the anchor
    200         *                point.
    201         * @param rotateX  the x-coordinate of the rotation point.
    202         * @param rotateY  the y-coordinate of the rotation point.
    203         * @param angle  the angle.
    204         */
    205        public void draw(final Graphics2D g2, final float anchorX,
    206                         final float anchorY, final TextAnchor anchor,
    207                         final float rotateX, final float rotateY,
    208                         final double angle) {
    209
    210            g2.setFont(this.font);
    211            g2.setPaint(this.paint);
    212            TextUtilities.drawRotatedString(
    213                this.text, g2, anchorX, anchorY + this.baselineOffset, anchor,
    214                angle, rotateX, rotateY
    215            );
    216
    217        }
    218
    219        /**
    220         * Calculates the dimensions of the text fragment.
    221         *
    222         * @param g2  the graphics device.
    223         *
    224         * @return The width and height of the text.
    225         */
    226        public Size2D calculateDimensions(final Graphics2D g2) {
    227            final FontMetrics fm = g2.getFontMetrics(this.font);
    228            final Rectangle2D bounds = TextUtilities.getTextBounds(this.text, g2,
    229                    fm);
    230            final Size2D result = new Size2D(bounds.getWidth(), bounds.getHeight());
    231            return result;
    232        }
    233
    234        /**
    235         * Calculates the vertical offset between the baseline and the specified
    236         * text anchor.
    237         *
    238         * @param g2  the graphics device.
    239         * @param anchor  the anchor.
    240         *
    241         * @return the offset.
    242         */
    243        public float calculateBaselineOffset(final Graphics2D g2,
    244                                             final TextAnchor anchor) {
    245            float result = 0.0f;
    246            final FontMetrics fm = g2.getFontMetrics(this.font);
    247            final LineMetrics lm = fm.getLineMetrics("ABCxyz", g2);
    248            if (anchor == TextAnchor.TOP_LEFT || anchor == TextAnchor.TOP_CENTER
    249                                              || anchor == TextAnchor.TOP_RIGHT) {
    250                result = lm.getAscent();
    251            }
    252            else if (anchor == TextAnchor.BOTTOM_LEFT
    253                    || anchor == TextAnchor.BOTTOM_CENTER
    254                    || anchor == TextAnchor.BOTTOM_RIGHT) {
    255                result = -lm.getDescent() - lm.getLeading();
    256            }
    257            return result;
    258        }
    259
    260        /**
    261         * Tests this instance for equality with an arbitrary object.
    262         *
    263         * @param obj  the object to test against (<code>null</code> permitted).
    264         *
    265         * @return A boolean.
    266         */
    267        public boolean equals(final Object obj) {
    268            if (obj == null) {
    269                return false;
    270            }
    271            if (obj == this) {
    272                return true;
    273            }
    274            if (obj instanceof TextFragment) {
    275                final TextFragment tf = (TextFragment) obj;
    276                if (!this.text.equals(tf.text)) {
    277                    return false;
    278                }
    279                if (!this.font.equals(tf.font)) {
    280                    return false;
    281                }
    282                if (!this.paint.equals(tf.paint)) {
    283                    return false;
    284                }
    285                return true;
    286            }
    287            return false;
    288        }
    289
    290        /**
    291         * Returns a hash code for this object.
    292         *
    293         * @return A hash code.
    294         */
    295        public int hashCode() {
    296            int result;
    297            result = (this.text != null ? this.text.hashCode() : 0);
    298            result = 29 * result + (this.font != null ? this.font.hashCode() : 0);
    299            result = 29 * result + (this.paint != null ? this.paint.hashCode() : 0);
    300            return result;
    301        }
    302
    303        /**
    304         * Provides serialization support.
    305         *
    306         * @param stream  the output stream.
    307         *
    308         * @throws IOException  if there is an I/O error.
    309         */
    310        private void writeObject(final ObjectOutputStream stream)
    311            throws IOException {
    312            stream.defaultWriteObject();
    313            SerialUtilities.writePaint(this.paint, stream);
    314        }
    315
    316        /**
    317         * Provides serialization support.
    318         *
    319         * @param stream  the input stream.
    320         *
    321         * @throws IOException  if there is an I/O error.
    322         * @throws ClassNotFoundException  if there is a classpath problem.
    323         */
    324        private void readObject(final ObjectInputStream stream)
    325            throws IOException, ClassNotFoundException {
    326            stream.defaultReadObject();
    327            this.paint = SerialUtilities.readPaint(stream);
    328        }
    329
    330    }