    001    /* ========================================================================
    002     * JCommon : a free general purpose class library for the Java(tm) platform
    003     * ========================================================================
    004     *
    005     * (C) Copyright 2000-2007, 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-2007, 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.13 2007/03/16 10:25:58 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     * 16-Mar-2007 : Fixed serialization for GradientPaint (DG);
    049     *
    050     */
    051
    052    package org.jfree.text;
    053
    054    import java.awt.Color;
    055    import java.awt.Font;
    056    import java.awt.FontMetrics;
    057    import java.awt.Graphics2D;
    058    import java.awt.Paint;
    059    import java.awt.font.LineMetrics;
    060    import java.awt.geom.Rectangle2D;
    061    import java.io.IOException;
    062    import java.io.ObjectInputStream;
    063    import java.io.ObjectOutputStream;
    064    import java.io.Serializable;
    065
    066    import org.jfree.io.SerialUtilities;
    067    import org.jfree.ui.Size2D;
    068    import org.jfree.ui.TextAnchor;
    069    import org.jfree.util.Log;
    070    import org.jfree.util.LogContext;
    071
    072    /**
    073     * A text item, with an associated font, that fits on a single line (see
    074     * {@link TextLine}).  Instances of the class are immutable.
    075     */
    076    public class TextFragment implements Serializable {
    077
    078        /** For serialization. */
    079        private static final long serialVersionUID = 4465945952903143262L;
    080
    081        /** The default font. */
    082        public static final Font DEFAULT_FONT = new Font("Serif", Font.PLAIN, 12);
    083
    084        /** The default text color. */
    085        public static final Paint DEFAULT_PAINT = Color.black;
    086
    087        /** The text. */
    088        private String text;
    089
    090        /** The font. */
    091        private Font font;
    092
    093        /** The text color. */
    094        private transient Paint paint;
    095
    096        /**
    097         * The baseline offset (can be used to simulate subscripts and
    098         * superscripts).
    099         */
    100        private float baselineOffset;
    101
    102        /** Access to logging facilities. */
    103        protected static final LogContext logger = Log.createContext(
    104                TextFragment.class);
    105
    106        /**
    107         * Creates a new text fragment.
    108         *
    109         * @param text  the text (<code>null</code> not permitted).
    110         */
    111        public TextFragment(final String text) {
    112            this(text, DEFAULT_FONT, DEFAULT_PAINT);
    113        }
    114
    115        /**
    116         * Creates a new text fragment.
    117         *
    118         * @param text  the text (<code>null</code> not permitted).
    119         * @param font  the font (<code>null</code> not permitted).
    120         */
    121        public TextFragment(final String text, final Font font) {
    122            this(text, font, DEFAULT_PAINT);
    123        }
    124
    125        /**
    126         * Creates a new text fragment.
    127         *
    128         * @param text  the text (<code>null</code> not permitted).
    129         * @param font  the font (<code>null</code> not permitted).
    130         * @param paint  the text color (<code>null</code> not permitted).
    131         */
    132        public TextFragment(final String text, final Font font, final Paint paint) {
    133            this(text, font, paint, 0.0f);
    134        }
    135
    136        /**
    137         * Creates a new text fragment.
    138         *
    139         * @param text  the text (<code>null</code> not permitted).
    140         * @param font  the font (<code>null</code> not permitted).
    141         * @param paint  the text color (<code>null</code> not permitted).
    142         * @param baselineOffset  the baseline offset.
    143         */
    144        public TextFragment(final String text, final Font font, final Paint paint,
    145                            final float baselineOffset) {
    146            if (text == null) {
    147                throw new IllegalArgumentException("Null 'text' argument.");
    148            }
    149            if (font == null) {
    150                throw new IllegalArgumentException("Null 'font' argument.");
    151            }
    152            if (paint == null) {
    153                throw new IllegalArgumentException("Null 'paint' argument.");
    154            }
    155            this.text = text;
    156            this.font = font;
    157            this.paint = paint;
    158            this.baselineOffset = baselineOffset;
    159        }
    160
    161        /**
    162         * Returns the text.
    163         *
    164         * @return The text (possibly <code>null</code>).
    165         */
    166        public String getText() {
    167            return this.text;
    168        }
    169
    170        /**
    171         * Returns the font.
    172         *
    173         * @return The font (never <code>null</code>).
    174         */
    175        public Font getFont() {
    176            return this.font;
    177        }
    178
    179        /**
    180         * Returns the text paint.
    181         *
    182         * @return The text paint (never <code>null</code>).
    183         */
    184        public Paint getPaint() {
    185            return this.paint;
    186        }
    187
    188        /**
    189         * Returns the baseline offset.
    190         *
    191         * @return The baseline offset.
    192         */
    193        public float getBaselineOffset() {
    194            return this.baselineOffset;
    195        }
    196
    197        /**
    198         * Draws the text fragment.
    199         *
    200         * @param g2  the graphics device.
    201         * @param anchorX  the x-coordinate of the anchor point.
    202         * @param anchorY  the y-coordinate of the anchor point.
    203         * @param anchor  the location of the text that is aligned to the anchor
    204         *                point.
    205         * @param rotateX  the x-coordinate of the rotation point.
    206         * @param rotateY  the y-coordinate of the rotation point.
    207         * @param angle  the angle.
    208         */
    209        public void draw(final Graphics2D g2, final float anchorX,
    210                         final float anchorY, final TextAnchor anchor,
    211                         final float rotateX, final float rotateY,
    212                         final double angle) {
    213
    214            g2.setFont(this.font);
    215            g2.setPaint(this.paint);
    216            TextUtilities.drawRotatedString(this.text, g2, anchorX, anchorY
    217                    + this.baselineOffset, anchor, angle, rotateX, rotateY);
    218
    219        }
    220
    221        /**
    222         * Calculates the dimensions of the text fragment.
    223         *
    224         * @param g2  the graphics device.
    225         *
    226         * @return The width and height of the text.
    227         */
    228        public Size2D calculateDimensions(final Graphics2D g2) {
    229            final FontMetrics fm = g2.getFontMetrics(this.font);
    230            final Rectangle2D bounds = TextUtilities.getTextBounds(this.text, g2,
    231                    fm);
    232            final Size2D result = new Size2D(bounds.getWidth(), bounds.getHeight());
    233            return result;
    234        }
    235
    236        /**
    237         * Calculates the vertical offset between the baseline and the specified
    238         * text anchor.
    239         *
    240         * @param g2  the graphics device.
    241         * @param anchor  the anchor.
    242         *
    243         * @return the offset.
    244         */
    245        public float calculateBaselineOffset(final Graphics2D g2,
    246                                             final TextAnchor anchor) {
    247            float result = 0.0f;
    248            final FontMetrics fm = g2.getFontMetrics(this.font);
    249            final LineMetrics lm = fm.getLineMetrics("ABCxyz", g2);
    250            if (anchor == TextAnchor.TOP_LEFT || anchor == TextAnchor.TOP_CENTER
    251                                              || anchor == TextAnchor.TOP_RIGHT) {
    252                result = lm.getAscent();
    253            }
    254            else if (anchor == TextAnchor.BOTTOM_LEFT
    255                    || anchor == TextAnchor.BOTTOM_CENTER
    256                    || anchor == TextAnchor.BOTTOM_RIGHT) {
    257                result = -lm.getDescent() - lm.getLeading();
    258            }
    259            return result;
    260        }
    261
    262        /**
    263         * Tests this instance for equality with an arbitrary object.
    264         *
    265         * @param obj  the object to test against (<code>null</code> permitted).
    266         *
    267         * @return A boolean.
    268         */
    269        public boolean equals(final Object obj) {
    270            if (obj == null) {
    271                return false;
    272            }
    273            if (obj == this) {
    274                return true;
    275            }
    276            if (obj instanceof TextFragment) {
    277                final TextFragment tf = (TextFragment) obj;
    278                if (!this.text.equals(tf.text)) {
    279                    return false;
    280                }
    281                if (!this.font.equals(tf.font)) {
    282                    return false;
    283                }
    284                if (!this.paint.equals(tf.paint)) {
    285                    return false;
    286                }
    287                return true;
    288            }
    289            return false;
    290        }
    291
    292        /**
    293         * Returns a hash code for this object.
    294         *
    295         * @return A hash code.
    296         */
    297        public int hashCode() {
    298            int result;
    299            result = (this.text != null ? this.text.hashCode() : 0);
    300            result = 29 * result + (this.font != null ? this.font.hashCode() : 0);
    301            result = 29 * result + (this.paint != null ? this.paint.hashCode() : 0);
    302            return result;
    303        }
    304
    305        /**
    306         * Provides serialization support.
    307         *
    308         * @param stream  the output stream.
    309         *
    310         * @throws IOException  if there is an I/O error.
    311         */
    312        private void writeObject(final ObjectOutputStream stream)
    313            throws IOException {
    314            stream.defaultWriteObject();
    315            SerialUtilities.writePaint(this.paint, stream);
    316        }
    317
    318        /**
    319         * Provides serialization support.
    320         *
    321         * @param stream  the input stream.
    322         *
    323         * @throws IOException  if there is an I/O error.
    324         * @throws ClassNotFoundException  if there is a classpath problem.
    325         */
    326        private void readObject(final ObjectInputStream stream)
    327            throws IOException, ClassNotFoundException {
    328            stream.defaultReadObject();
    329            this.paint = SerialUtilities.readPaint(stream);
    330        }
    331
    332    }