Source Code Cross Referenced for TextUtilities.java in  » Graphic-Library » jcommon-components » org » jfree » text » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Graphic Library » jcommon components » org.jfree.text 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


001:        /* ========================================================================
002:         * JCommon : a free general purpose class library for the Java(tm) platform
003:         * ========================================================================
004:         *
005:         * (C) Copyright 2000-2006, by Object Refinery Limited and Contributors.
006:         * 
007:         * Project Info:  http://www.jfree.org/jcommon/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:         * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 
025:         * in the United States and other countries.]
026:         * 
027:         * ------------------
028:         * TextUtilities.java
029:         * ------------------
030:         * (C) Copyright 2004-2006, by Object Refinery Limited and Contributors.
031:         *
032:         * Original Author:  David Gilbert (for Object Refinery Limited);
033:         * Contributor(s):   -;
034:         *
035:         * $Id: TextUtilities.java,v 1.21 2006/07/04 10:20:40 taqua Exp $
036:         *
037:         * Changes
038:         * -------
039:         * 07-Jan-2004 : Version 1 (DG);
040:         * 24-Mar-2004 : Added 'paint' argument to createTextBlock() method (DG);
041:         * 07-Apr-2004 : Added getTextBounds() method and useFontMetricsGetStringBounds
042:         *               flag (DG);
043:         * 08-Apr-2004 : Changed word break iterator to line break iterator in the 
044:         *               createTextBlock() method - see bug report 926074 (DG);
045:         * 03-Sep-2004 : Updated createTextBlock() method to add ellipses when limit 
046:         *               is reached (DG);
047:         * 30-Sep-2004 : Modified bounds returned by drawAlignedString() method (DG);
048:         * 10-Nov-2004 : Added new createTextBlock() method that works with 
049:         *               newlines (DG);
050:         * 19-Apr-2005 : Changed default value of useFontMetricsGetStringBounds (DG);
051:         * 17-May-2005 : createTextBlock() now recognises '\n' (DG);
052:         * 27-Jun-2005 : Added code to getTextBounds() method to work around Sun's bug 
053:         *               parade item 6183356 (DG);
054:         * 06-Jan-2006 : Reformatted (DG);
055:         * 
056:         */
057:
058:        package org.jfree.text;
059:
060:        import java.awt.Font;
061:        import java.awt.FontMetrics;
062:        import java.awt.Graphics2D;
063:        import java.awt.Paint;
064:        import java.awt.Shape;
065:        import java.awt.font.FontRenderContext;
066:        import java.awt.font.LineMetrics;
067:        import java.awt.font.TextLayout;
068:        import java.awt.geom.AffineTransform;
069:        import java.awt.geom.Rectangle2D;
070:        import java.text.BreakIterator;
071:
072:        import org.jfree.ui.TextAnchor;
073:        import org.jfree.util.Log;
074:        import org.jfree.util.LogContext;
075:        import org.jfree.util.ObjectUtilities;
076:        import org.jfree.base.BaseBoot;
077:
078:        /**
079:         * Some utility methods for working with text.
080:         *
081:         * @author David Gilbert
082:         */
083:        public class TextUtilities {
084:
085:            /** Access to logging facilities. */
086:            protected static final LogContext logger = Log
087:                    .createContext(TextUtilities.class);
088:
089:            /**
090:             * A flag that controls whether or not the rotated string workaround is
091:             * used.
092:             */
093:            private static boolean useDrawRotatedStringWorkaround;
094:
095:            /**
096:             * A flag that controls whether the FontMetrics.getStringBounds() method
097:             * is used or a workaround is applied.
098:             */
099:            private static boolean useFontMetricsGetStringBounds;
100:
101:            static {
102:                final boolean isJava14 = ObjectUtilities.isJDK14();
103:
104:                final String configRotatedStringWorkaround = BaseBoot
105:                        .getInstance()
106:                        .getGlobalConfig()
107:                        .getConfigProperty(
108:                                "org.jfree.text.UseDrawRotatedStringWorkaround",
109:                                "auto");
110:                if (configRotatedStringWorkaround.equals("auto")) {
111:                    useDrawRotatedStringWorkaround = (isJava14 == false);
112:                } else {
113:                    useDrawRotatedStringWorkaround = configRotatedStringWorkaround
114:                            .equals("true");
115:                }
116:
117:                final String configFontMetricsStringBounds = BaseBoot
118:                        .getInstance().getGlobalConfig().getConfigProperty(
119:                                "org.jfree.text.UseFontMetricsGetStringBounds",
120:                                "auto");
121:                if (configFontMetricsStringBounds.equals("auto")) {
122:                    useFontMetricsGetStringBounds = (isJava14 == true);
123:                } else {
124:                    useFontMetricsGetStringBounds = configFontMetricsStringBounds
125:                            .equals("true");
126:                }
127:            }
128:
129:            /**
130:             * Private constructor prevents object creation.
131:             */
132:            private TextUtilities() {
133:            }
134:
135:            /**
136:             * Creates a {@link TextBlock} from a <code>String</code>.  Line breaks 
137:             * are added where the <code>String</code> contains '\n' characters.
138:             * 
139:             * @param text  the text.
140:             * @param font  the font.
141:             * @param paint  the paint.
142:             * 
143:             * @return A text block.
144:             */
145:            public static TextBlock createTextBlock(final String text,
146:                    final Font font, final Paint paint) {
147:                if (text == null) {
148:                    throw new IllegalArgumentException("Null 'text' argument.");
149:                }
150:                final TextBlock result = new TextBlock();
151:                String input = text;
152:                boolean moreInputToProcess = (text.length() > 0);
153:                final int start = 0;
154:                while (moreInputToProcess) {
155:                    final int index = input.indexOf("\n");
156:                    if (index > start) {
157:                        final String line = input.substring(start, index);
158:                        if (index < input.length() - 1) {
159:                            result.addLine(line, font, paint);
160:                            input = input.substring(index + 1);
161:                        } else {
162:                            moreInputToProcess = false;
163:                        }
164:                    } else if (index == start) {
165:                        if (index < input.length() - 1) {
166:                            input = input.substring(index + 1);
167:                        } else {
168:                            moreInputToProcess = false;
169:                        }
170:                    } else {
171:                        result.addLine(input, font, paint);
172:                        moreInputToProcess = false;
173:                    }
174:                }
175:                return result;
176:            }
177:
178:            /**
179:             * Creates a new text block from the given string, breaking the
180:             * text into lines so that the <code>maxWidth</code> value is
181:             * respected.
182:             * 
183:             * @param text  the text.
184:             * @param font  the font.
185:             * @param paint  the paint.
186:             * @param maxWidth  the maximum width for each line.
187:             * @param measurer  the text measurer.
188:             * 
189:             * @return A text block.
190:             */
191:            public static TextBlock createTextBlock(final String text,
192:                    final Font font, final Paint paint, final float maxWidth,
193:                    final TextMeasurer measurer) {
194:
195:                return createTextBlock(text, font, paint, maxWidth,
196:                        Integer.MAX_VALUE, measurer);
197:            }
198:
199:            /**
200:             * Creates a new text block from the given string, breaking the
201:             * text into lines so that the <code>maxWidth</code> value is
202:             * respected.
203:             * 
204:             * @param text  the text.
205:             * @param font  the font.
206:             * @param paint  the paint.
207:             * @param maxWidth  the maximum width for each line.
208:             * @param maxLines  the maximum number of lines.
209:             * @param measurer  the text measurer.
210:             * 
211:             * @return A text block.
212:             */
213:            public static TextBlock createTextBlock(final String text,
214:                    final Font font, final Paint paint, final float maxWidth,
215:                    final int maxLines, final TextMeasurer measurer) {
216:
217:                final TextBlock result = new TextBlock();
218:                final BreakIterator iterator = BreakIterator.getLineInstance();
219:                iterator.setText(text);
220:                int current = 0;
221:                int lines = 0;
222:                final int length = text.length();
223:                while (current < length && lines < maxLines) {
224:                    final int next = nextLineBreak(text, current, maxWidth,
225:                            iterator, measurer);
226:                    if (next == BreakIterator.DONE) {
227:                        result.addLine(text.substring(current), font, paint);
228:                        return result;
229:                    }
230:                    result.addLine(text.substring(current, next), font, paint);
231:                    lines++;
232:                    current = next;
233:                    while (current < text.length()
234:                            && text.charAt(current) == '\n') {
235:                        current++;
236:                    }
237:                }
238:                if (current < length) {
239:                    final TextLine lastLine = result.getLastLine();
240:                    final TextFragment lastFragment = lastLine
241:                            .getLastTextFragment();
242:                    final String oldStr = lastFragment.getText();
243:                    String newStr = "...";
244:                    if (oldStr.length() > 3) {
245:                        newStr = oldStr.substring(0, oldStr.length() - 3)
246:                                + "...";
247:                    }
248:
249:                    lastLine.removeFragment(lastFragment);
250:                    final TextFragment newFragment = new TextFragment(newStr,
251:                            lastFragment.getFont(), lastFragment.getPaint());
252:                    lastLine.addFragment(newFragment);
253:                }
254:                return result;
255:            }
256:
257:            /**
258:             * Returns the character index of the next line break.
259:             * 
260:             * @param text  the text.
261:             * @param start  the start index.
262:             * @param width  the target display width.
263:             * @param iterator  the word break iterator.
264:             * @param measurer  the text measurer.
265:             * 
266:             * @return The index of the next line break.
267:             */
268:            private static int nextLineBreak(final String text,
269:                    final int start, final float width,
270:                    final BreakIterator iterator, final TextMeasurer measurer) {
271:
272:                // this method is (loosely) based on code in JFreeReport's 
273:                // TextParagraph class
274:                int current = start;
275:                int end;
276:                float x = 0.0f;
277:                boolean firstWord = true;
278:                int newline = text.indexOf('\n', start);
279:                if (newline < 0) {
280:                    newline = Integer.MAX_VALUE;
281:                }
282:                while (((end = iterator.next()) != BreakIterator.DONE)) {
283:                    if (end > newline) {
284:                        return newline;
285:                    }
286:                    x += measurer.getStringWidth(text, current, end);
287:                    if (x > width) {
288:                        if (firstWord) {
289:                            while (measurer.getStringWidth(text, start, end) > width) {
290:                                end--;
291:                                if (end <= start) {
292:                                    return end;
293:                                }
294:                            }
295:                            return end;
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 getTextBounds(final String text,
319:                    final Graphics2D g2, final FontMetrics fm) {
320:
321:                final Rectangle2D 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 lm = fm.getFont().getLineMetrics(text,
328:                            g2.getFontRenderContext());
329:                    bounds.setRect(bounds.getX(), bounds.getY(), bounds
330:                            .getWidth(), lm.getHeight());
331:                } else {
332:                    final double width = fm.stringWidth(text);
333:                    final double height = fm.getHeight();
334:                    if (logger.isDebugEnabled()) {
335:                        logger.debug("Height = " + height);
336:                    }
337:                    bounds = new Rectangle2D.Double(0.0, -fm.getAscent(),
338:                            width, height);
339:                }
340:                return bounds;
341:            }
342:
343:            /**
344:             * Draws a string such that the specified anchor point is aligned to the 
345:             * given (x, y) location.
346:             *
347:             * @param text  the text.
348:             * @param g2  the graphics device.
349:             * @param x  the x coordinate (Java 2D).
350:             * @param y  the y coordinate (Java 2D).
351:             * @param anchor  the anchor location.
352:             * 
353:             * @return The text bounds (adjusted for the text position).
354:             */
355:            public static Rectangle2D drawAlignedString(final String text,
356:                    final Graphics2D g2, final float x, final float y,
357:                    final TextAnchor anchor) {
358:
359:                final Rectangle2D textBounds = new Rectangle2D.Double();
360:                final float[] adjust = deriveTextBoundsAnchorOffsets(g2, text,
361:                        anchor, textBounds);
362:                // adjust text bounds to match string position
363:                textBounds.setRect(x + adjust[0], y + adjust[1] + adjust[2],
364:                        textBounds.getWidth(), textBounds.getHeight());
365:                g2.drawString(text, x + adjust[0], y + adjust[1]);
366:                return textBounds;
367:            }
368:
369:            /**
370:             * A utility method that calculates the anchor offsets for a string.  
371:             * Normally, the (x, y) coordinate for drawing text is a point on the 
372:             * baseline at the left of the text string.  If you add these offsets to 
373:             * (x, y) and draw the string, then the anchor point should coincide with 
374:             * the (x, y) point.
375:             *
376:             * @param g2  the graphics device (not <code>null</code>).
377:             * @param text  the text.
378:             * @param anchor  the anchor point.
379:             * @param textBounds  the text bounds (if not <code>null</code>, this 
380:             *                    object will be updated by this method to match the 
381:             *                    string bounds).
382:             * 
383:             * @return  The offsets.
384:             */
385:            private static float[] deriveTextBoundsAnchorOffsets(
386:                    final Graphics2D g2, final String text,
387:                    final TextAnchor anchor, final Rectangle2D textBounds) {
388:
389:                final float[] result = new float[3];
390:                final FontRenderContext frc = g2.getFontRenderContext();
391:                final Font f = g2.getFont();
392:                final FontMetrics fm = g2.getFontMetrics(f);
393:                final Rectangle2D bounds = TextUtilities.getTextBounds(text,
394:                        g2, fm);
395:                final LineMetrics 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:                } else if (anchor == TextAnchor.TOP_RIGHT
413:                        || anchor == TextAnchor.CENTER_RIGHT
414:                        || anchor == TextAnchor.BOTTOM_RIGHT
415:                        || anchor == TextAnchor.BASELINE_RIGHT
416:                        || anchor == TextAnchor.HALF_ASCENT_RIGHT) {
417:
418:                    xAdj = (float) -bounds.getWidth();
419:
420:                }
421:
422:                if (anchor == TextAnchor.TOP_LEFT
423:                        || anchor == TextAnchor.TOP_CENTER
424:                        || anchor == TextAnchor.TOP_RIGHT) {
425:
426:                    yAdj = -descent - leading + (float) bounds.getHeight();
427:
428:                } else if (anchor == TextAnchor.HALF_ASCENT_LEFT
429:                        || anchor == TextAnchor.HALF_ASCENT_CENTER
430:                        || anchor == TextAnchor.HALF_ASCENT_RIGHT) {
431:
432:                    yAdj = halfAscent;
433:
434:                } else if (anchor == TextAnchor.CENTER_LEFT
435:                        || anchor == TextAnchor.CENTER
436:                        || anchor == TextAnchor.CENTER_RIGHT) {
437:
438:                    yAdj = -descent - leading
439:                            + (float) (bounds.getHeight() / 2.0);
440:
441:                } else if (anchor == TextAnchor.BASELINE_LEFT
442:                        || anchor == TextAnchor.BASELINE_CENTER
443:                        || anchor == TextAnchor.BASELINE_RIGHT) {
444:
445:                    yAdj = 0.0f;
446:
447:                } else if (anchor == TextAnchor.BOTTOM_LEFT
448:                        || anchor == TextAnchor.BOTTOM_CENTER
449:                        || anchor == TextAnchor.BOTTOM_RIGHT) {
450:
451:                    yAdj = -metrics.getDescent() - metrics.getLeading();
452:
453:                }
454:                if (textBounds != null) {
455:                    textBounds.setRect(bounds);
456:                }
457:                result[0] = xAdj;
458:                result[1] = yAdj;
459:                return result;
460:
461:            }
462:
463:            /**
464:             * Sets the flag that controls whether or not a workaround is used for
465:             * drawing rotated strings.  The related bug is on Sun's bug parade 
466:             * (id 4312117) and the workaround involves using a <code>TextLayout</code> 
467:             * instance to draw the text instead of calling the 
468:             * <code>drawString()</code> method in the <code>Graphics2D</code> class.
469:             *
470:             * @param use  the new flag value.
471:             */
472:            public static void setUseDrawRotatedStringWorkaround(
473:                    final boolean use) {
474:                useDrawRotatedStringWorkaround = use;
475:            }
476:
477:            /**
478:             * A utility method for drawing rotated text.
479:             * <P>
480:             * A common rotation is -Math.PI/2 which draws text 'vertically' (with the 
481:             * top of the characters on the left).
482:             *
483:             * @param text  the text.
484:             * @param g2  the graphics device.
485:             * @param angle  the angle of the (clockwise) rotation (in radians).
486:             * @param x  the x-coordinate.
487:             * @param y  the y-coordinate.
488:             */
489:            public static void drawRotatedString(final String text,
490:                    final Graphics2D g2, final double angle, final float x,
491:                    final float y) {
492:                drawRotatedString(text, g2, x, y, angle, x, y);
493:            }
494:
495:            /**
496:             * A utility method for drawing rotated text.
497:             * <P>
498:             * A common rotation is -Math.PI/2 which draws text 'vertically' (with the 
499:             * top of the characters on the left).
500:             *
501:             * @param text  the text.
502:             * @param g2  the graphics device.
503:             * @param textX  the x-coordinate for the text (before rotation).
504:             * @param textY  the y-coordinate for the text (before rotation).
505:             * @param angle  the angle of the (clockwise) rotation (in radians).
506:             * @param rotateX  the point about which the text is rotated.
507:             * @param rotateY  the point about which the text is rotated.
508:             */
509:            public static void drawRotatedString(final String text,
510:                    final Graphics2D g2, final float textX, final float textY,
511:                    final double angle, final float rotateX, final float rotateY) {
512:
513:                if ((text == null) || (text.equals(""))) {
514:                    return;
515:                }
516:
517:                final AffineTransform saved = g2.getTransform();
518:
519:                // apply the rotation...
520:                final AffineTransform rotate = AffineTransform
521:                        .getRotateInstance(angle, rotateX, rotateY);
522:                g2.transform(rotate);
523:
524:                if (useDrawRotatedStringWorkaround) {
525:                    // workaround for JDC bug ID 4312117 and others...
526:                    final TextLayout tl = new TextLayout(text, g2.getFont(), g2
527:                            .getFontRenderContext());
528:                    tl.draw(g2, textX, textY);
529:                } else {
530:                    // replaces this code...
531:                    g2.drawString(text, textX, textY);
532:                }
533:                g2.setTransform(saved);
534:
535:            }
536:
537:            /**
538:             * Draws a string that is aligned by one anchor point and rotated about 
539:             * another anchor point.
540:             *
541:             * @param text  the text.
542:             * @param g2  the graphics device.
543:             * @param x  the x-coordinate for positioning the text.
544:             * @param y  the y-coordinate for positioning the text.
545:             * @param textAnchor  the text anchor.
546:             * @param angle  the rotation angle.
547:             * @param rotationX  the x-coordinate for the rotation anchor point.
548:             * @param rotationY  the y-coordinate for the rotation anchor point.
549:             */
550:            public static void drawRotatedString(final String text,
551:                    final Graphics2D g2, final float x, final float y,
552:                    final TextAnchor textAnchor, final double angle,
553:                    final float rotationX, final float rotationY) {
554:
555:                if (text == null || text.equals("")) {
556:                    return;
557:                }
558:                final float[] textAdj = deriveTextBoundsAnchorOffsets(g2, text,
559:                        textAnchor);
560:                drawRotatedString(text, g2, x + textAdj[0], y + textAdj[1],
561:                        angle, rotationX, rotationY);
562:            }
563:
564:            /**
565:             * Draws a string that is aligned by one anchor point and rotated about 
566:             * another anchor point.
567:             *
568:             * @param text  the text.
569:             * @param g2  the graphics device.
570:             * @param x  the x-coordinate for positioning the text.
571:             * @param y  the y-coordinate for positioning the text.
572:             * @param textAnchor  the text anchor.
573:             * @param angle  the rotation angle (in radians).
574:             * @param rotationAnchor  the rotation anchor.
575:             */
576:            public static void drawRotatedString(final String text,
577:                    final Graphics2D g2, final float x, final float y,
578:                    final TextAnchor textAnchor, final double angle,
579:                    final TextAnchor rotationAnchor) {
580:
581:                if (text == null || text.equals("")) {
582:                    return;
583:                }
584:                final float[] textAdj = deriveTextBoundsAnchorOffsets(g2, text,
585:                        textAnchor);
586:                final float[] rotateAdj = deriveRotationAnchorOffsets(g2, text,
587:                        rotationAnchor);
588:                drawRotatedString(text, g2, x + textAdj[0], y + textAdj[1],
589:                        angle, x + textAdj[0] + rotateAdj[0], y + textAdj[1]
590:                                + rotateAdj[1]);
591:
592:            }
593:
594:            /**
595:             * Returns a shape that represents the bounds of the string after the 
596:             * specified rotation has been applied.
597:             * 
598:             * @param text  the text (<code>null</code> permitted).
599:             * @param g2  the graphics device.
600:             * @param x  the x coordinate for the anchor point.
601:             * @param y  the y coordinate for the anchor point.
602:             * @param textAnchor  the text anchor.
603:             * @param angle  the angle.
604:             * @param rotationAnchor  the rotation anchor.
605:             * 
606:             * @return The bounds (possibly <code>null</code>).
607:             */
608:            public static Shape calculateRotatedStringBounds(final String text,
609:                    final Graphics2D g2, final float x, final float y,
610:                    final TextAnchor textAnchor, final double angle,
611:                    final TextAnchor rotationAnchor) {
612:
613:                if (text == null || text.equals("")) {
614:                    return null;
615:                }
616:                final float[] textAdj = deriveTextBoundsAnchorOffsets(g2, text,
617:                        textAnchor);
618:                if (logger.isDebugEnabled()) {
619:                    logger.debug("TextBoundsAnchorOffsets = " + textAdj[0]
620:                            + ", " + textAdj[1]);
621:                }
622:                final float[] rotateAdj = deriveRotationAnchorOffsets(g2, text,
623:                        rotationAnchor);
624:                if (logger.isDebugEnabled()) {
625:                    logger.debug("RotationAnchorOffsets = " + rotateAdj[0]
626:                            + ", " + rotateAdj[1]);
627:                }
628:                final Shape result = calculateRotatedStringBounds(text, g2, x
629:                        + textAdj[0], y + textAdj[1], angle, x + textAdj[0]
630:                        + rotateAdj[0], y + textAdj[1] + rotateAdj[1]);
631:                return result;
632:
633:            }
634:
635:            /**
636:             * A utility method that calculates the anchor offsets for a string.  
637:             * Normally, the (x, y) coordinate for drawing text is a point on the 
638:             * baseline at the left of the text string.  If you add these offsets to 
639:             * (x, y) and draw the string, then the anchor point should coincide with 
640:             * the (x, y) point.
641:             *
642:             * @param g2  the graphics device (not <code>null</code>).
643:             * @param text  the text.
644:             * @param anchor  the anchor point.
645:             *
646:             * @return  The offsets.
647:             */
648:            private static float[] deriveTextBoundsAnchorOffsets(
649:                    final Graphics2D g2, final String text,
650:                    final TextAnchor anchor) {
651:
652:                final float[] result = new float[2];
653:                final FontRenderContext frc = g2.getFontRenderContext();
654:                final Font f = g2.getFont();
655:                final FontMetrics fm = g2.getFontMetrics(f);
656:                final Rectangle2D bounds = TextUtilities.getTextBounds(text,
657:                        g2, fm);
658:                final LineMetrics 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:                } else if (anchor == TextAnchor.TOP_RIGHT
675:                        || anchor == TextAnchor.CENTER_RIGHT
676:                        || anchor == TextAnchor.BOTTOM_RIGHT
677:                        || anchor == TextAnchor.BASELINE_RIGHT
678:                        || anchor == TextAnchor.HALF_ASCENT_RIGHT) {
679:
680:                    xAdj = (float) -bounds.getWidth();
681:
682:                }
683:
684:                if (anchor == TextAnchor.TOP_LEFT
685:                        || anchor == TextAnchor.TOP_CENTER
686:                        || anchor == TextAnchor.TOP_RIGHT) {
687:
688:                    yAdj = -descent - leading + (float) bounds.getHeight();
689:
690:                } else if (anchor == TextAnchor.HALF_ASCENT_LEFT
691:                        || anchor == TextAnchor.HALF_ASCENT_CENTER
692:                        || anchor == TextAnchor.HALF_ASCENT_RIGHT) {
693:
694:                    yAdj = halfAscent;
695:
696:                } else if (anchor == TextAnchor.CENTER_LEFT
697:                        || anchor == TextAnchor.CENTER
698:                        || anchor == TextAnchor.CENTER_RIGHT) {
699:
700:                    yAdj = -descent - leading
701:                            + (float) (bounds.getHeight() / 2.0);
702:
703:                } else if (anchor == TextAnchor.BASELINE_LEFT
704:                        || anchor == TextAnchor.BASELINE_CENTER
705:                        || anchor == TextAnchor.BASELINE_RIGHT) {
706:
707:                    yAdj = 0.0f;
708:
709:                } else if (anchor == TextAnchor.BOTTOM_LEFT
710:                        || anchor == TextAnchor.BOTTOM_CENTER
711:                        || anchor == TextAnchor.BOTTOM_RIGHT) {
712:
713:                    yAdj = -metrics.getDescent() - metrics.getLeading();
714:
715:                }
716:                result[0] = xAdj;
717:                result[1] = yAdj;
718:                return result;
719:
720:            }
721:
722:            /**
723:             * A utility method that calculates the rotation anchor offsets for a 
724:             * string.  These offsets are relative to the text starting coordinate 
725:             * (BASELINE_LEFT).
726:             *
727:             * @param g2  the graphics device.
728:             * @param text  the text.
729:             * @param anchor  the anchor point.
730:             *
731:             * @return  The offsets.
732:             */
733:            private static float[] deriveRotationAnchorOffsets(
734:                    final Graphics2D g2, final String text,
735:                    final TextAnchor anchor) {
736:
737:                final float[] result = new float[2];
738:                final FontRenderContext frc = g2.getFontRenderContext();
739:                final LineMetrics metrics = g2.getFont().getLineMetrics(text,
740:                        frc);
741:                final FontMetrics fm = g2.getFontMetrics();
742:                final Rectangle2D bounds = TextUtilities.getTextBounds(text,
743:                        g2, fm);
744:                final float ascent = metrics.getAscent();
745:                final float halfAscent = ascent / 2.0f;
746:                final float descent = metrics.getDescent();
747:                final float leading = metrics.getLeading();
748:                float xAdj = 0.0f;
749:                float yAdj = 0.0f;
750:
751:                if (anchor == TextAnchor.TOP_LEFT
752:                        || anchor == TextAnchor.CENTER_LEFT
753:                        || anchor == TextAnchor.BOTTOM_LEFT
754:                        || anchor == TextAnchor.BASELINE_LEFT
755:                        || anchor == TextAnchor.HALF_ASCENT_LEFT) {
756:
757:                    xAdj = 0.0f;
758:
759:                } else if (anchor == TextAnchor.TOP_CENTER
760:                        || anchor == TextAnchor.CENTER
761:                        || anchor == TextAnchor.BOTTOM_CENTER
762:                        || anchor == TextAnchor.BASELINE_CENTER
763:                        || anchor == TextAnchor.HALF_ASCENT_CENTER) {
764:
765:                    xAdj = (float) bounds.getWidth() / 2.0f;
766:
767:                } else if (anchor == TextAnchor.TOP_RIGHT
768:                        || anchor == TextAnchor.CENTER_RIGHT
769:                        || anchor == TextAnchor.BOTTOM_RIGHT
770:                        || anchor == TextAnchor.BASELINE_RIGHT
771:                        || anchor == TextAnchor.HALF_ASCENT_RIGHT) {
772:
773:                    xAdj = (float) bounds.getWidth();
774:
775:                }
776:
777:                if (anchor == TextAnchor.TOP_LEFT
778:                        || anchor == TextAnchor.TOP_CENTER
779:                        || anchor == TextAnchor.TOP_RIGHT) {
780:
781:                    yAdj = descent + leading - (float) bounds.getHeight();
782:
783:                } else if (anchor == TextAnchor.CENTER_LEFT
784:                        || anchor == TextAnchor.CENTER
785:                        || anchor == TextAnchor.CENTER_RIGHT) {
786:
787:                    yAdj = descent + leading
788:                            - (float) (bounds.getHeight() / 2.0);
789:
790:                } else if (anchor == TextAnchor.HALF_ASCENT_LEFT
791:                        || anchor == TextAnchor.HALF_ASCENT_CENTER
792:                        || anchor == TextAnchor.HALF_ASCENT_RIGHT) {
793:
794:                    yAdj = -halfAscent;
795:
796:                } else if (anchor == TextAnchor.BASELINE_LEFT
797:                        || anchor == TextAnchor.BASELINE_CENTER
798:                        || anchor == TextAnchor.BASELINE_RIGHT) {
799:
800:                    yAdj = 0.0f;
801:
802:                } else if (anchor == TextAnchor.BOTTOM_LEFT
803:                        || anchor == TextAnchor.BOTTOM_CENTER
804:                        || anchor == TextAnchor.BOTTOM_RIGHT) {
805:
806:                    yAdj = metrics.getDescent() + metrics.getLeading();
807:
808:                }
809:                result[0] = xAdj;
810:                result[1] = yAdj;
811:                return result;
812:
813:            }
814:
815:            /**
816:             * Returns a shape that represents the bounds of the string after the 
817:             * specified rotation has been applied.
818:             * 
819:             * @param text  the text (<code>null</code> permitted).
820:             * @param g2  the graphics device.
821:             * @param textX  the x coordinate for the text.
822:             * @param textY  the y coordinate for the text.
823:             * @param angle  the angle.
824:             * @param rotateX  the x coordinate for the rotation point.
825:             * @param rotateY  the y coordinate for the rotation point.
826:             * 
827:             * @return The bounds (<code>null</code> if <code>text</code> is 
828:             *         </code>null</code> or has zero length).
829:             */
830:            public static Shape calculateRotatedStringBounds(final String text,
831:                    final Graphics2D g2, final float textX, final float textY,
832:                    final double angle, final float rotateX, final float rotateY) {
833:
834:                if ((text == null) || (text.equals(""))) {
835:                    return null;
836:                }
837:                final FontMetrics fm = g2.getFontMetrics();
838:                final Rectangle2D bounds = TextUtilities.getTextBounds(text,
839:                        g2, fm);
840:                final AffineTransform translate = AffineTransform
841:                        .getTranslateInstance(textX, textY);
842:                final Shape translatedBounds = translate
843:                        .createTransformedShape(bounds);
844:                final AffineTransform rotate = AffineTransform
845:                        .getRotateInstance(angle, rotateX, rotateY);
846:                final Shape result = rotate
847:                        .createTransformedShape(translatedBounds);
848:                return result;
849:
850:            }
851:
852:            /**
853:             * Returns the flag that controls whether the FontMetrics.getStringBounds() 
854:             * method is used or not.  If you are having trouble with label alignment
855:             * or positioning, try changing the value of this flag.
856:             * 
857:             * @return A boolean.
858:             */
859:            public static boolean getUseFontMetricsGetStringBounds() {
860:                return useFontMetricsGetStringBounds;
861:            }
862:
863:            /**
864:             * Sets the flag that controls whether the FontMetrics.getStringBounds() 
865:             * method is used or not.  If you are having trouble with label alignment 
866:             * or positioning, try changing the value of this flag.
867:             * 
868:             * @param use  the flag.
869:             */
870:            public static void setUseFontMetricsGetStringBounds(
871:                    final boolean use) {
872:                useFontMetricsGetStringBounds = use;
873:            }
874:
875:            public static boolean isUseDrawRotatedStringWorkaround() {
876:                return useDrawRotatedStringWorkaround;
877:            }
878:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.