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