001/* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2017, 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 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 * @since 1.3 302 */ 303 public static Rectangle2D calcAlignedStringBounds(String text, 304 Graphics2D g2, float x, float y, TextAnchor anchor) { 305 306 Rectangle2D textBounds = new Rectangle2D.Double(); 307 float[] adjust = deriveTextBoundsAnchorOffsets(g2, text, anchor, 308 textBounds); 309 // adjust text bounds to match string position 310 textBounds.setRect(x + adjust[0], y + adjust[1] + adjust[2], 311 textBounds.getWidth(), textBounds.getHeight()); 312 return textBounds; 313 } 314 315 /** 316 * Draws a string such that the specified anchor point is aligned to the 317 * given (x, y) location. 318 * 319 * @param text the text. 320 * @param g2 the graphics device. 321 * @param x the x coordinate (Java 2D). 322 * @param y the y coordinate (Java 2D). 323 * @param anchor the anchor location. 324 * 325 * @return The text bounds (adjusted for the text position). 326 */ 327 public static Rectangle2D drawAlignedString(String text, Graphics2D g2, 328 float x, float y, TextAnchor anchor) { 329 330 Rectangle2D textBounds = new Rectangle2D.Double(); 331 float[] adjust = deriveTextBoundsAnchorOffsets(g2, text, anchor, 332 textBounds); 333 // adjust text bounds to match string position 334 textBounds.setRect(x + adjust[0], y + adjust[1] + adjust[2], 335 textBounds.getWidth(), textBounds.getHeight()); 336 if (!drawStringsWithFontAttributes) { 337 g2.drawString(text, x + adjust[0], y + adjust[1]); 338 } else { 339 AttributedString as = new AttributedString(text, 340 g2.getFont().getAttributes()); 341 g2.drawString(as.getIterator(), x + adjust[0], y + adjust[1]); 342 } 343 return textBounds; 344 } 345 346 /** 347 * A utility method that calculates the anchor offsets for a string. 348 * Normally, the (x, y) coordinate for drawing text is a point on the 349 * baseline at the left of the text string. If you add these offsets to 350 * (x, y) and draw the string, then the anchor point should coincide with 351 * the (x, y) point. 352 * 353 * @param g2 the graphics device (not {@code null}). 354 * @param text the text. 355 * @param anchor the anchor point. 356 * @param textBounds the text bounds (if not {@code null}, this 357 * object will be updated by this method to match the 358 * string bounds). 359 * 360 * @return The offsets. 361 */ 362 private static float[] deriveTextBoundsAnchorOffsets(Graphics2D g2, 363 String text, TextAnchor anchor, Rectangle2D textBounds) { 364 365 float[] result = new float[3]; 366 FontRenderContext frc = g2.getFontRenderContext(); 367 Font f = g2.getFont(); 368 FontMetrics fm = g2.getFontMetrics(f); 369 Rectangle2D bounds = TextUtils.getTextBounds(text, g2, fm); 370 LineMetrics metrics = f.getLineMetrics(text, frc); 371 float ascent = metrics.getAscent(); 372 result[2] = -ascent; 373 float halfAscent = ascent / 2.0f; 374 float descent = metrics.getDescent(); 375 float leading = metrics.getLeading(); 376 float xAdj = 0.0f; 377 float yAdj = 0.0f; 378 379 if (anchor.isHorizontalCenter()) { 380 xAdj = (float) -bounds.getWidth() / 2.0f; 381 } 382 else if (anchor.isRight()) { 383 xAdj = (float) -bounds.getWidth(); 384 } 385 386 if (anchor.isTop()) { 387 yAdj = -descent - leading + (float) bounds.getHeight(); 388 } 389 else if (anchor.isHalfAscent()) { 390 yAdj = halfAscent; 391 } 392 else if (anchor.isVerticalCenter()) { 393 yAdj = -descent - leading + (float) (bounds.getHeight() / 2.0); 394 } 395 else if (anchor.isBaseline()) { 396 yAdj = 0.0f; 397 } 398 else if (anchor.isBottom()) { 399 yAdj = -metrics.getDescent() - metrics.getLeading(); 400 } 401 if (textBounds != null) { 402 textBounds.setRect(bounds); 403 } 404 result[0] = xAdj; 405 result[1] = yAdj; 406 return result; 407 408 } 409 410 /** 411 * A utility method for drawing rotated text. 412 * <P> 413 * A common rotation is -Math.PI/2 which draws text 'vertically' (with the 414 * top of the characters on the left). 415 * 416 * @param text the text. 417 * @param g2 the graphics device. 418 * @param angle the angle of the (clockwise) rotation (in radians). 419 * @param x the x-coordinate. 420 * @param y the y-coordinate. 421 */ 422 public static void drawRotatedString(String text, Graphics2D g2, 423 double angle, float x, float y) { 424 drawRotatedString(text, g2, x, y, angle, x, y); 425 } 426 427 /** 428 * A utility method for drawing rotated text. 429 * <P> 430 * A common rotation is -Math.PI/2 which draws text 'vertically' (with the 431 * top of the characters on the left). 432 * 433 * @param text the text. 434 * @param g2 the graphics device. 435 * @param textX the x-coordinate for the text (before rotation). 436 * @param textY the y-coordinate for the text (before rotation). 437 * @param angle the angle of the (clockwise) rotation (in radians). 438 * @param rotateX the point about which the text is rotated. 439 * @param rotateY the point about which the text is rotated. 440 */ 441 public static void drawRotatedString(String text, Graphics2D g2, 442 float textX, float textY, 443 double angle, float rotateX, float rotateY) { 444 445 if ((text == null) || (text.equals(""))) { 446 return; 447 } 448 if (angle == 0.0) { 449 drawAlignedString(text, g2, textX, textY, TextAnchor.BASELINE_LEFT); 450 return; 451 } 452 453 AffineTransform saved = g2.getTransform(); 454 AffineTransform rotate = AffineTransform.getRotateInstance( 455 angle, rotateX, rotateY); 456 g2.transform(rotate); 457 458 if (useDrawRotatedStringWorkaround) { 459 // workaround for JDC bug ID 4312117 and others... 460 TextLayout tl = new TextLayout(text, g2.getFont(), 461 g2.getFontRenderContext()); 462 tl.draw(g2, textX, textY); 463 } 464 else { 465 if (!drawStringsWithFontAttributes) { 466 g2.drawString(text, textX, textY); 467 } else { 468 AttributedString as = new AttributedString(text, 469 g2.getFont().getAttributes()); 470 g2.drawString(as.getIterator(), textX, textY); 471 } 472 } 473 g2.setTransform(saved); 474 475 } 476 477 /** 478 * Draws a string that is aligned by one anchor point and rotated about 479 * another anchor point. 480 * 481 * @param text the text. 482 * @param g2 the graphics device. 483 * @param x the x-coordinate for positioning the text. 484 * @param y the y-coordinate for positioning the text. 485 * @param textAnchor the text anchor. 486 * @param angle the rotation angle. 487 * @param rotationX the x-coordinate for the rotation anchor point. 488 * @param rotationY the y-coordinate for the rotation anchor point. 489 */ 490 public static void drawRotatedString(String text, Graphics2D g2, 491 float x, float y, TextAnchor textAnchor, 492 double angle, float rotationX, float rotationY) { 493 494 if (text == null || text.equals("")) { 495 return; 496 } 497 if (angle == 0.0) { 498 drawAlignedString(text, g2, x, y, textAnchor); 499 } else { 500 float[] textAdj = deriveTextBoundsAnchorOffsets(g2, text, 501 textAnchor); 502 drawRotatedString(text, g2, x + textAdj[0], y + textAdj[1], angle, 503 rotationX, rotationY); 504 } 505 } 506 507 /** 508 * Draws a string that is aligned by one anchor point and rotated about 509 * another anchor point. 510 * 511 * @param text the text. 512 * @param g2 the graphics device. 513 * @param x the x-coordinate for positioning the text. 514 * @param y the y-coordinate for positioning the text. 515 * @param textAnchor the text anchor. 516 * @param angle the rotation angle (in radians). 517 * @param rotationAnchor the rotation anchor. 518 */ 519 public static void drawRotatedString(String text, Graphics2D g2, 520 float x, float y, TextAnchor textAnchor, 521 double angle, TextAnchor rotationAnchor) { 522 523 if (text == null || text.equals("")) { 524 return; 525 } 526 if (angle == 0.0) { 527 drawAlignedString(text, g2, x, y, textAnchor); 528 } else { 529 float[] textAdj = deriveTextBoundsAnchorOffsets(g2, text, 530 textAnchor); 531 float[] rotateAdj = deriveRotationAnchorOffsets(g2, text, 532 rotationAnchor); 533 drawRotatedString(text, g2, x + textAdj[0], y + textAdj[1], 534 angle, x + textAdj[0] + rotateAdj[0], 535 y + textAdj[1] + rotateAdj[1]); 536 } 537 } 538 539 /** 540 * Returns a shape that represents the bounds of the string after the 541 * specified rotation has been applied. 542 * 543 * @param text the text ({@code null} permitted). 544 * @param g2 the graphics device. 545 * @param x the x coordinate for the anchor point. 546 * @param y the y coordinate for the anchor point. 547 * @param textAnchor the text anchor. 548 * @param angle the angle. 549 * @param rotationAnchor the rotation anchor. 550 * 551 * @return The bounds (possibly {@code null}). 552 */ 553 public static Shape calculateRotatedStringBounds(String text, Graphics2D g2, 554 float x, float y, TextAnchor textAnchor, 555 double angle, TextAnchor rotationAnchor) { 556 557 if (text == null || text.equals("")) { 558 return null; 559 } 560 float[] textAdj = deriveTextBoundsAnchorOffsets(g2, text, textAnchor); 561 float[] rotateAdj = deriveRotationAnchorOffsets(g2, text, 562 rotationAnchor); 563 Shape result = calculateRotatedStringBounds(text, g2, 564 x + textAdj[0], y + textAdj[1], angle, 565 x + textAdj[0] + rotateAdj[0], y + textAdj[1] + rotateAdj[1]); 566 return result; 567 568 } 569 570 /** 571 * A utility method that calculates the anchor offsets for a string. 572 * Normally, the (x, y) coordinate for drawing text is a point on the 573 * baseline at the left of the text string. If you add these offsets to 574 * (x, y) and draw the string, then the anchor point should coincide with 575 * the (x, y) point. 576 * 577 * @param g2 the graphics device (not {@code null}). 578 * @param text the text. 579 * @param anchor the anchor point. 580 * 581 * @return The offsets. 582 */ 583 private static float[] deriveTextBoundsAnchorOffsets(Graphics2D g2, 584 String text, TextAnchor anchor) { 585 586 float[] result = new float[2]; 587 FontRenderContext frc = g2.getFontRenderContext(); 588 Font f = g2.getFont(); 589 FontMetrics fm = g2.getFontMetrics(f); 590 Rectangle2D bounds = getTextBounds(text, g2, fm); 591 LineMetrics metrics = f.getLineMetrics(text, frc); 592 float ascent = metrics.getAscent(); 593 float halfAscent = ascent / 2.0f; 594 float descent = metrics.getDescent(); 595 float leading = metrics.getLeading(); 596 float xAdj = 0.0f; 597 float yAdj = 0.0f; 598 599 if (anchor.isHorizontalCenter()) { 600 xAdj = (float) -bounds.getWidth() / 2.0f; 601 } 602 else if (anchor.isRight()) { 603 xAdj = (float) -bounds.getWidth(); 604 } 605 606 if (anchor.isTop()) { 607 yAdj = -descent - leading + (float) bounds.getHeight(); 608 } 609 else if (anchor.isHalfAscent()) { 610 yAdj = halfAscent; 611 } 612 else if (anchor.isVerticalCenter()) { 613 yAdj = -descent - leading + (float) (bounds.getHeight() / 2.0); 614 } 615 else if (anchor.isBaseline()) { 616 yAdj = 0.0f; 617 } 618 else if (anchor.isBottom()) { 619 yAdj = -metrics.getDescent() - metrics.getLeading(); 620 } 621 result[0] = xAdj; 622 result[1] = yAdj; 623 return result; 624 625 } 626 627 /** 628 * A utility method that calculates the rotation anchor offsets for a 629 * string. These offsets are relative to the text starting coordinate 630 * ({@code BASELINE_LEFT}). 631 * 632 * @param g2 the graphics device. 633 * @param text the text. 634 * @param anchor the anchor point. 635 * 636 * @return The offsets. 637 */ 638 private static float[] deriveRotationAnchorOffsets(Graphics2D g2, 639 String text, TextAnchor anchor) { 640 641 float[] result = new float[2]; 642 FontRenderContext frc = g2.getFontRenderContext(); 643 LineMetrics metrics = g2.getFont().getLineMetrics(text, frc); 644 FontMetrics fm = g2.getFontMetrics(); 645 Rectangle2D bounds = TextUtils.getTextBounds(text, g2, fm); 646 float ascent = metrics.getAscent(); 647 float halfAscent = ascent / 2.0f; 648 float descent = metrics.getDescent(); 649 float leading = metrics.getLeading(); 650 float xAdj = 0.0f; 651 float yAdj = 0.0f; 652 653 if (anchor.isLeft()) { 654 xAdj = 0.0f; 655 } 656 else if (anchor.isHorizontalCenter()) { 657 xAdj = (float) bounds.getWidth() / 2.0f; 658 } 659 else if (anchor.isRight()) { 660 xAdj = (float) bounds.getWidth(); 661 } 662 663 if (anchor.isTop()) { 664 yAdj = descent + leading - (float) bounds.getHeight(); 665 } 666 else if (anchor.isVerticalCenter()) { 667 yAdj = descent + leading - (float) (bounds.getHeight() / 2.0); 668 } 669 else if (anchor.isHalfAscent()) { 670 yAdj = -halfAscent; 671 } 672 else if (anchor.isBaseline()) { 673 yAdj = 0.0f; 674 } 675 else if (anchor.isBottom()) { 676 yAdj = metrics.getDescent() + metrics.getLeading(); 677 } 678 result[0] = xAdj; 679 result[1] = yAdj; 680 return result; 681 682 } 683 684 /** 685 * Returns a shape that represents the bounds of the string after the 686 * specified rotation has been applied. 687 * 688 * @param text the text ({@code null} permitted). 689 * @param g2 the graphics device. 690 * @param textX the x coordinate for the text. 691 * @param textY the y coordinate for the text. 692 * @param angle the angle. 693 * @param rotateX the x coordinate for the rotation point. 694 * @param rotateY the y coordinate for the rotation point. 695 * 696 * @return The bounds ({@code null} if {@code text} is 697 * {@code null} or has zero length). 698 */ 699 public static Shape calculateRotatedStringBounds(String text, Graphics2D g2, 700 float textX, float textY, double angle, float rotateX, 701 float rotateY) { 702 703 if ((text == null) || (text.equals(""))) { 704 return null; 705 } 706 FontMetrics fm = g2.getFontMetrics(); 707 Rectangle2D bounds = TextUtils.getTextBounds(text, g2, fm); 708 AffineTransform translate = AffineTransform.getTranslateInstance( 709 textX, textY); 710 Shape translatedBounds = translate.createTransformedShape(bounds); 711 AffineTransform rotate = AffineTransform.getRotateInstance( 712 angle, rotateX, rotateY); 713 Shape result = rotate.createTransformedShape(translatedBounds); 714 return result; 715 716 } 717 718 /** 719 * Returns the flag that controls whether the FontMetrics.getStringBounds() 720 * method is used or not. If you are having trouble with label alignment 721 * or positioning, try changing the value of this flag. 722 * 723 * @return A boolean. 724 */ 725 public static boolean getUseFontMetricsGetStringBounds() { 726 return useFontMetricsGetStringBounds; 727 } 728 729 /** 730 * Sets the flag that controls whether the FontMetrics.getStringBounds() 731 * method is used or not. If you are having trouble with label alignment 732 * or positioning, try changing the value of this flag. 733 * 734 * @param use the flag. 735 */ 736 public static void setUseFontMetricsGetStringBounds(boolean use) { 737 useFontMetricsGetStringBounds = use; 738 } 739 740 /** 741 * Returns the flag that controls whether or not a workaround is used for 742 * drawing rotated strings. 743 * 744 * @return A boolean. 745 */ 746 public static boolean isUseDrawRotatedStringWorkaround() { 747 return useDrawRotatedStringWorkaround; 748 } 749 750 /** 751 * Sets the flag that controls whether or not a workaround is used for 752 * drawing rotated strings. The related bug is on Sun's bug parade 753 * (id 4312117) and the workaround involves using a {@code TextLayout} 754 * instance to draw the text instead of calling the 755 * {@code drawString()} method in the {@code Graphics2D} class. 756 * 757 * @param use the new flag value. 758 */ 759 public static void setUseDrawRotatedStringWorkaround(boolean use) { 760 TextUtils.useDrawRotatedStringWorkaround = use; 761 } 762 763 /** 764 * Returns the flag that controls whether or not strings are drawn using 765 * the current font attributes (such as underlining, strikethrough etc). 766 * The default value is {@code false}. 767 * 768 * @return A boolean. 769 * 770 * @since 1.0.21 771 */ 772 public static boolean getDrawStringsWithFontAttributes() { 773 return TextUtils.drawStringsWithFontAttributes; 774 } 775 776 /** 777 * Sets the flag that controls whether or not strings are drawn using the 778 * current font attributes. This is a hack to allow underlining of titles 779 * without big changes to the API. See: 780 * http://www.jfree.org/phpBB2/viewtopic.php?p=45459&highlight=#45459 781 * 782 * @param b the new flag value. 783 * 784 * @since 1.0.21 785 */ 786 public static void setDrawStringsWithFontAttributes(boolean b) { 787 TextUtils.drawStringsWithFontAttributes = b; 788 } 789 790} 791