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 }