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 * TextLine.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: TextLine.java,v 1.13 2007/11/02 17:50:35 taqua Exp $ 036 * 037 * Changes 038 * ------- 039 * 07-Nov-2003 : Version 1 (DG); 040 * 22-Dec-2003 : Added workaround for Java bug 4245442 (DG); 041 * 29-Jan-2004 : Added new constructor (DG); 042 * 22-Mar-2004 : Added equals() method and implemented Serializable (DG); 043 * 01-Apr-2004 : Changed java.awt.geom.Dimension2D to org.jfree.ui.Size2D 044 * because of JDK bug 4976448 which persists on JDK 1.3.1 (DG); 045 * 03-Sep-2004 : Added a method to remove a fragment (DG); 046 * 08-Jul-2005 : Fixed bug in calculateBaselineOffset() (DG); 047 * 048 */ 049 050 package org.jfree.text; 051 052 import java.awt.Font; 053 import java.awt.Graphics2D; 054 import java.awt.Paint; 055 import java.io.Serializable; 056 import java.util.Iterator; 057 import java.util.List; 058 059 import org.jfree.ui.Size2D; 060 import org.jfree.ui.TextAnchor; 061 062 /** 063 * A sequence of {@link TextFragment} objects that together form a line of 064 * text. A sequence of text lines is managed by the {@link TextBlock} class. 065 * 066 * @author David Gilbert 067 */ 068 public class TextLine implements Serializable { 069 070 /** For serialization. */ 071 private static final long serialVersionUID = 7100085690160465444L; 072 073 /** Storage for the text fragments that make up the line. */ 074 private List fragments; 075 076 /** 077 * Creates a new empty line. 078 */ 079 public TextLine() { 080 this.fragments = new java.util.ArrayList(); 081 } 082 083 /** 084 * Creates a new text line using the default font. 085 * 086 * @param text the text (<code>null</code> not permitted). 087 */ 088 public TextLine(final String text) { 089 this(text, TextFragment.DEFAULT_FONT); 090 } 091 092 /** 093 * Creates a new text line. 094 * 095 * @param text the text (<code>null</code> not permitted). 096 * @param font the text font (<code>null</code> not permitted). 097 */ 098 public TextLine(final String text, final Font font) { 099 this.fragments = new java.util.ArrayList(); 100 final TextFragment fragment = new TextFragment(text, font); 101 this.fragments.add(fragment); 102 } 103 104 /** 105 * Creates a new text line. 106 * 107 * @param text the text (<code>null</code> not permitted). 108 * @param font the text font (<code>null</code> not permitted). 109 * @param paint the text color (<code>null</code> not permitted). 110 */ 111 public TextLine(final String text, final Font font, final Paint paint) { 112 if (text == null) { 113 throw new IllegalArgumentException("Null 'text' argument."); 114 } 115 if (font == null) { 116 throw new IllegalArgumentException("Null 'font' argument."); 117 } 118 if (paint == null) { 119 throw new IllegalArgumentException("Null 'paint' argument."); 120 } 121 this.fragments = new java.util.ArrayList(); 122 final TextFragment fragment = new TextFragment(text, font, paint); 123 this.fragments.add(fragment); 124 } 125 126 /** 127 * Adds a text fragment to the text line. 128 * 129 * @param fragment the text fragment (<code>null</code> not permitted). 130 */ 131 public void addFragment(final TextFragment fragment) { 132 this.fragments.add(fragment); 133 } 134 135 /** 136 * Removes a fragment from the line. 137 * 138 * @param fragment the fragment to remove. 139 */ 140 public void removeFragment(final TextFragment fragment) { 141 this.fragments.remove(fragment); 142 } 143 144 /** 145 * Draws the text line. 146 * 147 * @param g2 the graphics device. 148 * @param anchorX the x-coordinate for the anchor point. 149 * @param anchorY the y-coordinate for the anchor point. 150 * @param anchor the point on the text line that is aligned to the anchor 151 * point. 152 * @param rotateX the x-coordinate for the rotation point. 153 * @param rotateY the y-coordinate for the rotation point. 154 * @param angle the rotation angle (in radians). 155 */ 156 public void draw(final Graphics2D g2, 157 final float anchorX, final float anchorY, 158 final TextAnchor anchor, 159 final float rotateX, final float rotateY, 160 final double angle) { 161 162 float x = anchorX; 163 final float yOffset = calculateBaselineOffset(g2, anchor); 164 final Iterator iterator = this.fragments.iterator(); 165 while (iterator.hasNext()) { 166 final TextFragment fragment = (TextFragment) iterator.next(); 167 final Size2D d = fragment.calculateDimensions(g2); 168 fragment.draw( 169 g2, x, anchorY + yOffset, TextAnchor.BASELINE_LEFT, 170 rotateX, rotateY, angle 171 ); 172 x = x + (float) d.getWidth(); 173 } 174 175 } 176 177 /** 178 * Calculates the width and height of the text line. 179 * 180 * @param g2 the graphics device. 181 * 182 * @return The width and height. 183 */ 184 public Size2D calculateDimensions(final Graphics2D g2) { 185 double width = 0.0; 186 double height = 0.0; 187 final Iterator iterator = this.fragments.iterator(); 188 while (iterator.hasNext()) { 189 final TextFragment fragment = (TextFragment) iterator.next(); 190 final Size2D dimension = fragment.calculateDimensions(g2); 191 width = width + dimension.getWidth(); 192 height = Math.max(height, dimension.getHeight()); 193 } 194 return new Size2D(width, height); 195 } 196 197 /** 198 * Returns the first text fragment in the line. 199 * 200 * @return The first text fragment in the line. 201 */ 202 public TextFragment getFirstTextFragment() { 203 TextFragment result = null; 204 if (this.fragments.size() > 0) { 205 result = (TextFragment) this.fragments.get(0); 206 } 207 return result; 208 } 209 210 /** 211 * Returns the last text fragment in the line. 212 * 213 * @return The last text fragment in the line. 214 */ 215 public TextFragment getLastTextFragment() { 216 TextFragment result = null; 217 if (this.fragments.size() > 0) { 218 result = (TextFragment) this.fragments.get(this.fragments.size() 219 - 1); 220 } 221 return result; 222 } 223 224 /** 225 * Calculate the offsets required to translate from the specified anchor 226 * position to the left baseline position. 227 * 228 * @param g2 the graphics device. 229 * @param anchor the anchor position. 230 * 231 * @return The offsets. 232 */ 233 private float calculateBaselineOffset(final Graphics2D g2, 234 final TextAnchor anchor) { 235 float result = 0.0f; 236 Iterator iterator = this.fragments.iterator(); 237 while (iterator.hasNext()) { 238 TextFragment fragment = (TextFragment) iterator.next(); 239 result = Math.max(result, 240 fragment.calculateBaselineOffset(g2, anchor)); 241 } 242 return result; 243 } 244 245 /** 246 * Tests this object for equality with an arbitrary object. 247 * 248 * @param obj the object to test against (<code>null</code> permitted). 249 * 250 * @return A boolean. 251 */ 252 public boolean equals(final Object obj) { 253 if (obj == null) { 254 return false; 255 } 256 if (obj == this) { 257 return true; 258 } 259 if (obj instanceof TextLine) { 260 final TextLine line = (TextLine) obj; 261 return this.fragments.equals(line.fragments); 262 } 263 return false; 264 } 265 266 /** 267 * Returns a hash code for this object. 268 * 269 * @return A hash code. 270 */ 271 public int hashCode() { 272 return (this.fragments != null ? this.fragments.hashCode() : 0); 273 } 274 275 }