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.FontMetrics; 033import java.awt.Graphics2D; 034import java.awt.Paint; 035import java.awt.Shape; 036import java.awt.font.FontRenderContext; 037import java.awt.font.LineMetrics; 038import java.awt.font.TextLayout; 039import java.awt.geom.AffineTransform; 040import java.awt.geom.Rectangle2D; 041import java.text.AttributedString; 042import java.text.BreakIterator; 043import org.jfree.chart.ui.TextAnchor; 044 045/** 046 * Some utility methods for working with text in Java2D. 047 */ 048public class TextUtils { 049 050 /** 051 * When this flag is set to {@code true}, strings will be drawn 052 * as attributed strings with the attributes taken from the current font. 053 * This allows for underlining, strike-out etc, but it means that 054 * TextLayout will be used to render the text: 055 * 056 * http://www.jfree.org/phpBB2/viewtopic.php?p=45459&highlight=#45459 057 */ 058 private static boolean drawStringsWithFontAttributes = false; 059 060 /** 061 * A flag that controls whether or not the rotated string workaround is 062 * used. 063 */ 064 private static boolean useDrawRotatedStringWorkaround = true; 065 066 /** 067 * A flag that controls whether the FontMetrics.getStringBounds() method 068 * is used or a workaround is applied. 069 */ 070 private static boolean useFontMetricsGetStringBounds = false; 071 072 /** 073 * Private constructor prevents object creation. 074 */ 075 private TextUtils() { 076 // prevent instantiation 077 } 078 079 /** 080 * Creates a {@link TextBlock} from a {@code String}. Line breaks 081 * are added where the {@code String} contains '\n' characters. 082 * 083 * @param text the text. 084 * @param font the font. 085 * @param paint the paint. 086 * 087 * @return A text block. 088 */ 089 public static TextBlock createTextBlock(String text, Font font, 090 Paint paint) { 091 if (text == null) { 092 throw new IllegalArgumentException("Null 'text' argument."); 093 } 094 TextBlock result = new TextBlock(); 095 String input = text; 096 boolean moreInputToProcess = (text.length() > 0); 097 int start = 0; 098 while (moreInputToProcess) { 099 int index = input.indexOf("\n"); 100 if (index > start) { 101 String line = input.substring(start, index); 102 if (index < input.length() - 1) { 103 result.addLine(line, font, paint); 104 input = input.substring(index + 1); 105 } 106 else { 107 moreInputToProcess = false; 108 } 109 } 110 else if (index == start) { 111 if (index < input.length() - 1) { 112 input = input.substring(index + 1); 113 } 114 else { 115 moreInputToProcess = false; 116 } 117 } 118 else { 119 result.addLine(input, font, paint); 120 moreInputToProcess = false; 121 } 122 } 123 return result; 124 } 125 126 /** 127 * Creates a new text block from the given string, breaking the 128 * text into lines so that the {@code maxWidth} value is respected. 129 * 130 * @param text the text. 131 * @param font the font. 132 * @param paint the paint. 133 * @param maxWidth the maximum width for each line. 134 * @param measurer the text measurer. 135 * 136 * @return A text block. 137 */ 138 public static TextBlock createTextBlock(String text, Font font, 139 Paint paint, float maxWidth, TextMeasurer measurer) { 140 return createTextBlock(text, font, paint, maxWidth, Integer.MAX_VALUE, 141 measurer); 142 } 143 144 /** 145 * Creates a new text block from the given string, breaking the 146 * text into lines so that the {@code maxWidth} value is 147 * respected. 148 * 149 * @param text the text. 150 * @param font the font. 151 * @param paint the paint. 152 * @param maxWidth the maximum width for each line. 153 * @param maxLines the maximum number of lines. 154 * @param measurer the text measurer. 155 * 156 * @return A text block. 157 */ 158 public static TextBlock createTextBlock(String text, Font font, 159 Paint paint, float maxWidth, int maxLines, TextMeasurer measurer) { 160 161 TextBlock result = new TextBlock(); 162 BreakIterator iterator = BreakIterator.getLineInstance(); 163 iterator.setText(text); 164 int current = 0; 165 int lines = 0; 166 int length = text.length(); 167 while (current < length && lines < maxLines) { 168 int next = nextLineBreak(text, current, maxWidth, iterator, 169 measurer); 170 if (next == BreakIterator.DONE) { 171 result.addLine(text.substring(current), font, paint); 172 return result; 173 } else if (next == current) { 174 next++; // we must take one more character or we'll loop forever 175 } 176 result.addLine(text.substring(current, next), font, paint); 177 lines++; 178 current = next; 179 while (current < text.length()&& text.charAt(current) == '\n') { 180 current++; 181 } 182 } 183 if (current < length) { 184 TextLine lastLine = result.getLastLine(); 185 TextFragment lastFragment = lastLine.getLastTextFragment(); 186 String oldStr = lastFragment.getText(); 187 String newStr = "..."; 188 if (oldStr.length() > 3) { 189 newStr = oldStr.substring(0, oldStr.length() - 3) + "..."; 190 } 191 192 lastLine.removeFragment(lastFragment); 193 TextFragment newFragment = new TextFragment(newStr, 194 lastFragment.getFont(), lastFragment.getPaint()); 195 lastLine.addFragment(newFragment); 196 } 197 return result; 198 } 199 200 /** 201 * Returns the character index of the next line break. If the next 202 * character is wider than {@code width]} this method will return 203 * {@code start} - the caller should check for this case. 204 * 205 * @param text the text ({@code null} not permitted). 206 * @param start the start index. 207 * @param width the target display width. 208 * @param iterator the word break iterator. 209 * @param measurer the text measurer. 210 * 211 * @return The index of the next line break. 212 */ 213 private static int nextLineBreak(String text, int start, float width, 214 BreakIterator iterator, TextMeasurer measurer) { 215 216 // this method is (loosely) based on code in JFreeReport's 217 // TextParagraph class 218 int current = start; 219 int end; 220 float x = 0.0f; 221 boolean firstWord = true; 222 int newline = text.indexOf('\n', start); 223 if (newline < 0) { 224 newline = Integer.MAX_VALUE; 225 } 226 while (((end = iterator.following(current)) != BreakIterator.DONE)) { 227 x += measurer.getStringWidth(text, current, end); 228 if (x > width) { 229 if (firstWord) { 230 while (measurer.getStringWidth(text, start, end) > width) { 231 end--; 232 if (end <= start) { 233 return end; 234 } 235 } 236 return end; 237 } 238 else { 239 end = iterator.previous(); 240 return end; 241 } 242 } 243 else { 244 if (end > newline) { 245 return newline; 246 } 247 } 248 // we found at least one word that fits ... 249 firstWord = false; 250 current = end; 251 } 252 return BreakIterator.DONE; 253 } 254 255 /** 256 * Returns the bounds for the specified text. 257 * 258 * @param text the text ({@code null} permitted). 259 * @param g2 the graphics context (not {@code null}). 260 * @param fm the font metrics (not {@code null}). 261 * 262 * @return The text bounds ({@code null} if the {@code text} 263 * argument is {@code null}). 264 */ 265 public static Rectangle2D getTextBounds(String text, Graphics2D g2, 266 FontMetrics fm) { 267 268 Rectangle2D bounds; 269 if (TextUtils.useFontMetricsGetStringBounds) { 270 bounds = fm.getStringBounds(text, g2); 271 // getStringBounds() can return incorrect height for some Unicode 272 // characters...see bug parade 6183356, let's replace it with 273 // something correct 274 LineMetrics lm = fm.getFont().getLineMetrics(text, 275 g2.getFontRenderContext()); 276 bounds.setRect(bounds.getX(), bounds.getY(), bounds.getWidth(), 277 lm.getHeight()); 278 } 279 else { 280 double width = fm.stringWidth(text); 281 double height = fm.getHeight(); 282 bounds = new Rectangle2D.Double(0.0, -fm.getAscent(), width, 283 height); 284 } 285 return bounds; 286 } 287 288 289 /** 290 * Returns the bounds of an aligned string. 291 * 292 * @param text the string ({@code null} not permitted). 293 * @param g2 the graphics target ({@code null} not permitted). 294 * @param x the x-coordinate. 295 * @param y the y-coordinate. 296 * @param anchor the anchor point that will be aligned to 297 * {@code (x, y)} ({@code null} not permitted). 298 * 299 * @return The text bounds (never {@code null}). 300 */ 301 public static Rectangle2D calcAlignedStringBounds(String text, 302 Graphics2D g2, float x, float y, TextAnchor anchor) { 303 304 Rectangle2D textBounds = new Rectangle2D.Double(); 305 float[] adjust = deriveTextBoundsAnchorOffsets(g2, text, anchor, 306 textBounds); 307 // adjust text bounds to match string position 308 textBounds.setRect(x + adjust[0], y + adjust[1] + adjust[2], 309 textBounds.getWidth(), textBounds.getHeight()); 310 return textBounds; 311 } 312 313 /** 314 * Draws a string such that the specified anchor point is aligned to the 315 * given (x, y) location. 316 * 317 * @param text the text. 318 * @param g2 the graphics device. 319 * @param x the x coordinate (Java 2D). 320 * @param y the y coordinate (Java 2D). 321 * @param anchor the anchor location. 322 * 323 * @return The text bounds (adjusted for the text position). 324 */ 325 public static Rectangle2D drawAlignedString(String text, Graphics2D g2, 326 float x, float y, TextAnchor anchor) { 327 328 Rectangle2D textBounds = new Rectangle2D.Double(); 329 float[] adjust = deriveTextBoundsAnchorOffsets(g2, text, anchor, 330 textBounds); 331 // adjust text bounds to match string position 332 textBounds.setRect(x + adjust[0], y + adjust[1] + adjust[2], 333 textBounds.getWidth(), textBounds.getHeight()); 334 if (!drawStringsWithFontAttributes) { 335 g2.drawString(text, x + adjust[0], y + adjust[1]); 336 } else { 337 AttributedString as = new AttributedString(text, 338 g2.getFont().getAttributes()); 339 g2.drawString(as.getIterator(), x + adjust[0], y + adjust[1]); 340 } 341 return textBounds; 342 } 343 344 /** 345 * A utility method that calculates the anchor offsets for a string. 346 * Normally, the (x, y) coordinate for drawing text is a point on the 347 * baseline at the left of the text string. If you add these offsets to 348 * (x, y) and draw the string, then the anchor point should coincide with 349 * the (x, y) point. 350 * 351 * @param g2 the graphics device (not {@code null}). 352 * @param text the text. 353 * @param anchor the anchor point. 354 * @param textBounds the text bounds (if not {@code null}, this 355 * object will be updated by this method to match the 356 * string bounds). 357 * 358 * @return The offsets. 359 */ 360 private static float[] deriveTextBoundsAnchorOffsets(Graphics2D g2, 361 String text, TextAnchor anchor, Rectangle2D textBounds) { 362 363 float[] result = new float[3]; 364 FontRenderContext frc = g2.getFontRenderContext(); 365 Font f = g2.getFont(); 366 FontMetrics fm = g2.getFontMetrics(f); 367 Rectangle2D bounds = TextUtils.getTextBounds(text, g2, fm); 368 LineMetrics metrics = f.getLineMetrics(text, frc); 369 float ascent = metrics.getAscent(); 370 result[2] = -ascent; 371 float halfAscent = ascent / 2.0f; 372 float descent = metrics.getDescent(); 373 float leading = metrics.getLeading(); 374 float xAdj = 0.0f; 375 float yAdj = 0.0f; 376 377 if (anchor.isHorizontalCenter()) { 378 xAdj = (float) -bounds.getWidth() / 2.0f; 379 } 380 else if (anchor.isRight()) { 381 xAdj = (float) -bounds.getWidth(); 382 } 383 384 if (anchor.isTop()) { 385 yAdj = -descent - leading + (float) bounds.getHeight(); 386 } 387 else if (anchor.isHalfAscent()) { 388 yAdj = halfAscent; 389 } 390 else if (anchor.isVerticalCenter()) { 391 yAdj = -descent - leading + (float) (bounds.getHeight() / 2.0); 392 } 393 else if (anchor.isBaseline()) { 394 yAdj = 0.0f; 395 } 396 else if (anchor.isBottom()) { 397 yAdj = -metrics.getDescent() - metrics.getLeading(); 398 } 399 if (textBounds != null) { 400 textBounds.setRect(bounds); 401 } 402 result[0] = xAdj; 403 result[1] = yAdj; 404 return result; 405 406 } 407 408 /** 409 * A utility method for drawing rotated text. 410 * <P> 411 * A common rotation is -Math.PI/2 which draws text 'vertically' (with the 412 * top of the characters on the left). 413 * 414 * @param text the text. 415 * @param g2 the graphics device. 416 * @param angle the angle of the (clockwise) rotation (in radians). 417 * @param x the x-coordinate. 418 * @param y the y-coordinate. 419 */ 420 public static void drawRotatedString(String text, Graphics2D g2, 421 double angle, float x, float y) { 422 drawRotatedString(text, g2, x, y, angle, x, y); 423 } 424 425 /** 426 * A utility method for drawing rotated text. 427 * <P> 428 * A common rotation is -Math.PI/2 which draws text 'vertically' (with the 429 * top of the characters on the left). 430 * 431 * @param text the text. 432 * @param g2 the graphics device. 433 * @param textX the x-coordinate for the text (before rotation). 434 * @param textY the y-coordinate for the text (before rotation). 435 * @param angle the angle of the (clockwise) rotation (in radians). 436 * @param rotateX the point about which the text is rotated. 437 * @param rotateY the point about which the text is rotated. 438 */ 439 public static void drawRotatedString(String text, Graphics2D g2, 440 float textX, float textY, 441 double angle, float rotateX, float rotateY) { 442 443 if ((text == null) || (text.equals(""))) { 444 return; 445 } 446 if (angle == 0.0) { 447 drawAlignedString(text, g2, textX, textY, TextAnchor.BASELINE_LEFT); 448 return; 449 } 450 451 AffineTransform saved = g2.getTransform(); 452 AffineTransform rotate = AffineTransform.getRotateInstance( 453 angle, rotateX, rotateY); 454 g2.transform(rotate); 455 456 if (useDrawRotatedStringWorkaround) { 457 // workaround for JDC bug ID 4312117 and others... 458 TextLayout tl = new TextLayout(text, g2.getFont(), 459 g2.getFontRenderContext()); 460 tl.draw(g2, textX, textY); 461 } 462 else { 463 if (!drawStringsWithFontAttributes) { 464 g2.drawString(text, textX, textY); 465 } else { 466 AttributedString as = new AttributedString(text, 467 g2.getFont().getAttributes()); 468 g2.drawString(as.getIterator(), textX, textY); 469 } 470 } 471 g2.setTransform(saved); 472 473 } 474 475 /** 476 * Draws a string that is aligned by one anchor point and rotated about 477 * another anchor point. 478 * 479 * @param text the text. 480 * @param g2 the graphics device. 481 * @param x the x-coordinate for positioning the text. 482 * @param y the y-coordinate for positioning the text. 483 * @param textAnchor the text anchor. 484 * @param angle the rotation angle. 485 * @param rotationX the x-coordinate for the rotation anchor point. 486 * @param rotationY the y-coordinate for the rotation anchor point. 487 */ 488 public static void drawRotatedString(String text, Graphics2D g2, 489 float x, float y, TextAnchor textAnchor, 490 double angle, float rotationX, float rotationY) { 491 492 if (text == null || text.equals("")) { 493 return; 494 } 495 if (angle == 0.0) { 496 drawAlignedString(text, g2, x, y, textAnchor); 497 } else { 498 float[] textAdj = deriveTextBoundsAnchorOffsets(g2, text, 499 textAnchor); 500 drawRotatedString(text, g2, x + textAdj[0], y + textAdj[1], angle, 501 rotationX, rotationY); 502 } 503 } 504 505 /** 506 * Draws a string that is aligned by one anchor point and rotated about 507 * another anchor point. 508 * 509 * @param text the text. 510 * @param g2 the graphics device. 511 * @param x the x-coordinate for positioning the text. 512 * @param y the y-coordinate for positioning the text. 513 * @param textAnchor the text anchor. 514 * @param angle the rotation angle (in radians). 515 * @param rotationAnchor the rotation anchor. 516 */ 517 public static void drawRotatedString(String text, Graphics2D g2, 518 float x, float y, TextAnchor textAnchor, 519 double angle, TextAnchor rotationAnchor) { 520 521 if (text == null || text.equals("")) { 522 return; 523 } 524 if (angle == 0.0) { 525 drawAlignedString(text, g2, x, y, textAnchor); 526 } else { 527 float[] textAdj = deriveTextBoundsAnchorOffsets(g2, text, 528 textAnchor); 529 float[] rotateAdj = deriveRotationAnchorOffsets(g2, text, 530 rotationAnchor); 531 drawRotatedString(text, g2, x + textAdj[0], y + textAdj[1], 532 angle, x + textAdj[0] + rotateAdj[0], 533 y + textAdj[1] + rotateAdj[1]); 534 } 535 } 536 537 /** 538 * Returns a shape that represents the bounds of the string after the 539 * specified rotation has been applied. 540 * 541 * @param text the text ({@code null} permitted). 542 * @param g2 the graphics device. 543 * @param x the x coordinate for the anchor point. 544 * @param y the y coordinate for the anchor point. 545 * @param textAnchor the text anchor. 546 * @param angle the angle. 547 * @param rotationAnchor the rotation anchor. 548 * 549 * @return The bounds (possibly {@code null}). 550 */ 551 public static Shape calculateRotatedStringBounds(String text, Graphics2D g2, 552 float x, float y, TextAnchor textAnchor, 553 double angle, TextAnchor rotationAnchor) { 554 555 if (text == null || text.equals("")) { 556 return null; 557 } 558 float[] textAdj = deriveTextBoundsAnchorOffsets(g2, text, textAnchor); 559 float[] rotateAdj = deriveRotationAnchorOffsets(g2, text, 560 rotationAnchor); 561 Shape result = calculateRotatedStringBounds(text, g2, 562 x + textAdj[0], y + textAdj[1], angle, 563 x + textAdj[0] + rotateAdj[0], y + textAdj[1] + rotateAdj[1]); 564 return result; 565 566 } 567 568 /** 569 * A utility method that calculates the anchor offsets for a string. 570 * Normally, the (x, y) coordinate for drawing text is a point on the 571 * baseline at the left of the text string. If you add these offsets to 572 * (x, y) and draw the string, then the anchor point should coincide with 573 * the (x, y) point. 574 * 575 * @param g2 the graphics device (not {@code null}). 576 * @param text the text. 577 * @param anchor the anchor point. 578 * 579 * @return The offsets. 580 */ 581 private static float[] deriveTextBoundsAnchorOffsets(Graphics2D g2, 582 String text, TextAnchor anchor) { 583 584 float[] result = new float[2]; 585 FontRenderContext frc = g2.getFontRenderContext(); 586 Font f = g2.getFont(); 587 FontMetrics fm = g2.getFontMetrics(f); 588 Rectangle2D bounds = getTextBounds(text, g2, fm); 589 LineMetrics metrics = f.getLineMetrics(text, frc); 590 float ascent = metrics.getAscent(); 591 float halfAscent = ascent / 2.0f; 592 float descent = metrics.getDescent(); 593 float leading = metrics.getLeading(); 594 float xAdj = 0.0f; 595 float yAdj = 0.0f; 596 597 if (anchor.isHorizontalCenter()) { 598 xAdj = (float) -bounds.getWidth() / 2.0f; 599 } 600 else if (anchor.isRight()) { 601 xAdj = (float) -bounds.getWidth(); 602 } 603 604 if (anchor.isTop()) { 605 yAdj = -descent - leading + (float) bounds.getHeight(); 606 } 607 else if (anchor.isHalfAscent()) { 608 yAdj = halfAscent; 609 } 610 else if (anchor.isVerticalCenter()) { 611 yAdj = -descent - leading + (float) (bounds.getHeight() / 2.0); 612 } 613 else if (anchor.isBaseline()) { 614 yAdj = 0.0f; 615 } 616 else if (anchor.isBottom()) { 617 yAdj = -metrics.getDescent() - metrics.getLeading(); 618 } 619 result[0] = xAdj; 620 result[1] = yAdj; 621 return result; 622 623 } 624 625 /** 626 * A utility method that calculates the rotation anchor offsets for a 627 * string. These offsets are relative to the text starting coordinate 628 * ({@code BASELINE_LEFT}). 629 * 630 * @param g2 the graphics device. 631 * @param text the text. 632 * @param anchor the anchor point. 633 * 634 * @return The offsets. 635 */ 636 private static float[] deriveRotationAnchorOffsets(Graphics2D g2, 637 String text, TextAnchor anchor) { 638 639 float[] result = new float[2]; 640 FontRenderContext frc = g2.getFontRenderContext(); 641 LineMetrics metrics = g2.getFont().getLineMetrics(text, frc); 642 FontMetrics fm = g2.getFontMetrics(); 643 Rectangle2D bounds = TextUtils.getTextBounds(text, g2, fm); 644 float ascent = metrics.getAscent(); 645 float halfAscent = ascent / 2.0f; 646 float descent = metrics.getDescent(); 647 float leading = metrics.getLeading(); 648 float xAdj = 0.0f; 649 float yAdj = 0.0f; 650 651 if (anchor.isLeft()) { 652 xAdj = 0.0f; 653 } 654 else if (anchor.isHorizontalCenter()) { 655 xAdj = (float) bounds.getWidth() / 2.0f; 656 } 657 else if (anchor.isRight()) { 658 xAdj = (float) bounds.getWidth(); 659 } 660 661 if (anchor.isTop()) { 662 yAdj = descent + leading - (float) bounds.getHeight(); 663 } 664 else if (anchor.isVerticalCenter()) { 665 yAdj = descent + leading - (float) (bounds.getHeight() / 2.0); 666 } 667 else if (anchor.isHalfAscent()) { 668 yAdj = -halfAscent; 669 } 670 else if (anchor.isBaseline()) { 671 yAdj = 0.0f; 672 } 673 else if (anchor.isBottom()) { 674 yAdj = metrics.getDescent() + metrics.getLeading(); 675 } 676 result[0] = xAdj; 677 result[1] = yAdj; 678 return result; 679 680 } 681 682 /** 683 * Returns a shape that represents the bounds of the string after the 684 * specified rotation has been applied. 685 * 686 * @param text the text ({@code null} permitted). 687 * @param g2 the graphics device. 688 * @param textX the x coordinate for the text. 689 * @param textY the y coordinate for the text. 690 * @param angle the angle. 691 * @param rotateX the x coordinate for the rotation point. 692 * @param rotateY the y coordinate for the rotation point. 693 * 694 * @return The bounds ({@code null} if {@code text} is 695 * {@code null} or has zero length). 696 */ 697 public static Shape calculateRotatedStringBounds(String text, Graphics2D g2, 698 float textX, float textY, double angle, float rotateX, 699 float rotateY) { 700 701 if ((text == null) || (text.equals(""))) { 702 return null; 703 } 704 FontMetrics fm = g2.getFontMetrics(); 705 Rectangle2D bounds = TextUtils.getTextBounds(text, g2, fm); 706 AffineTransform translate = AffineTransform.getTranslateInstance( 707 textX, textY); 708 Shape translatedBounds = translate.createTransformedShape(bounds); 709 AffineTransform rotate = AffineTransform.getRotateInstance( 710 angle, rotateX, rotateY); 711 Shape result = rotate.createTransformedShape(translatedBounds); 712 return result; 713 714 } 715 716 /** 717 * Returns the flag that controls whether the FontMetrics.getStringBounds() 718 * method is used or not. If you are having trouble with label alignment 719 * or positioning, try changing the value of this flag. 720 * 721 * @return A boolean. 722 */ 723 public static boolean getUseFontMetricsGetStringBounds() { 724 return useFontMetricsGetStringBounds; 725 } 726 727 /** 728 * Sets the flag that controls whether the FontMetrics.getStringBounds() 729 * method is used or not. If you are having trouble with label alignment 730 * or positioning, try changing the value of this flag. 731 * 732 * @param use the flag. 733 */ 734 public static void setUseFontMetricsGetStringBounds(boolean use) { 735 useFontMetricsGetStringBounds = use; 736 } 737 738 /** 739 * Returns the flag that controls whether or not a workaround is used for 740 * drawing rotated strings. 741 * 742 * @return A boolean. 743 */ 744 public static boolean isUseDrawRotatedStringWorkaround() { 745 return useDrawRotatedStringWorkaround; 746 } 747 748 /** 749 * Sets the flag that controls whether or not a workaround is used for 750 * drawing rotated strings. The related bug is on Sun's bug parade 751 * (id 4312117) and the workaround involves using a {@code TextLayout} 752 * instance to draw the text instead of calling the 753 * {@code drawString()} method in the {@code Graphics2D} class. 754 * 755 * @param use the new flag value. 756 */ 757 public static void setUseDrawRotatedStringWorkaround(boolean use) { 758 TextUtils.useDrawRotatedStringWorkaround = use; 759 } 760 761 /** 762 * Returns the flag that controls whether or not strings are drawn using 763 * the current font attributes (such as underlining, strikethrough etc). 764 * The default value is {@code false}. 765 * 766 * @return A boolean. 767 */ 768 public static boolean getDrawStringsWithFontAttributes() { 769 return TextUtils.drawStringsWithFontAttributes; 770 } 771 772 /** 773 * Sets the flag that controls whether or not strings are drawn using the 774 * current font attributes. This is a hack to allow underlining of titles 775 * without big changes to the API. See: 776 * http://www.jfree.org/phpBB2/viewtopic.php?p=45459&highlight=#45459 777 * 778 * @param b the new flag value. 779 */ 780 public static void setDrawStringsWithFontAttributes(boolean b) { 781 TextUtils.drawStringsWithFontAttributes = b; 782 } 783 784} 785