KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jfree > text > TextUtilities


1 /* ========================================================================
2  * JCommon : a free general purpose class library for the Java(tm) platform
3  * ========================================================================
4  *
5  * (C) Copyright 2000-2006, by Object Refinery Limited and Contributors.
6  *
7  * Project Info: http://www.jfree.org/jcommon/index.html
8  *
9  * This library is free software; you can redistribute it and/or modify it
10  * under the terms of the GNU Lesser General Public License as published by
11  * the Free Software Foundation; either version 2.1 of the License, or
12  * (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful, but
15  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
16  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
17  * License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
22  * USA.
23  *
24  * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
25  * in the United States and other countries.]
26  *
27  * ------------------
28  * TextUtilities.java
29  * ------------------
30  * (C) Copyright 2004-2006, by Object Refinery Limited and Contributors.
31  *
32  * Original Author: David Gilbert (for Object Refinery Limited);
33  * Contributor(s): -;
34  *
35  * $Id: TextUtilities.java,v 1.21 2006/07/04 10:20:40 taqua Exp $
36  *
37  * Changes
38  * -------
39  * 07-Jan-2004 : Version 1 (DG);
40  * 24-Mar-2004 : Added 'paint' argument to createTextBlock() method (DG);
41  * 07-Apr-2004 : Added getTextBounds() method and useFontMetricsGetStringBounds
42  * flag (DG);
43  * 08-Apr-2004 : Changed word break iterator to line break iterator in the
44  * createTextBlock() method - see bug report 926074 (DG);
45  * 03-Sep-2004 : Updated createTextBlock() method to add ellipses when limit
46  * is reached (DG);
47  * 30-Sep-2004 : Modified bounds returned by drawAlignedString() method (DG);
48  * 10-Nov-2004 : Added new createTextBlock() method that works with
49  * newlines (DG);
50  * 19-Apr-2005 : Changed default value of useFontMetricsGetStringBounds (DG);
51  * 17-May-2005 : createTextBlock() now recognises '\n' (DG);
52  * 27-Jun-2005 : Added code to getTextBounds() method to work around Sun's bug
53  * parade item 6183356 (DG);
54  * 06-Jan-2006 : Reformatted (DG);
55  *
56  */

57
58 package org.jfree.text;
59
60 import java.awt.Font JavaDoc;
61 import java.awt.FontMetrics JavaDoc;
62 import java.awt.Graphics2D JavaDoc;
63 import java.awt.Paint JavaDoc;
64 import java.awt.Shape JavaDoc;
65 import java.awt.font.FontRenderContext JavaDoc;
66 import java.awt.font.LineMetrics JavaDoc;
67 import java.awt.font.TextLayout JavaDoc;
68 import java.awt.geom.AffineTransform JavaDoc;
69 import java.awt.geom.Rectangle2D JavaDoc;
70 import java.text.BreakIterator JavaDoc;
71
72 import org.jfree.ui.TextAnchor;
73 import org.jfree.util.Log;
74 import org.jfree.util.LogContext;
75 import org.jfree.util.ObjectUtilities;
76 import org.jfree.base.BaseBoot;
77
78 /**
79  * Some utility methods for working with text.
80  *
81  * @author David Gilbert
82  */

83 public class TextUtilities {
84
85     /** Access to logging facilities. */
86     protected static final LogContext logger = Log.createContext(
87             TextUtilities.class);
88
89     /**
90      * A flag that controls whether or not the rotated string workaround is
91      * used.
92      */

93     private static boolean useDrawRotatedStringWorkaround;
94
95     /**
96      * A flag that controls whether the FontMetrics.getStringBounds() method
97      * is used or a workaround is applied.
98      */

99     private static boolean useFontMetricsGetStringBounds;
100
101     static {
102         final boolean isJava14 = ObjectUtilities.isJDK14();
103
104         final String JavaDoc configRotatedStringWorkaround =
105               BaseBoot.getInstance().getGlobalConfig().getConfigProperty(
106                       "org.jfree.text.UseDrawRotatedStringWorkaround", "auto");
107         if (configRotatedStringWorkaround.equals("auto")) {
108            useDrawRotatedStringWorkaround = (isJava14 == false);
109         }
110         else {
111             useDrawRotatedStringWorkaround
112                     = configRotatedStringWorkaround.equals("true");
113         }
114
115         final String JavaDoc configFontMetricsStringBounds
116                 = BaseBoot.getInstance().getGlobalConfig().getConfigProperty(
117                         "org.jfree.text.UseFontMetricsGetStringBounds", "auto");
118         if (configFontMetricsStringBounds.equals("auto")) {
119             useFontMetricsGetStringBounds = (isJava14 == true);
120         }
121         else {
122             useFontMetricsGetStringBounds
123                     = configFontMetricsStringBounds.equals("true");
124         }
125     }
126
127     /**
128      * Private constructor prevents object creation.
129      */

130     private TextUtilities() {
131     }
132
133     /**
134      * Creates a {@link TextBlock} from a <code>String</code>. Line breaks
135      * are added where the <code>String</code> contains '\n' characters.
136      *
137      * @param text the text.
138      * @param font the font.
139      * @param paint the paint.
140      *
141      * @return A text block.
142      */

143     public static TextBlock createTextBlock(final String JavaDoc text, final Font JavaDoc font,
144                                             final Paint JavaDoc paint) {
145         if (text == null) {
146             throw new IllegalArgumentException JavaDoc("Null 'text' argument.");
147         }
148         final TextBlock result = new TextBlock();
149         String JavaDoc input = text;
150         boolean moreInputToProcess = (text.length() > 0);
151         final int start = 0;
152         while (moreInputToProcess) {
153             final int index = input.indexOf("\n");
154             if (index > start) {
155                 final String JavaDoc line = input.substring(start, index);
156                 if (index < input.length() - 1) {
157                     result.addLine(line, font, paint);
158                     input = input.substring(index + 1);
159                 }
160                 else {
161                     moreInputToProcess = false;
162                 }
163             }
164             else if (index == start) {
165                 if (index < input.length() - 1) {
166                     input = input.substring(index + 1);
167                 }
168                 else {
169                     moreInputToProcess = false;
170                 }
171             }
172             else {
173                 result.addLine(input, font, paint);
174                 moreInputToProcess = false;
175             }
176         }
177         return result;
178     }
179
180     /**
181      * Creates a new text block from the given string, breaking the
182      * text into lines so that the <code>maxWidth</code> value is
183      * respected.
184      *
185      * @param text the text.
186      * @param font the font.
187      * @param paint the paint.
188      * @param maxWidth the maximum width for each line.
189      * @param measurer the text measurer.
190      *
191      * @return A text block.
192      */

193     public static TextBlock createTextBlock(final String JavaDoc text, final Font JavaDoc font,
194             final Paint JavaDoc paint, final float maxWidth,
195             final TextMeasurer measurer) {
196         
197         return createTextBlock(text, font, paint, maxWidth, Integer.MAX_VALUE,
198                 measurer);
199     }
200
201     /**
202      * Creates a new text block from the given string, breaking the
203      * text into lines so that the <code>maxWidth</code> value is
204      * respected.
205      *
206      * @param text the text.
207      * @param font the font.
208      * @param paint the paint.
209      * @param maxWidth the maximum width for each line.
210      * @param maxLines the maximum number of lines.
211      * @param measurer the text measurer.
212      *
213      * @return A text block.
214      */

215     public static TextBlock createTextBlock(final String JavaDoc text, final Font JavaDoc font,
216             final Paint JavaDoc paint, final float maxWidth, final int maxLines,
217             final TextMeasurer measurer) {
218         
219         final TextBlock result = new TextBlock();
220         final BreakIterator JavaDoc iterator = BreakIterator.getLineInstance();
221         iterator.setText(text);
222         int current = 0;
223         int lines = 0;
224         final int length = text.length();
225         while (current < length && lines < maxLines) {
226             final int next = nextLineBreak(text, current, maxWidth, iterator,
227                     measurer);
228             if (next == BreakIterator.DONE) {
229                 result.addLine(text.substring(current), font, paint);
230                 return result;
231             }
232             result.addLine(text.substring(current, next), font, paint);
233             lines++;
234             current = next;
235             while (current < text.length()&& text.charAt(current) == '\n') {
236                 current++;
237             }
238         }
239         if (current < length) {
240             final TextLine lastLine = result.getLastLine();
241             final TextFragment lastFragment = lastLine.getLastTextFragment();
242             final String JavaDoc oldStr = lastFragment.getText();
243             String JavaDoc newStr = "...";
244             if (oldStr.length() > 3) {
245                 newStr = oldStr.substring(0, oldStr.length() - 3) + "...";
246             }
247
248             lastLine.removeFragment(lastFragment);
249             final TextFragment newFragment = new TextFragment(newStr,
250                     lastFragment.getFont(), lastFragment.getPaint());
251             lastLine.addFragment(newFragment);
252         }
253         return result;
254     }
255
256     /**
257      * Returns the character index of the next line break.
258      *
259      * @param text the text.
260      * @param start the start index.
261      * @param width the target display width.
262      * @param iterator the word break iterator.
263      * @param measurer the text measurer.
264      *
265      * @return The index of the next line break.
266      */

267     private static int nextLineBreak(final String JavaDoc text, final int start,
268             final float width, final BreakIterator JavaDoc iterator,
269             final TextMeasurer measurer) {
270         
271         // this method is (loosely) based on code in JFreeReport's
272
// TextParagraph class
273
int current = start;
274         int end;
275         float x = 0.0f;
276         boolean firstWord = true;
277         int newline = text.indexOf('\n', start);
278         if (newline < 0) {
279             newline = Integer.MAX_VALUE;
280         }
281         while (((end = iterator.next()) != BreakIterator.DONE)) {
282             if (end > newline) {
283                 return newline;
284             }
285             x += measurer.getStringWidth(text, current, end);
286             if (x > width) {
287                 if (firstWord) {
288                     while (measurer.getStringWidth(text, start, end) > width) {
289                         end--;
290                         if (end <= start) {
291                             return end;
292                         }
293                     }
294                     return end;
295                 }
296                 else {
297                     end = iterator.previous();
298                     return end;
299                 }
300             }
301             // we found at least one word that fits ...
302
firstWord = false;
303             current = end;
304         }
305         return BreakIterator.DONE;
306     }
307
308     /**
309      * Returns the bounds for the specified text.
310      *
311      * @param text the text (<code>null</code> permitted).
312      * @param g2 the graphics context (not <code>null</code>).
313      * @param fm the font metrics (not <code>null</code>).
314      *
315      * @return The text bounds (<code>null</code> if the <code>text</code>
316      * argument is <code>null</code>).
317      */

318     public static Rectangle2D JavaDoc getTextBounds(final String JavaDoc text,
319             final Graphics2D JavaDoc g2, final FontMetrics JavaDoc fm) {
320         
321         final Rectangle2D JavaDoc bounds;
322         if (TextUtilities.useFontMetricsGetStringBounds) {
323             bounds = fm.getStringBounds(text, g2);
324             // getStringBounds() can return incorrect height for some Unicode
325
// characters...see bug parade 6183356, let's replace it with
326
// something correct
327
LineMetrics JavaDoc lm = fm.getFont().getLineMetrics(text,
328                     g2.getFontRenderContext());
329             bounds.setRect(bounds.getX(), bounds.getY(), bounds.getWidth(),
330                     lm.getHeight());
331         }
332         else {
333             final double width = fm.stringWidth(text);
334             final double height = fm.getHeight();
335             if (logger.isDebugEnabled()) {
336                 logger.debug("Height = " + height);
337             }
338             bounds = new Rectangle2D.Double JavaDoc(0.0, -fm.getAscent(), width,
339                     height);
340         }
341         return bounds;
342     }
343
344     /**
345      * Draws a string such that the specified anchor point is aligned to the
346      * given (x, y) location.
347      *
348      * @param text the text.
349      * @param g2 the graphics device.
350      * @param x the x coordinate (Java 2D).
351      * @param y the y coordinate (Java 2D).
352      * @param anchor the anchor location.
353      *
354      * @return The text bounds (adjusted for the text position).
355      */

356     public static Rectangle2D JavaDoc drawAlignedString(final String JavaDoc text,
357             final Graphics2D JavaDoc g2, final float x, final float y,
358             final TextAnchor anchor) {
359
360         final Rectangle2D JavaDoc textBounds = new Rectangle2D.Double JavaDoc();
361         final float[] adjust = deriveTextBoundsAnchorOffsets(g2, text, anchor,
362                 textBounds);
363         // adjust text bounds to match string position
364
textBounds.setRect(x + adjust[0], y + adjust[1] + adjust[2],
365             textBounds.getWidth(), textBounds.getHeight());
366         g2.drawString(text, x + adjust[0], y + adjust[1]);
367         return textBounds;
368     }
369
370     /**
371      * A utility method that calculates the anchor offsets for a string.
372      * Normally, the (x, y) coordinate for drawing text is a point on the
373      * baseline at the left of the text string. If you add these offsets to
374      * (x, y) and draw the string, then the anchor point should coincide with
375      * the (x, y) point.
376      *
377      * @param g2 the graphics device (not <code>null</code>).
378      * @param text the text.
379      * @param anchor the anchor point.
380      * @param textBounds the text bounds (if not <code>null</code>, this
381      * object will be updated by this method to match the
382      * string bounds).
383      *
384      * @return The offsets.
385      */

386     private static float[] deriveTextBoundsAnchorOffsets(final Graphics2D JavaDoc g2,
387             final String JavaDoc text, final TextAnchor anchor,
388             final Rectangle2D JavaDoc textBounds) {
389
390         final float[] result = new float[3];
391         final FontRenderContext JavaDoc frc = g2.getFontRenderContext();
392         final Font JavaDoc f = g2.getFont();
393         final FontMetrics JavaDoc fm = g2.getFontMetrics(f);
394         final Rectangle2D JavaDoc bounds = TextUtilities.getTextBounds(text, g2, fm);
395         final LineMetrics JavaDoc metrics = f.getLineMetrics(text, frc);
396         final float ascent = metrics.getAscent();
397         result[2] = -ascent;
398         final float halfAscent = ascent / 2.0f;
399         final float descent = metrics.getDescent();
400         final float leading = metrics.getLeading();
401         float xAdj = 0.0f;
402         float yAdj = 0.0f;
403
404         if (anchor == TextAnchor.TOP_CENTER
405                 || anchor == TextAnchor.CENTER
406                 || anchor == TextAnchor.BOTTOM_CENTER
407                 || anchor == TextAnchor.BASELINE_CENTER
408                 || anchor == TextAnchor.HALF_ASCENT_CENTER) {
409
410             xAdj = (float) -bounds.getWidth() / 2.0f;
411
412         }
413         else if (anchor == TextAnchor.TOP_RIGHT
414                 || anchor == TextAnchor.CENTER_RIGHT
415                 || anchor == TextAnchor.BOTTOM_RIGHT
416                 || anchor == TextAnchor.BASELINE_RIGHT
417                 || anchor == TextAnchor.HALF_ASCENT_RIGHT) {
418
419             xAdj = (float) -bounds.getWidth();
420
421         }
422
423         if (anchor == TextAnchor.TOP_LEFT
424                 || anchor == TextAnchor.TOP_CENTER
425                 || anchor == TextAnchor.TOP_RIGHT) {
426
427             yAdj = -descent - leading + (float) bounds.getHeight();
428
429         }
430         else if (anchor == TextAnchor.HALF_ASCENT_LEFT
431                 || anchor == TextAnchor.HALF_ASCENT_CENTER
432                 || anchor == TextAnchor.HALF_ASCENT_RIGHT) {
433
434             yAdj = halfAscent;
435
436         }
437         else if (anchor == TextAnchor.CENTER_LEFT
438                 || anchor == TextAnchor.CENTER
439                 || anchor == TextAnchor.CENTER_RIGHT) {
440
441             yAdj = -descent - leading + (float) (bounds.getHeight() / 2.0);
442
443         }
444         else if (anchor == TextAnchor.BASELINE_LEFT
445                 || anchor == TextAnchor.BASELINE_CENTER
446                 || anchor == TextAnchor.BASELINE_RIGHT) {
447
448             yAdj = 0.0f;
449
450         }
451         else if (anchor == TextAnchor.BOTTOM_LEFT
452                 || anchor == TextAnchor.BOTTOM_CENTER
453                 || anchor == TextAnchor.BOTTOM_RIGHT) {
454
455             yAdj = -metrics.getDescent() - metrics.getLeading();
456
457         }
458         if (textBounds != null) {
459             textBounds.setRect(bounds);
460         }
461         result[0] = xAdj;
462         result[1] = yAdj;
463         return result;
464
465     }
466
467     /**
468      * Sets the flag that controls whether or not a workaround is used for
469      * drawing rotated strings. The related bug is on Sun's bug parade
470      * (id 4312117) and the workaround involves using a <code>TextLayout</code>
471      * instance to draw the text instead of calling the
472      * <code>drawString()</code> method in the <code>Graphics2D</code> class.
473      *
474      * @param use the new flag value.
475      */

476     public static void setUseDrawRotatedStringWorkaround(final boolean use) {
477         useDrawRotatedStringWorkaround = use;
478     }
479
480     /**
481      * A utility method for drawing rotated text.
482      * <P>
483      * A common rotation is -Math.PI/2 which draws text 'vertically' (with the
484      * top of the characters on the left).
485      *
486      * @param text the text.
487      * @param g2 the graphics device.
488      * @param angle the angle of the (clockwise) rotation (in radians).
489      * @param x the x-coordinate.
490      * @param y the y-coordinate.
491      */

492     public static void drawRotatedString(final String JavaDoc text, final Graphics2D JavaDoc g2,
493             final double angle, final float x, final float y) {
494         drawRotatedString(text, g2, x, y, angle, x, y);
495     }
496
497     /**
498      * A utility method for drawing rotated text.
499      * <P>
500      * A common rotation is -Math.PI/2 which draws text 'vertically' (with the
501      * top of the characters on the left).
502      *
503      * @param text the text.
504      * @param g2 the graphics device.
505      * @param textX the x-coordinate for the text (before rotation).
506      * @param textY the y-coordinate for the text (before rotation).
507      * @param angle the angle of the (clockwise) rotation (in radians).
508      * @param rotateX the point about which the text is rotated.
509      * @param rotateY the point about which the text is rotated.
510      */

511     public static void drawRotatedString(final String JavaDoc text, final Graphics2D JavaDoc g2,
512             final float textX, final float textY, final double angle,
513             final float rotateX, final float rotateY) {
514
515         if ((text == null) || (text.equals(""))) {
516             return;
517         }
518
519         final AffineTransform JavaDoc saved = g2.getTransform();
520
521         // apply the rotation...
522
final AffineTransform JavaDoc rotate = AffineTransform.getRotateInstance(
523                 angle, rotateX, rotateY);
524         g2.transform(rotate);
525
526         if (useDrawRotatedStringWorkaround) {
527             // workaround for JDC bug ID 4312117 and others...
528
final TextLayout JavaDoc tl = new TextLayout JavaDoc(text, g2.getFont(),
529                     g2.getFontRenderContext());
530             tl.draw(g2, textX, textY);
531         }
532         else {
533             // replaces this code...
534
g2.drawString(text, textX, textY);
535         }
536         g2.setTransform(saved);
537
538     }
539
540     /**
541      * Draws a string that is aligned by one anchor point and rotated about
542      * another anchor point.
543      *
544      * @param text the text.
545      * @param g2 the graphics device.
546      * @param x the x-coordinate for positioning the text.
547      * @param y the y-coordinate for positioning the text.
548      * @param textAnchor the text anchor.
549      * @param angle the rotation angle.
550      * @param rotationX the x-coordinate for the rotation anchor point.
551      * @param rotationY the y-coordinate for the rotation anchor point.
552      */

553     public static void drawRotatedString(final String JavaDoc text,
554             final Graphics2D JavaDoc g2, final float x, final float y,
555             final TextAnchor textAnchor, final double angle,
556             final float rotationX, final float rotationY) {
557
558         if (text == null || text.equals("")) {
559             return;
560         }
561         final float[] textAdj = deriveTextBoundsAnchorOffsets(g2, text,
562                 textAnchor);
563         drawRotatedString(text, g2, x + textAdj[0], y + textAdj[1], angle,
564                 rotationX, rotationY);
565     }
566
567     /**
568      * Draws a string that is aligned by one anchor point and rotated about
569      * another anchor point.
570      *
571      * @param text the text.
572      * @param g2 the graphics device.
573      * @param x the x-coordinate for positioning the text.
574      * @param y the y-coordinate for positioning the text.
575      * @param textAnchor the text anchor.
576      * @param angle the rotation angle (in radians).
577      * @param rotationAnchor the rotation anchor.
578      */

579     public static void drawRotatedString(final String JavaDoc text, final Graphics2D JavaDoc g2,
580             final float x, final float y, final TextAnchor textAnchor,
581             final double angle, final TextAnchor rotationAnchor) {
582
583         if (text == null || text.equals("")) {
584             return;
585         }
586         final float[] textAdj = deriveTextBoundsAnchorOffsets(g2, text,
587                 textAnchor);
588         final float[] rotateAdj = deriveRotationAnchorOffsets(g2, text,
589                 rotationAnchor);
590         drawRotatedString(text, g2, x + textAdj[0], y + textAdj[1],
591                 angle, x + textAdj[0] + rotateAdj[0],
592                 y + textAdj[1] + rotateAdj[1]);
593
594     }
595
596     /**
597      * Returns a shape that represents the bounds of the string after the
598      * specified rotation has been applied.
599      *
600      * @param text the text (<code>null</code> permitted).
601      * @param g2 the graphics device.
602      * @param x the x coordinate for the anchor point.
603      * @param y the y coordinate for the anchor point.
604      * @param textAnchor the text anchor.
605      * @param angle the angle.
606      * @param rotationAnchor the rotation anchor.
607      *
608      * @return The bounds (possibly <code>null</code>).
609      */

610     public static Shape JavaDoc calculateRotatedStringBounds(final String JavaDoc text,
611             final Graphics2D JavaDoc g2, final float x, final float y,
612             final TextAnchor textAnchor, final double angle,
613             final TextAnchor rotationAnchor) {
614
615         if (text == null || text.equals("")) {
616             return null;
617         }
618         final float[] textAdj = deriveTextBoundsAnchorOffsets(g2, text,
619                 textAnchor);
620         if (logger.isDebugEnabled()) {
621             logger.debug("TextBoundsAnchorOffsets = " + textAdj[0] + ", "
622                     + textAdj[1]);
623         }
624         final float[] rotateAdj = deriveRotationAnchorOffsets(g2, text,
625                 rotationAnchor);
626         if (logger.isDebugEnabled()) {
627             logger.debug("RotationAnchorOffsets = " + rotateAdj[0] + ", "
628                     + rotateAdj[1]);
629         }
630         final Shape JavaDoc result = calculateRotatedStringBounds(text, g2,
631                 x + textAdj[0], y + textAdj[1], angle,
632                 x + textAdj[0] + rotateAdj[0], y + textAdj[1] + rotateAdj[1]);
633         return result;
634
635     }
636
637     /**
638      * A utility method that calculates the anchor offsets for a string.
639      * Normally, the (x, y) coordinate for drawing text is a point on the
640      * baseline at the left of the text string. If you add these offsets to
641      * (x, y) and draw the string, then the anchor point should coincide with
642      * the (x, y) point.
643      *
644      * @param g2 the graphics device (not <code>null</code>).
645      * @param text the text.
646      * @param anchor the anchor point.
647      *
648      * @return The offsets.
649      */

650     private static float[] deriveTextBoundsAnchorOffsets(final Graphics2D JavaDoc g2,
651             final String JavaDoc text, final TextAnchor anchor) {
652
653         final float[] result = new float[2];
654         final FontRenderContext JavaDoc frc = g2.getFontRenderContext();
655         final Font JavaDoc f = g2.getFont();
656         final FontMetrics JavaDoc fm = g2.getFontMetrics(f);
657         final Rectangle2D JavaDoc bounds = TextUtilities.getTextBounds(text, g2, fm);
658         final LineMetrics JavaDoc metrics = f.getLineMetrics(text, frc);
659         final float ascent = metrics.getAscent();
660         final float halfAscent = ascent / 2.0f;
661         final float descent = metrics.getDescent();
662         final float leading = metrics.getLeading();
663         float xAdj = 0.0f;
664         float yAdj = 0.0f;
665
666         if (anchor == TextAnchor.TOP_CENTER
667                 || anchor == TextAnchor.CENTER
668                 || anchor == TextAnchor.BOTTOM_CENTER
669                 || anchor == TextAnchor.BASELINE_CENTER
670                 || anchor == TextAnchor.HALF_ASCENT_CENTER) {
671
672             xAdj = (float) -bounds.getWidth() / 2.0f;
673
674         }
675         else if (anchor == TextAnchor.TOP_RIGHT
676                 || anchor == TextAnchor.CENTER_RIGHT
677                 || anchor == TextAnchor.BOTTOM_RIGHT
678                 || anchor == TextAnchor.BASELINE_RIGHT
679                 || anchor == TextAnchor.HALF_ASCENT_RIGHT) {
680
681             xAdj = (float) -bounds.getWidth();
682
683         }
684
685         if (anchor == TextAnchor.TOP_LEFT
686                 || anchor == TextAnchor.TOP_CENTER
687                 || anchor == TextAnchor.TOP_RIGHT) {
688
689             yAdj = -descent - leading + (float) bounds.getHeight();
690
691         }
692         else if (anchor == TextAnchor.HALF_ASCENT_LEFT
693                 || anchor == TextAnchor.HALF_ASCENT_CENTER
694                 || anchor == TextAnchor.HALF_ASCENT_RIGHT) {
695
696             yAdj = halfAscent;
697
698         }
699         else if (anchor == TextAnchor.CENTER_LEFT
700                 || anchor == TextAnchor.CENTER
701                 || anchor == TextAnchor.CENTER_RIGHT) {
702
703             yAdj = -descent - leading + (float) (bounds.getHeight() / 2.0);
704
705         }
706         else if (anchor == TextAnchor.BASELINE_LEFT
707                 || anchor == TextAnchor.BASELINE_CENTER
708                 || anchor == TextAnchor.BASELINE_RIGHT) {
709
710             yAdj = 0.0f;
711
712         }
713         else if (anchor == TextAnchor.BOTTOM_LEFT
714                 || anchor == TextAnchor.BOTTOM_CENTER
715                 || anchor == TextAnchor.BOTTOM_RIGHT) {
716
717             yAdj = -metrics.getDescent() - metrics.getLeading();
718
719         }
720         result[0] = xAdj;
721         result[1] = yAdj;
722         return result;
723
724     }
725
726     /**
727      * A utility method that calculates the rotation anchor offsets for a
728      * string. These offsets are relative to the text starting coordinate
729      * (BASELINE_LEFT).
730      *
731      * @param g2 the graphics device.
732      * @param text the text.
733      * @param anchor the anchor point.
734      *
735      * @return The offsets.
736      */

737     private static float[] deriveRotationAnchorOffsets(final Graphics2D JavaDoc g2,
738             final String JavaDoc text, final TextAnchor anchor) {
739
740         final float[] result = new float[2];
741         final FontRenderContext JavaDoc frc = g2.getFontRenderContext();
742         final LineMetrics JavaDoc metrics = g2.getFont().getLineMetrics(text, frc);
743         final FontMetrics JavaDoc fm = g2.getFontMetrics();
744         final Rectangle2D JavaDoc bounds = TextUtilities.getTextBounds(text, g2, fm);
745         final float ascent = metrics.getAscent();
746         final float halfAscent = ascent / 2.0f;
747         final float descent = metrics.getDescent();
748         final float leading = metrics.getLeading();
749         float xAdj = 0.0f;
750         float yAdj = 0.0f;
751
752         if (anchor == TextAnchor.TOP_LEFT
753                 || anchor == TextAnchor.CENTER_LEFT
754                 || anchor == TextAnchor.BOTTOM_LEFT
755                 || anchor == TextAnchor.BASELINE_LEFT
756                 || anchor == TextAnchor.HALF_ASCENT_LEFT) {
757
758             xAdj = 0.0f;
759
760         }
761         else if (anchor == TextAnchor.TOP_CENTER
762                 || anchor == TextAnchor.CENTER
763                 || anchor == TextAnchor.BOTTOM_CENTER
764                 || anchor == TextAnchor.BASELINE_CENTER
765                 || anchor == TextAnchor.HALF_ASCENT_CENTER) {
766
767             xAdj = (float) bounds.getWidth() / 2.0f;
768
769         }
770         else if (anchor == TextAnchor.TOP_RIGHT
771                 || anchor == TextAnchor.CENTER_RIGHT
772                 || anchor == TextAnchor.BOTTOM_RIGHT
773                 || anchor == TextAnchor.BASELINE_RIGHT
774                 || anchor == TextAnchor.HALF_ASCENT_RIGHT) {
775
776             xAdj = (float) bounds.getWidth();
777
778         }
779
780         if (anchor == TextAnchor.TOP_LEFT
781                 || anchor == TextAnchor.TOP_CENTER
782                 || anchor == TextAnchor.TOP_RIGHT) {
783
784             yAdj = descent + leading - (float) bounds.getHeight();
785
786         }
787         else if (anchor == TextAnchor.CENTER_LEFT
788                 || anchor == TextAnchor.CENTER
789                 || anchor == TextAnchor.CENTER_RIGHT) {
790
791             yAdj = descent + leading - (float) (bounds.getHeight() / 2.0);
792
793         }
794         else if (anchor == TextAnchor.HALF_ASCENT_LEFT
795                 || anchor == TextAnchor.HALF_ASCENT_CENTER
796                 || anchor == TextAnchor.HALF_ASCENT_RIGHT) {
797
798             yAdj = -halfAscent;
799
800         }
801         else if (anchor == TextAnchor.BASELINE_LEFT
802                 || anchor == TextAnchor.BASELINE_CENTER
803                 || anchor == TextAnchor.BASELINE_RIGHT) {
804
805             yAdj = 0.0f;
806
807         }
808         else if (anchor == TextAnchor.BOTTOM_LEFT
809                 || anchor == TextAnchor.BOTTOM_CENTER
810                 || anchor == TextAnchor.BOTTOM_RIGHT) {
811
812             yAdj = metrics.getDescent() + metrics.getLeading();
813
814         }
815         result[0] = xAdj;
816         result[1] = yAdj;
817         return result;
818
819     }
820
821     /**
822      * Returns a shape that represents the bounds of the string after the
823      * specified rotation has been applied.
824      *
825      * @param text the text (<code>null</code> permitted).
826      * @param g2 the graphics device.
827      * @param textX the x coordinate for the text.
828      * @param textY the y coordinate for the text.
829      * @param angle the angle.
830      * @param rotateX the x coordinate for the rotation point.
831      * @param rotateY the y coordinate for the rotation point.
832      *
833      * @return The bounds (<code>null</code> if <code>text</code> is
834      * </code>null</code> or has zero length).
835      */

836     public static Shape JavaDoc calculateRotatedStringBounds(final String JavaDoc text,
837             final Graphics2D JavaDoc g2, final float textX, final float textY,
838             final double angle, final float rotateX, final float rotateY) {
839
840         if ((text == null) || (text.equals(""))) {
841             return null;
842         }
843         final FontMetrics JavaDoc fm = g2.getFontMetrics();
844         final Rectangle2D JavaDoc bounds = TextUtilities.getTextBounds(text, g2, fm);
845         final AffineTransform JavaDoc translate = AffineTransform.getTranslateInstance(
846                 textX, textY);
847         final Shape JavaDoc translatedBounds = translate.createTransformedShape(bounds);
848         final AffineTransform JavaDoc rotate = AffineTransform.getRotateInstance(
849                 angle, rotateX, rotateY);
850         final Shape JavaDoc result = rotate.createTransformedShape(translatedBounds);
851         return result;
852
853     }
854
855     /**
856      * Returns the flag that controls whether the FontMetrics.getStringBounds()
857      * method is used or not. If you are having trouble with label alignment
858      * or positioning, try changing the value of this flag.
859      *
860      * @return A boolean.
861      */

862     public static boolean getUseFontMetricsGetStringBounds() {
863         return useFontMetricsGetStringBounds;
864     }
865
866     /**
867      * Sets the flag that controls whether the FontMetrics.getStringBounds()
868      * method is used or not. If you are having trouble with label alignment
869      * or positioning, try changing the value of this flag.
870      *
871      * @param use the flag.
872      */

873     public static void setUseFontMetricsGetStringBounds(final boolean use) {
874         useFontMetricsGetStringBounds = use;
875     }
876
877     public static boolean isUseDrawRotatedStringWorkaround() {
878         return useDrawRotatedStringWorkaround;
879     }
880 }
881
Popular Tags