001/* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2020, by Object Refinery Limited and Contributors. 006 * 007 * Project Info: http://www.jfree.org/jfreechart/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 * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. 025 * Other names may be trademarks of their respective owners.] 026 * 027 */ 028 029package org.jfree.chart.text; 030 031import java.awt.Color; 032import java.awt.Font; 033import java.awt.FontMetrics; 034import java.awt.Graphics2D; 035import java.awt.Paint; 036import java.awt.font.LineMetrics; 037import java.awt.geom.Rectangle2D; 038import java.io.IOException; 039import java.io.ObjectInputStream; 040import java.io.ObjectOutputStream; 041import java.io.Serializable; 042import org.jfree.chart.ui.Size2D; 043import org.jfree.chart.ui.TextAnchor; 044import org.jfree.chart.util.SerialUtils; 045 046/** 047 * A text item, with an associated font, that fits on a single line (see 048 * {@link TextLine}). Instances of the class are immutable. 049 */ 050public class TextFragment implements Serializable { 051 052 /** For serialization. */ 053 private static final long serialVersionUID = 4465945952903143262L; 054 055 /** The default font. */ 056 public static final Font DEFAULT_FONT = new Font("Serif", Font.PLAIN, 12); 057 058 /** The default text color. */ 059 public static final Paint DEFAULT_PAINT = Color.BLACK; 060 061 /** The text. */ 062 private String text; 063 064 /** The font. */ 065 private Font font; 066 067 /** The text color. */ 068 private transient Paint paint; 069 070 /** 071 * The baseline offset (can be used to simulate subscripts and 072 * superscripts). 073 */ 074 private float baselineOffset; 075 076 /** 077 * Creates a new text fragment. 078 * 079 * @param text the text ({@code null} not permitted). 080 */ 081 public TextFragment(String text) { 082 this(text, DEFAULT_FONT, DEFAULT_PAINT); 083 } 084 085 /** 086 * Creates a new text fragment. 087 * 088 * @param text the text ({@code null} not permitted). 089 * @param font the font ({@code null} not permitted). 090 */ 091 public TextFragment(String text, Font font) { 092 this(text, font, DEFAULT_PAINT); 093 } 094 095 /** 096 * Creates a new text fragment. 097 * 098 * @param text the text ({@code null} not permitted). 099 * @param font the font ({@code null} not permitted). 100 * @param paint the text color ({@code null} not permitted). 101 */ 102 public TextFragment(String text, Font font, Paint paint) { 103 this(text, font, paint, 0.0f); 104 } 105 106 /** 107 * Creates a new text fragment. 108 * 109 * @param text the text ({@code null} not permitted). 110 * @param font the font ({@code null} not permitted). 111 * @param paint the text color ({@code null} not permitted). 112 * @param baselineOffset the baseline offset. 113 */ 114 public TextFragment(String text, Font font, Paint paint, 115 float baselineOffset) { 116 if (text == null) { 117 throw new IllegalArgumentException("Null 'text' argument."); 118 } 119 if (font == null) { 120 throw new IllegalArgumentException("Null 'font' argument."); 121 } 122 if (paint == null) { 123 throw new IllegalArgumentException("Null 'paint' argument."); 124 } 125 this.text = text; 126 this.font = font; 127 this.paint = paint; 128 this.baselineOffset = baselineOffset; 129 } 130 131 /** 132 * Returns the text. 133 * 134 * @return The text (possibly {@code null}). 135 */ 136 public String getText() { 137 return this.text; 138 } 139 140 /** 141 * Returns the font. 142 * 143 * @return The font (never {@code null}). 144 */ 145 public Font getFont() { 146 return this.font; 147 } 148 149 /** 150 * Returns the text paint. 151 * 152 * @return The text paint (never {@code null}). 153 */ 154 public Paint getPaint() { 155 return this.paint; 156 } 157 158 /** 159 * Returns the baseline offset. 160 * 161 * @return The baseline offset. 162 */ 163 public float getBaselineOffset() { 164 return this.baselineOffset; 165 } 166 167 /** 168 * Draws the text fragment. 169 * 170 * @param g2 the graphics device. 171 * @param anchorX the x-coordinate of the anchor point. 172 * @param anchorY the y-coordinate of the anchor point. 173 * @param anchor the location of the text that is aligned to the anchor 174 * point. 175 * @param rotateX the x-coordinate of the rotation point. 176 * @param rotateY the y-coordinate of the rotation point. 177 * @param angle the angle. 178 */ 179 public void draw(Graphics2D g2, float anchorX, float anchorY, 180 TextAnchor anchor, float rotateX, float rotateY, double angle) { 181 g2.setFont(this.font); 182 g2.setPaint(this.paint); 183 TextUtils.drawRotatedString(this.text, g2, anchorX, anchorY 184 + this.baselineOffset, anchor, angle, rotateX, rotateY); 185 } 186 187 /** 188 * Calculates the dimensions of the text fragment. 189 * 190 * @param g2 the graphics device. 191 * 192 * @return The width and height of the text. 193 */ 194 public Size2D calculateDimensions(Graphics2D g2) { 195 FontMetrics fm = g2.getFontMetrics(this.font); 196 Rectangle2D bounds = TextUtils.getTextBounds(this.text, g2, fm); 197 Size2D result = new Size2D(bounds.getWidth(), bounds.getHeight()); 198 return result; 199 } 200 201 /** 202 * Calculates the vertical offset between the baseline and the specified 203 * text anchor. 204 * 205 * @param g2 the graphics device. 206 * @param anchor the anchor. 207 * 208 * @return the offset. 209 */ 210 public float calculateBaselineOffset(Graphics2D g2, TextAnchor anchor) { 211 float result = 0.0f; 212 FontMetrics fm = g2.getFontMetrics(this.font); 213 LineMetrics lm = fm.getLineMetrics("ABCxyz", g2); 214 if (anchor.isTop()) { 215 result = lm.getAscent(); 216 } 217 else if (anchor.isHalfAscent()) { 218 result = lm.getAscent() / 2.0f; 219 } 220 else if (anchor.isVerticalCenter()) { 221 result = lm.getAscent() / 2.0f - lm.getDescent() / 2.0f; 222 } 223 else if (anchor.isBottom()) { 224 result = -lm.getDescent() - lm.getLeading(); 225 } 226 return result; 227 } 228 229 /** 230 * Tests this instance for equality with an arbitrary object. 231 * 232 * @param obj the object to test against ({@code null} permitted). 233 * 234 * @return A boolean. 235 */ 236 @Override 237 public boolean equals(Object obj) { 238 if (obj == null) { 239 return false; 240 } 241 if (obj == this) { 242 return true; 243 } 244 if (obj instanceof TextFragment) { 245 TextFragment tf = (TextFragment) obj; 246 if (!this.text.equals(tf.text)) { 247 return false; 248 } 249 if (!this.font.equals(tf.font)) { 250 return false; 251 } 252 if (!this.paint.equals(tf.paint)) { 253 return false; 254 } 255 return true; 256 } 257 return false; 258 } 259 260 /** 261 * Returns a hash code for this object. 262 * 263 * @return A hash code. 264 */ 265 @Override 266 public int hashCode() { 267 int result; 268 result = (this.text != null ? this.text.hashCode() : 0); 269 result = 29 * result + (this.font != null ? this.font.hashCode() : 0); 270 result = 29 * result + (this.paint != null ? this.paint.hashCode() : 0); 271 return result; 272 } 273 274 /** 275 * Provides serialization support. 276 * 277 * @param stream the output stream. 278 * 279 * @throws IOException if there is an I/O error. 280 */ 281 private void writeObject(ObjectOutputStream stream) throws IOException { 282 stream.defaultWriteObject(); 283 SerialUtils.writePaint(this.paint, stream); 284 } 285 286 /** 287 * Provides serialization support. 288 * 289 * @param stream the input stream. 290 * 291 * @throws IOException if there is an I/O error. 292 * @throws ClassNotFoundException if there is a classpath problem. 293 */ 294 private void readObject(ObjectInputStream stream) throws IOException, 295 ClassNotFoundException { 296 stream.defaultReadObject(); 297 this.paint = SerialUtils.readPaint(stream); 298 } 299 300} 301