001/* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2014, 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 * TextUtils.java 029 * -------------- 030 * (C) Copyright 2014, by Object Refinery Limited. 031 * 032 * Original Author: David Gilbert (for Object Refinery Limited); 033 * Contributor(s): -; 034 * 035 * Changes 036 * ------- 037 * 30-Jun-2014 : Version 1 (DG); 038 * 039 */ 040 041package org.jfree.chart.util; 042 043import java.awt.Font; 044import java.awt.FontMetrics; 045import java.awt.Graphics2D; 046import java.awt.font.FontRenderContext; 047import java.awt.font.LineMetrics; 048import java.awt.geom.Rectangle2D; 049import org.jfree.ui.TextAnchor; 050 051/** 052 * Text utility functions. 053 * 054 * @since 1.0.18 055 */ 056public class TextUtils { 057 058 /** 059 * Draws a string such that the specified anchor point is aligned to the 060 * given <code>(x, y)</code> location, and returns a bounding rectangle 061 * for the text. 062 * 063 * @param text the text. 064 * @param g2 the graphics device. 065 * @param x the x coordinate (Java 2D). 066 * @param y the y coordinate (Java 2D). 067 * @param anchor the anchor location. 068 * 069 * @return The text bounds (adjusted for the text position). 070 */ 071 public static Rectangle2D drawAlignedString(String text, 072 Graphics2D g2, float x, float y, TextAnchor anchor) { 073 074 Rectangle2D textBounds = new Rectangle2D.Double(); 075 float[] adjust = deriveTextBoundsAnchorOffsets(g2, text, anchor, 076 textBounds); 077 // adjust text bounds to match string position 078 textBounds.setRect(x + adjust[0], y + adjust[1] + adjust[2], 079 textBounds.getWidth(), textBounds.getHeight()); 080 g2.drawString(text, x + adjust[0], y + adjust[1]); 081 return textBounds; 082 } 083 084 /** 085 * Returns the bounds of an aligned string. 086 * 087 * @param text the string (<code>null</code> not permitted). 088 * @param g2 the graphics target (<code>null</code> not permitted). 089 * @param x the x-coordinate. 090 * @param y the y-coordinate. 091 * @param anchor the anchor point that will be aligned to 092 * <code>(x, y)</code> (<code>null</code> not permitted). 093 * 094 * @return The text bounds (never <code>null</code>). 095 * 096 * @since 1.3 097 */ 098 public static Rectangle2D calcAlignedStringBounds(String text, 099 Graphics2D g2, float x, float y, TextAnchor anchor) { 100 101 Rectangle2D textBounds = new Rectangle2D.Double(); 102 float[] adjust = deriveTextBoundsAnchorOffsets(g2, text, anchor, 103 textBounds); 104 // adjust text bounds to match string position 105 textBounds.setRect(x + adjust[0], y + adjust[1] + adjust[2], 106 textBounds.getWidth(), textBounds.getHeight()); 107 return textBounds; 108 } 109 110 /** 111 * A utility method that calculates the anchor offsets for a string. 112 * Normally, the (x, y) coordinate for drawing text is a point on the 113 * baseline at the left of the text string. If you add these offsets to 114 * (x, y) and draw the string, then the anchor point should coincide with 115 * the (x, y) point. 116 * 117 * @param g2 the graphics device (not <code>null</code>). 118 * @param text the text. 119 * @param anchor the anchor point. 120 * 121 * @return The offsets. 122 */ 123 private static float[] deriveTextBoundsAnchorOffsets(Graphics2D g2, 124 String text, TextAnchor anchor) { 125 126 float[] result = new float[2]; 127 FontRenderContext frc = g2.getFontRenderContext(); 128 Font f = g2.getFont(); 129 FontMetrics fm = g2.getFontMetrics(f); 130 Rectangle2D bounds = getTextBounds(text, fm); 131 LineMetrics metrics = f.getLineMetrics(text, frc); 132 float ascent = metrics.getAscent(); 133 float halfAscent = ascent / 2.0f; 134 float descent = metrics.getDescent(); 135 float leading = metrics.getLeading(); 136 float xAdj = 0.0f; 137 float yAdj = 0.0f; 138 139 if (anchor.isHorizontalCenter()) { 140 xAdj = (float) -bounds.getWidth() / 2.0f; 141 } 142 else if (anchor.isRight()) { 143 xAdj = (float) -bounds.getWidth(); 144 } 145 146 if (anchor.isTop()) { 147 yAdj = -descent - leading + (float) bounds.getHeight(); 148 } 149 else if (anchor.isHalfAscent()) { 150 yAdj = halfAscent; 151 } 152 else if (anchor.isVerticalCenter()) { 153 yAdj = -descent - leading + (float) (bounds.getHeight() / 2.0); 154 } 155 else if (anchor.isBaseline()) { 156 yAdj = 0.0f; 157 } 158 else if (anchor.isBottom()) { 159 yAdj = -metrics.getDescent() - metrics.getLeading(); 160 } 161 result[0] = xAdj; 162 result[1] = yAdj; 163 return result; 164 165 } 166 167 /** 168 * A utility method that calculates the anchor offsets for a string. 169 * Normally, the (x, y) coordinate for drawing text is a point on the 170 * baseline at the left of the text string. If you add these offsets to 171 * (x, y) and draw the string, then the anchor point should coincide with 172 * the (x, y) point. 173 * 174 * @param g2 the graphics device (not <code>null</code>). 175 * @param text the text. 176 * @param anchor the anchor point. 177 * @param textBounds the text bounds (if not <code>null</code>, this 178 * object will be updated by this method to match the 179 * string bounds). 180 * 181 * @return The offsets. 182 */ 183 private static float[] deriveTextBoundsAnchorOffsets(Graphics2D g2, 184 String text, TextAnchor anchor, Rectangle2D textBounds) { 185 186 float[] result = new float[3]; 187 FontRenderContext frc = g2.getFontRenderContext(); 188 Font f = g2.getFont(); 189 FontMetrics fm = g2.getFontMetrics(f); 190 Rectangle2D bounds = getTextBounds(text, fm); 191 LineMetrics metrics = f.getLineMetrics(text, frc); 192 float ascent = metrics.getAscent(); 193 result[2] = -ascent; 194 float halfAscent = ascent / 2.0f; 195 float descent = metrics.getDescent(); 196 float leading = metrics.getLeading(); 197 float xAdj = 0.0f; 198 float yAdj = 0.0f; 199 200 if (anchor.isHorizontalCenter()) { 201 xAdj = (float) -bounds.getWidth() / 2.0f; 202 } 203 else if (anchor.isRight()) { 204 xAdj = (float) -bounds.getWidth(); 205 } 206 207 if (anchor.isTop()) { 208 yAdj = -descent - leading + (float) bounds.getHeight(); 209 } 210 else if (anchor.isHalfAscent()) { 211 yAdj = halfAscent; 212 } 213 else if (anchor.isHorizontalCenter()) { 214 yAdj = -descent - leading + (float) (bounds.getHeight() / 2.0); 215 } 216 else if (anchor.isBaseline()) { 217 yAdj = 0.0f; 218 } 219 else if (anchor.isBottom()) { 220 yAdj = -metrics.getDescent() - metrics.getLeading(); 221 } 222 if (textBounds != null) { 223 textBounds.setRect(bounds); 224 } 225 result[0] = xAdj; 226 result[1] = yAdj; 227 return result; 228 } 229 230 /** 231 * Returns the bounds for the specified text. The supplied text is 232 * assumed to be on a single line (no carriage return or newline 233 * characters). 234 * 235 * @param text the text (<code>null</code> not permitted). 236 * @param fm the font metrics (<code>null</code> not permitted). 237 * 238 * @return The text bounds. 239 */ 240 public static Rectangle2D getTextBounds(String text, FontMetrics fm) { 241 return getTextBounds(text, 0.0, 0.0, fm); 242 } 243 244 /** 245 * Returns the bounds for the specified text when it is drawn with the 246 * left-baseline aligned to the point <code>(x, y)</code>. 247 * 248 * @param text the text (<code>null</code> not permitted). 249 * @param x the x-coordinate. 250 * @param y the y-coordinate. 251 * @param fm the font metrics (<code>null</code> not permitted). 252 * 253 * @return The bounding rectangle (never <code>null</code>). 254 */ 255 public static Rectangle2D getTextBounds(String text, double x, double y, 256 FontMetrics fm) { 257 ParamChecks.nullNotPermitted(text, "text"); 258 ParamChecks.nullNotPermitted(fm, "fm"); 259 double width = fm.stringWidth(text); 260 double height = fm.getHeight(); 261 return new Rectangle2D.Double(x, y - fm.getAscent(), width, height); 262 263 } 264}