001/* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-present, by David Gilbert 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.Font; 032import java.awt.Graphics2D; 033import java.awt.Paint; 034import java.awt.Shape; 035import java.awt.geom.Rectangle2D; 036import java.io.Serializable; 037import java.util.Collections; 038import java.util.Iterator; 039import java.util.List; 040import org.jfree.chart.ui.HorizontalAlignment; 041import org.jfree.chart.ui.Size2D; 042import org.jfree.chart.ui.TextAnchor; 043import org.jfree.chart.util.ShapeUtils; 044 045/** 046 * A list of {@link TextLine} objects that form a block of text. 047 * 048 * @see TextUtils#createTextBlock(String, Font, Paint) 049 */ 050public class TextBlock implements Serializable { 051 052 /** For serialization. */ 053 private static final long serialVersionUID = -4333175719424385526L; 054 055 /** Storage for the lines of text. */ 056 private List lines; 057 058 /** The alignment of the lines. */ 059 private HorizontalAlignment lineAlignment; 060 061 /** 062 * Creates a new empty text block. 063 */ 064 public TextBlock() { 065 this.lines = new java.util.ArrayList(); 066 this.lineAlignment = HorizontalAlignment.CENTER; 067 } 068 069 /** 070 * Returns the alignment of the lines of text within the block. 071 * 072 * @return The alignment (never {@code null}). 073 */ 074 public HorizontalAlignment getLineAlignment() { 075 return this.lineAlignment; 076 } 077 078 /** 079 * Sets the alignment of the lines of text within the block. 080 * 081 * @param alignment the alignment ({@code null} not permitted). 082 */ 083 public void setLineAlignment(HorizontalAlignment alignment) { 084 if (alignment == null) { 085 throw new IllegalArgumentException("Null 'alignment' argument."); 086 } 087 this.lineAlignment = alignment; 088 } 089 090 /** 091 * Adds a line of text that will be displayed using the specified font. 092 * 093 * @param text the text. 094 * @param font the font. 095 * @param paint the paint. 096 */ 097 public void addLine(String text, Font font, Paint paint) { 098 addLine(new TextLine(text, font, paint)); 099 } 100 101 /** 102 * Adds a {@link TextLine} to the block. 103 * 104 * @param line the line. 105 */ 106 public void addLine(TextLine line) { 107 this.lines.add(line); 108 } 109 110 /** 111 * Returns the last line in the block. 112 * 113 * @return The last line in the block. 114 */ 115 public TextLine getLastLine() { 116 TextLine last = null; 117 final int index = this.lines.size() - 1; 118 if (index >= 0) { 119 last = (TextLine) this.lines.get(index); 120 } 121 return last; 122 } 123 124 /** 125 * Returns an unmodifiable list containing the lines for the text block. 126 * 127 * @return A list of {@link TextLine} objects. 128 */ 129 public List getLines() { 130 return Collections.unmodifiableList(this.lines); 131 } 132 133 /** 134 * Returns the width and height of the text block. 135 * 136 * @param g2 the graphics device. 137 * 138 * @return The width and height. 139 */ 140 public Size2D calculateDimensions(Graphics2D g2) { 141 double width = 0.0; 142 double height = 0.0; 143 Iterator iterator = this.lines.iterator(); 144 while (iterator.hasNext()) { 145 final TextLine line = (TextLine) iterator.next(); 146 final Size2D dimension = line.calculateDimensions(g2); 147 width = Math.max(width, dimension.getWidth()); 148 height = height + dimension.getHeight(); 149 } 150 return new Size2D(width, height); 151 } 152 153 /** 154 * Returns the bounds of the text block. 155 * 156 * @param g2 the graphics device ({@code null} not permitted). 157 * @param anchorX the x-coordinate for the anchor point. 158 * @param anchorY the y-coordinate for the anchor point. 159 * @param anchor the text block anchor ({@code null} not permitted). 160 * @param rotateX the x-coordinate for the rotation point. 161 * @param rotateY the y-coordinate for the rotation point. 162 * @param angle the rotation angle. 163 * 164 * @return The bounds. 165 */ 166 public Shape calculateBounds(Graphics2D g2, float anchorX, float anchorY, 167 TextBlockAnchor anchor, float rotateX, float rotateY, double angle) { 168 169 Size2D d = calculateDimensions(g2); 170 float[] offsets = calculateOffsets(anchor, d.getWidth(), d.getHeight()); 171 Rectangle2D bounds = new Rectangle2D.Double(anchorX + offsets[0], 172 anchorY + offsets[1], d.getWidth(), d.getHeight()); 173 Shape rotatedBounds = ShapeUtils.rotateShape(bounds, angle, rotateX, 174 rotateY); 175 return rotatedBounds; 176 177 } 178 179 /** 180 * Draws the text block at a specific location. 181 * 182 * @param g2 the graphics device. 183 * @param x the x-coordinate for the anchor point. 184 * @param y the y-coordinate for the anchor point. 185 * @param anchor the anchor point. 186 */ 187 public void draw(Graphics2D g2, float x, float y, TextBlockAnchor anchor) { 188 draw(g2, x, y, anchor, 0.0f, 0.0f, 0.0); 189 } 190 191 /** 192 * Draws the text block, aligning it with the specified anchor point and 193 * rotating it about the specified rotation point. 194 * 195 * @param g2 the graphics device. 196 * @param anchorX the x-coordinate for the anchor point. 197 * @param anchorY the y-coordinate for the anchor point. 198 * @param anchor the point on the text block that is aligned to the 199 * anchor point. 200 * @param rotateX the x-coordinate for the rotation point. 201 * @param rotateY the x-coordinate for the rotation point. 202 * @param angle the rotation (in radians). 203 */ 204 public void draw(Graphics2D g2, float anchorX, float anchorY, 205 TextBlockAnchor anchor, float rotateX, float rotateY, double angle) { 206 207 Size2D d = calculateDimensions(g2); 208 float[] offsets = calculateOffsets(anchor, d.getWidth(), 209 d.getHeight()); 210 Iterator iterator = this.lines.iterator(); 211 float yCursor = 0.0f; 212 while (iterator.hasNext()) { 213 TextLine line = (TextLine) iterator.next(); 214 Size2D dimension = line.calculateDimensions(g2); 215 float lineOffset = 0.0f; 216 if (this.lineAlignment == HorizontalAlignment.CENTER) { 217 lineOffset = (float) (d.getWidth() - dimension.getWidth()) 218 / 2.0f; 219 } 220 else if (this.lineAlignment == HorizontalAlignment.RIGHT) { 221 lineOffset = (float) (d.getWidth() - dimension.getWidth()); 222 } 223 line.draw(g2, anchorX + offsets[0] + lineOffset, 224 anchorY + offsets[1] + yCursor, TextAnchor.TOP_LEFT, 225 rotateX, rotateY, angle); 226 yCursor = yCursor + (float) dimension.getHeight(); 227 } 228 229 } 230 231 /** 232 * Calculates the x and y offsets required to align the text block with the 233 * specified anchor point. This assumes that the top left of the text 234 * block is at (0.0, 0.0). 235 * 236 * @param anchor the anchor position. 237 * @param width the width of the text block. 238 * @param height the height of the text block. 239 * 240 * @return The offsets (float[0] = x offset, float[1] = y offset). 241 */ 242 private float[] calculateOffsets(TextBlockAnchor anchor, double width, 243 double height) { 244 float[] result = new float[2]; 245 float xAdj = 0.0f; 246 float yAdj = 0.0f; 247 248 if (anchor == TextBlockAnchor.TOP_CENTER 249 || anchor == TextBlockAnchor.CENTER 250 || anchor == TextBlockAnchor.BOTTOM_CENTER) { 251 252 xAdj = (float) -width / 2.0f; 253 254 } 255 else if (anchor == TextBlockAnchor.TOP_RIGHT 256 || anchor == TextBlockAnchor.CENTER_RIGHT 257 || anchor == TextBlockAnchor.BOTTOM_RIGHT) { 258 259 xAdj = (float) -width; 260 261 } 262 263 if (anchor == TextBlockAnchor.TOP_LEFT 264 || anchor == TextBlockAnchor.TOP_CENTER 265 || anchor == TextBlockAnchor.TOP_RIGHT) { 266 267 yAdj = 0.0f; 268 269 } 270 else if (anchor == TextBlockAnchor.CENTER_LEFT 271 || anchor == TextBlockAnchor.CENTER 272 || anchor == TextBlockAnchor.CENTER_RIGHT) { 273 274 yAdj = (float) -height / 2.0f; 275 276 } 277 else if (anchor == TextBlockAnchor.BOTTOM_LEFT 278 || anchor == TextBlockAnchor.BOTTOM_CENTER 279 || anchor == TextBlockAnchor.BOTTOM_RIGHT) { 280 281 yAdj = (float) -height; 282 283 } 284 result[0] = xAdj; 285 result[1] = yAdj; 286 return result; 287 } 288 289 /** 290 * Tests this object for equality with an arbitrary object. 291 * 292 * @param obj the object to test against ({@code null} permitted). 293 * 294 * @return A boolean. 295 */ 296 @Override 297 public boolean equals(Object obj) { 298 if (obj == this) { 299 return true; 300 } 301 if (obj instanceof TextBlock) { 302 TextBlock block = (TextBlock) obj; 303 return this.lines.equals(block.lines); 304 } 305 return false; 306 } 307 308 /** 309 * Returns a hash code for this object. 310 * 311 * @return A hash code. 312 */ 313 @Override 314 public int hashCode() { 315 return (this.lines != null ? this.lines.hashCode() : 0); 316 } 317} 318