Source Code Cross Referenced for GlyphLayout.java in  » Graphic-Library » batik » org » apache » batik » gvt » 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 » batik » org.apache.batik.gvt.text 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:
0003:           Licensed to the Apache Software Foundation (ASF) under one or more
0004:           contributor license agreements.  See the NOTICE file distributed with
0005:           this work for additional information regarding copyright ownership.
0006:           The ASF licenses this file to You under the Apache License, Version 2.0
0007:           (the "License"); you may not use this file except in compliance with
0008:           the License.  You may obtain a copy of the License at
0009:
0010:               http://www.apache.org/licenses/LICENSE-2.0
0011:
0012:           Unless required by applicable law or agreed to in writing, software
0013:           distributed under the License is distributed on an "AS IS" BASIS,
0014:           WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0015:           See the License for the specific language governing permissions and
0016:           limitations under the License.
0017:
0018:         */
0019:        package org.apache.batik.gvt.text;
0020:
0021:        import java.awt.BasicStroke;
0022:        import java.awt.Graphics2D;
0023:        import java.awt.Shape;
0024:        import java.awt.Stroke;
0025:        import java.awt.font.FontRenderContext;
0026:        import java.awt.font.TextAttribute;
0027:        import java.awt.geom.AffineTransform;
0028:        import java.awt.geom.Area;
0029:        import java.awt.geom.GeneralPath;
0030:        import java.awt.geom.PathIterator;
0031:        import java.awt.geom.Point2D;
0032:        import java.awt.geom.Rectangle2D;
0033:        import java.awt.geom.Line2D;
0034:        import java.text.AttributedCharacterIterator;
0035:        import java.text.CharacterIterator;
0036:        import java.util.HashSet;
0037:        import java.util.Set;
0038:
0039:        import org.apache.batik.gvt.font.AWTGVTFont;
0040:        import org.apache.batik.gvt.font.AltGlyphHandler;
0041:        import org.apache.batik.gvt.font.GVTFont;
0042:        import org.apache.batik.gvt.font.GVTGlyphMetrics;
0043:        import org.apache.batik.gvt.font.GVTGlyphVector;
0044:        import org.apache.batik.gvt.font.GVTLineMetrics;
0045:
0046:        /**
0047:         * Implementation of TextSpanLayout which uses java.awt.font.GlyphVector.
0048:         * @see org.apache.batik.gvt.text.TextSpanLayout
0049:         *
0050:         * @author <a href="mailto:bill.haneman@ireland.sun.com">Bill Haneman</a>
0051:         * @version $Id: GlyphLayout.java 501922 2007-01-31 17:47:47Z dvholten $
0052:         */
0053:        public class GlyphLayout implements  TextSpanLayout {
0054:
0055:            private GVTGlyphVector gv;
0056:            private GVTFont font;
0057:            private GVTLineMetrics metrics;
0058:            private AttributedCharacterIterator aci;
0059:            private Point2D advance;
0060:            private Point2D offset;
0061:            private float xScale = 1;
0062:            private float yScale = 1;
0063:            private TextPath textPath;
0064:            private Point2D textPathAdvance;
0065:            private int[] charMap;
0066:            private boolean vertical, adjSpacing = true;
0067:            private float[] glyphAdvances;
0068:            private boolean isAltGlyph; //false
0069:
0070:            // When layoutApplied is false it means that the glyph positions
0071:            // are different from where they would be if you did
0072:            // doExplicitGlyphLayout().
0073:            private boolean layoutApplied = false;
0074:            // When spacingApplied is false it means that xScale, yScale and
0075:            // kerning/wordspacing stuff haven't been applied. This can
0076:            // be rectified by calling adjustTextSpacing().  Note that when
0077:            // spacing is actually used layoutApplied will be cleared it
0078:            // is not garunteed that applying text spacing will cause it to
0079:            // be cleared (it will only be cleared if the glyphs move).
0080:            private boolean spacingApplied = false;
0081:            // When pathApplied is false it means that the text has not been
0082:            // layed out on the associated text path (if any).  If there is an
0083:            // associated text path then this will clear both layoutApplied
0084:            // and spacing applied but neither will be touched if no text path
0085:            // is present.
0086:            private boolean pathApplied = false;
0087:
0088:            public static final AttributedCharacterIterator.Attribute FLOW_LINE_BREAK = GVTAttributedCharacterIterator.TextAttribute.FLOW_LINE_BREAK;
0089:
0090:            public static final AttributedCharacterIterator.Attribute FLOW_PARAGRAPH = GVTAttributedCharacterIterator.TextAttribute.FLOW_PARAGRAPH;
0091:
0092:            public static final AttributedCharacterIterator.Attribute FLOW_EMPTY_PARAGRAPH = GVTAttributedCharacterIterator.TextAttribute.FLOW_EMPTY_PARAGRAPH;
0093:
0094:            public static final AttributedCharacterIterator.Attribute LINE_HEIGHT = GVTAttributedCharacterIterator.TextAttribute.LINE_HEIGHT;
0095:
0096:            public static final AttributedCharacterIterator.Attribute VERTICAL_ORIENTATION = GVTAttributedCharacterIterator.TextAttribute.VERTICAL_ORIENTATION;
0097:
0098:            public static final AttributedCharacterIterator.Attribute VERTICAL_ORIENTATION_ANGLE = GVTAttributedCharacterIterator.TextAttribute.VERTICAL_ORIENTATION_ANGLE;
0099:
0100:            public static final AttributedCharacterIterator.Attribute HORIZONTAL_ORIENTATION_ANGLE = GVTAttributedCharacterIterator.TextAttribute.HORIZONTAL_ORIENTATION_ANGLE;
0101:
0102:            private static final AttributedCharacterIterator.Attribute X = GVTAttributedCharacterIterator.TextAttribute.X;
0103:
0104:            private static final AttributedCharacterIterator.Attribute Y = GVTAttributedCharacterIterator.TextAttribute.Y;
0105:
0106:            private static final AttributedCharacterIterator.Attribute DX = GVTAttributedCharacterIterator.TextAttribute.DX;
0107:
0108:            private static final AttributedCharacterIterator.Attribute DY = GVTAttributedCharacterIterator.TextAttribute.DY;
0109:
0110:            private static final AttributedCharacterIterator.Attribute ROTATION = GVTAttributedCharacterIterator.TextAttribute.ROTATION;
0111:
0112:            private static final AttributedCharacterIterator.Attribute BASELINE_SHIFT = GVTAttributedCharacterIterator.TextAttribute.BASELINE_SHIFT;
0113:
0114:            private static final AttributedCharacterIterator.Attribute WRITING_MODE = GVTAttributedCharacterIterator.TextAttribute.WRITING_MODE;
0115:
0116:            private static final Integer WRITING_MODE_TTB = GVTAttributedCharacterIterator.TextAttribute.WRITING_MODE_TTB;
0117:
0118:            private static final Integer ORIENTATION_AUTO = GVTAttributedCharacterIterator.TextAttribute.ORIENTATION_AUTO;
0119:
0120:            public static final AttributedCharacterIterator.Attribute GVT_FONT = GVTAttributedCharacterIterator.TextAttribute.GVT_FONT;
0121:
0122:            protected static Set runAtts = new HashSet();
0123:
0124:            static {
0125:                runAtts.add(X);
0126:                runAtts.add(Y);
0127:                runAtts.add(DX);
0128:                runAtts.add(DY);
0129:                runAtts.add(ROTATION);
0130:                runAtts.add(BASELINE_SHIFT);
0131:            }
0132:
0133:            protected static Set szAtts = new HashSet();
0134:
0135:            static {
0136:                szAtts.add(TextAttribute.SIZE);
0137:                szAtts.add(GVT_FONT);
0138:                szAtts.add(LINE_HEIGHT);
0139:            }
0140:
0141:            /**
0142:             * Creates the specified text layout using the
0143:             * specified AttributedCharacterIterator and rendering context.
0144:             *
0145:             * @param aci the AttributedCharacterIterator whose text is to
0146:             *  be laid out
0147:             * @param charMap Indicates how chars in aci map to original
0148:             *                text char array.
0149:             * @param offset The offset position of this text layout
0150:             * @param frc the FontRenderContext to use for generating glyphs.
0151:             */
0152:            public GlyphLayout(AttributedCharacterIterator aci, int[] charMap,
0153:                    Point2D offset, FontRenderContext frc) {
0154:
0155:                this .aci = aci;
0156:                this .offset = offset;
0157:                this .font = getFont();
0158:                this .charMap = charMap;
0159:
0160:                this .metrics = font.getLineMetrics(aci, aci.getBeginIndex(),
0161:                        aci.getEndIndex(), frc);
0162:
0163:                // create the glyph vector
0164:                this .gv = null;
0165:                this .aci.first();
0166:                this .vertical = (aci.getAttribute(WRITING_MODE) == WRITING_MODE_TTB);
0167:                this .textPath = (TextPath) aci
0168:                        .getAttribute(GVTAttributedCharacterIterator.TextAttribute.TEXTPATH);
0169:
0170:                AltGlyphHandler altGlyphHandler = (AltGlyphHandler) this .aci
0171:                        .getAttribute(GVTAttributedCharacterIterator.TextAttribute.ALT_GLYPH_HANDLER);
0172:                if (altGlyphHandler != null) {
0173:                    // this must be an altGlyph text element, try and create
0174:                    // the alternate glyphs
0175:                    this .gv = altGlyphHandler.createGlyphVector(frc, this .font
0176:                            .getSize(), this .aci);
0177:                    if (this .gv != null) {
0178:                        this .isAltGlyph = true;
0179:                    }
0180:                }
0181:                if (this .gv == null) {
0182:                    // either not an altGlyph or the altGlyphHandler failed to
0183:                    // create a glyph vector
0184:                    this .gv = font.createGlyphVector(frc, this .aci);
0185:                }
0186:            }
0187:
0188:            public GVTGlyphVector getGlyphVector() {
0189:                return this .gv;
0190:            }
0191:
0192:            /**
0193:             * Returns the current text position at the beginning
0194:             * of glyph layout, before the application of explicit
0195:             * glyph positioning attributes.
0196:             */
0197:            public Point2D getOffset() {
0198:                return offset;
0199:            }
0200:
0201:            /**
0202:             * Sets the scaling factor to use for string.  if ajdSpacing is
0203:             * true then only the spacing between glyphs will be adjusted
0204:             * otherwise the glyphs and the spaces between them will be
0205:             * adjusted.  Only the scale factor in the progression direction
0206:             * is used (x for horizontal text, y for vertical text
0207:             * ).
0208:             * @param xScale Scale factor to apply in X direction.
0209:             * @param yScale Scale factor to apply in Y direction.
0210:             * @param adjSpacing True if only spaces should be adjusted.
0211:             */
0212:            public void setScale(float xScale, float yScale, boolean adjSpacing) {
0213:                // Fix the off axis scale factor.
0214:                if (vertical)
0215:                    xScale = 1;
0216:                else
0217:                    yScale = 1;
0218:
0219:                if ((xScale != this .xScale) || (yScale != this .yScale)
0220:                        || (adjSpacing != this .adjSpacing)) {
0221:                    this .xScale = xScale;
0222:                    this .yScale = yScale;
0223:                    this .adjSpacing = adjSpacing;
0224:
0225:                    // We don't affect layoutApplied directly...
0226:                    // System.out.println("layoutApplied: " + layoutApplied);
0227:
0228:                    // However if we did path layout or spacing it's all junk now...
0229:                    spacingApplied = false;
0230:                    glyphAdvances = null;
0231:                    pathApplied = false;
0232:                }
0233:            }
0234:
0235:            /**
0236:             * Sets the text position used for the implicit origin
0237:             * of glyph layout. Ignored if multiple explicit glyph
0238:             * positioning attributes are present in ACI
0239:             * (e.g. if the aci has multiple X or Y values).
0240:             */
0241:            public void setOffset(Point2D offset) {
0242:                // System.err.println("SetOffset: " + offset + " - " + this.offset);
0243:                if ((offset.getX() != this .offset.getX())
0244:                        || (offset.getY() != this .offset.getY())) {
0245:                    if ((layoutApplied) || (spacingApplied)) {
0246:                        // Already layed out need to shift glyph positions to
0247:                        // account for new offset.
0248:                        float dx = (float) (offset.getX() - this .offset.getX());
0249:                        float dy = (float) (offset.getY() - this .offset.getY());
0250:                        int numGlyphs = gv.getNumGlyphs();
0251:
0252:                        // System.out.println("DXY: [" + dx +","+dy+"]");
0253:                        float[] gp = gv.getGlyphPositions(0, numGlyphs + 1,
0254:                                null);
0255:                        Point2D.Float pos = new Point2D.Float();
0256:                        for (int i = 0; i <= numGlyphs; i++) {
0257:                            pos.x = gp[2 * i] + dx;
0258:                            pos.y = gp[2 * i + 1] + dy;
0259:                            gv.setGlyphPosition(i, pos);
0260:                        }
0261:                    }
0262:
0263:                    // When not layed out (or after updating) just set the new
0264:                    // offset this will be factored in for any future layout
0265:                    // operations.
0266:                    this .offset = offset;
0267:
0268:                    // We don't affect layoutApplied or spacingApplied since
0269:                    // they both work off the offset value.
0270:
0271:                    // However if we did path layout it's all junk now...
0272:                    pathApplied = false;
0273:                }
0274:            }
0275:
0276:            public GVTGlyphMetrics getGlyphMetrics(int glyphIndex) {
0277:                return gv.getGlyphMetrics(glyphIndex);
0278:            }
0279:
0280:            public GVTLineMetrics getLineMetrics() {
0281:                return metrics;
0282:            }
0283:
0284:            /**
0285:             * Returns true if the advance direction of this text is vertical.
0286:             */
0287:            public boolean isVertical() {
0288:                return vertical;
0289:            }
0290:
0291:            /**
0292:             * Returns true if this layout in on a text path.
0293:             */
0294:            public boolean isOnATextPath() {
0295:                return (textPath != null);
0296:            }
0297:
0298:            /**
0299:             * Returns the number of glyphs in this layout.
0300:             */
0301:            public int getGlyphCount() {
0302:                return gv.getNumGlyphs();
0303:            }
0304:
0305:            /**
0306:             * Returns the number of chars represented by the glyphs within the
0307:             * specified range.
0308:             *
0309:             * @param startGlyphIndex The index of the first glyph in the range.
0310:             * @param endGlyphIndex The index of the last glyph in the range.
0311:             *
0312:             * @return The number of chars.
0313:             */
0314:            public int getCharacterCount(int startGlyphIndex, int endGlyphIndex) {
0315:                return gv.getCharacterCount(startGlyphIndex, endGlyphIndex);
0316:            }
0317:
0318:            /**
0319:             * Returns true if the text direction in this layout is from left to right.
0320:             */
0321:            public boolean isLeftToRight() {
0322:                aci.first();
0323:                int bidiLevel = ((Integer) aci
0324:                        .getAttribute(GVTAttributedCharacterIterator.TextAttribute.BIDI_LEVEL))
0325:                        .intValue();
0326:
0327:                // Check if low bit is set if not then we are left to right
0328:                // (even bidi level).
0329:                return ((bidiLevel & 0x01) == 0);
0330:            }
0331:
0332:            /**
0333:             * This method makes certain that the layout has been
0334:             * completed at this point (much of the layout is done lazily).
0335:             */
0336:            private final void syncLayout() {
0337:                if (!pathApplied) {
0338:                    // System.out.println("Doing Path Layout: " + this);
0339:                    doPathLayout();
0340:                }
0341:            }
0342:
0343:            /**
0344:             * Paints the text layout using the
0345:             * specified Graphics2D and rendering context.
0346:             * @param g2d the Graphics2D to use
0347:             */
0348:            public void draw(Graphics2D g2d) {
0349:                syncLayout();
0350:                gv.draw(g2d, aci);
0351:            }
0352:
0353:            /**
0354:             * Returns the current text position at the completion
0355:             * of glyph layout.
0356:             */
0357:            public Point2D getAdvance2D() {
0358:                adjustTextSpacing();
0359:                return advance;
0360:            }
0361:
0362:            /**
0363:             * Returns the outline of the completed glyph layout.
0364:             */
0365:            public Shape getOutline() {
0366:                syncLayout();
0367:
0368:                return gv.getOutline();
0369:            }
0370:
0371:            public float[] getGlyphAdvances() {
0372:                if (glyphAdvances != null)
0373:                    return glyphAdvances;
0374:
0375:                if (!spacingApplied)
0376:                    // This will layout the text if needed.
0377:                    adjustTextSpacing();
0378:
0379:                int numGlyphs = gv.getNumGlyphs();
0380:                float[] glyphPos = gv.getGlyphPositions(0, numGlyphs + 1, null);
0381:                glyphAdvances = new float[numGlyphs + 1];
0382:                int off = 0;
0383:                if (isVertical())
0384:                    off = 1;
0385:
0386:                float start = glyphPos[off];
0387:                for (int i = 0; i < numGlyphs + 1; i++) {
0388:                    glyphAdvances[i] = glyphPos[2 * i + off] - start;
0389:                }
0390:                return glyphAdvances;
0391:            }
0392:
0393:            /**
0394:             * Returns the outline of the specified decorations on the glyphs,
0395:             * @param decorationType an integer indicating the type(s) of decorations
0396:             *     included in this shape.  May be the result of "OR-ing" several
0397:             *     values together:
0398:             * e.g. <tt>DECORATION_UNDERLINE | DECORATION_STRIKETHROUGH</tt>
0399:             */
0400:            public Shape getDecorationOutline(int decorationType) {
0401:                syncLayout();
0402:
0403:                Shape g = new GeneralPath();
0404:                if ((decorationType & DECORATION_UNDERLINE) != 0) {
0405:                    ((GeneralPath) g).append(getUnderlineShape(), false);
0406:                }
0407:                if ((decorationType & DECORATION_STRIKETHROUGH) != 0) {
0408:                    ((GeneralPath) g).append(getStrikethroughShape(), false);
0409:                }
0410:                if ((decorationType & DECORATION_OVERLINE) != 0) {
0411:                    ((GeneralPath) g).append(getOverlineShape(), false);
0412:                }
0413:                return g;
0414:            }
0415:
0416:            /**
0417:             * Returns the rectangular bounds of the completed glyph layout.
0418:             */
0419:            public Rectangle2D getBounds2D() {
0420:                syncLayout();
0421:                return gv.getBounds2D(aci);
0422:            }
0423:
0424:            /**
0425:             * Returns the rectangular bounds of the completed glyph layout,
0426:             * inclusive of "decoration" (underline, overline, etc.)
0427:             */
0428:            public Rectangle2D getGeometricBounds() {
0429:                syncLayout();
0430:                Rectangle2D gvB, decB;
0431:                gvB = gv.getGeometricBounds();
0432:                decB = getDecorationOutline(DECORATION_ALL).getBounds2D();
0433:                return gvB.createUnion(decB);
0434:            }
0435:
0436:            /**
0437:             * Returns the position to used when drawing a text run after this one.
0438:             * It takes into account the text path layout if there is one.
0439:             */
0440:            public Point2D getTextPathAdvance() {
0441:                syncLayout();
0442:                if (textPath != null) {
0443:                    return textPathAdvance;
0444:                } else {
0445:                    return getAdvance2D();
0446:                }
0447:            }
0448:
0449:            /**
0450:             * Returns the index of the first glyph that has the specified char index.
0451:             *
0452:             * @param charIndex The original index of the character in the text node's
0453:             * text string.
0454:             * @return The index of the matching glyph in this layout's glyph vector,
0455:             *         or -1 if a matching glyph could not be found.
0456:             */
0457:            public int getGlyphIndex(int charIndex) {
0458:                int numGlyphs = getGlyphCount();
0459:                int j = 0;
0460:                for (int i = 0; i < numGlyphs; i++) {
0461:                    int count = getCharacterCount(i, i);
0462:                    for (int n = 0; n < count; n++) {
0463:                        int glyphCharIndex = charMap[j++];
0464:                        if (charIndex == glyphCharIndex)
0465:                            return i;
0466:                        if (j >= charMap.length)
0467:                            return -1;
0468:                    }
0469:                }
0470:                return -1;
0471:            }
0472:
0473:            /**
0474:             * Returns the index of the last glyph that has the specified char index.
0475:             *
0476:             * @param charIndex The original index of the character in the text node's
0477:             * text string.
0478:             * @return The index of the matching glyph in this layout's glyph vector,
0479:             *         or -1 if a matching glyph could not be found.
0480:             */
0481:            public int getLastGlyphIndex(int charIndex) {
0482:                int numGlyphs = getGlyphCount();
0483:                int j = charMap.length - 1;
0484:                for (int i = numGlyphs - 1; i >= 0; --i) {
0485:                    int count = getCharacterCount(i, i);
0486:                    for (int n = 0; n < count; n++) {
0487:                        int glyphCharIndex = charMap[j--];
0488:                        if (charIndex == glyphCharIndex)
0489:                            return i;
0490:                        if (j < 0)
0491:                            return -1;
0492:                    }
0493:                }
0494:                return -1;
0495:            }
0496:
0497:            /**
0498:             * Return the angle value according to the orientation
0499:             * of the character.
0500:             */
0501:            public double getComputedOrientationAngle(int index) {
0502:
0503:                if (isGlyphOrientationAuto()) {
0504:                    if (isVertical()) {
0505:                        char ch = aci.setIndex(index);
0506:                        if (isLatinChar(ch))
0507:                            return 90.0;
0508:                        else
0509:                            return 0.0;
0510:                    }
0511:                    return 0.0;
0512:                } else {
0513:                    return getGlyphOrientationAngle();
0514:                }
0515:            }
0516:
0517:            /**
0518:             * Returns a Shape which encloses the currently selected glyphs
0519:             * as specified by the character indices.
0520:             *
0521:             * @param beginCharIndex the index of the first char in the
0522:             * contiguous selection.
0523:             * @param endCharIndex the index of the last char in the
0524:             * contiguous selection.
0525:             * @return The highlight shape or null if the spacified char range
0526:             * does not overlap with the chars in this layout.  */
0527:            public Shape getHighlightShape(int beginCharIndex, int endCharIndex) {
0528:                syncLayout();
0529:
0530:                if (beginCharIndex > endCharIndex) {
0531:                    int temp = beginCharIndex;
0532:                    beginCharIndex = endCharIndex;
0533:                    endCharIndex = temp;
0534:                }
0535:                GeneralPath shape = null;
0536:                int numGlyphs = getGlyphCount();
0537:
0538:                Point2D.Float[] topPts = new Point2D.Float[2 * numGlyphs];
0539:                Point2D.Float[] botPts = new Point2D.Float[2 * numGlyphs];
0540:
0541:                int ptIdx = 0;
0542:
0543:                int currentChar = 0;
0544:                for (int i = 0; i < numGlyphs; i++) {
0545:                    int glyphCharIndex = charMap[currentChar];
0546:                    if ((glyphCharIndex >= beginCharIndex)
0547:                            && (glyphCharIndex <= endCharIndex)
0548:                            && gv.isGlyphVisible(i)) {
0549:
0550:                        Shape gbounds = gv.getGlyphLogicalBounds(i);
0551:                        if (gbounds != null) {
0552:                            // We got something...
0553:                            if (shape == null)
0554:                                shape = new GeneralPath();
0555:
0556:                            // We are pretty dumb here we assume that we always
0557:                            // get back polygons with four sides to them if
0558:                            // isn't met we are SOL.
0559:                            float[] pts = new float[6];
0560:                            int count = 0;
0561:                            int type = -1;
0562:
0563:                            PathIterator pi = gbounds.getPathIterator(null);
0564:                            Point2D.Float firstPt = null;
0565:
0566:                            while (!pi.isDone()) {
0567:                                type = pi.currentSegment(pts);
0568:                                if ((type == PathIterator.SEG_MOVETO)
0569:                                        || (type == PathIterator.SEG_LINETO)) {
0570:                                    // LINETO or MOVETO
0571:                                    if (count > 4)
0572:                                        break; // too many lines...
0573:                                    if (count == 4) {
0574:                                        // make sure we are just closing it..
0575:                                        if ((firstPt == null)
0576:                                                || (firstPt.x != pts[0])
0577:                                                || (firstPt.y != pts[1]))
0578:                                            break;
0579:                                    } else {
0580:                                        Point2D.Float pt;
0581:                                        pt = new Point2D.Float(pts[0], pts[1]);
0582:                                        if (count == 0)
0583:                                            firstPt = pt;
0584:                                        // Use sides of  rectangle...
0585:                                        switch (count) {
0586:                                        case 0:
0587:                                            botPts[ptIdx] = pt;
0588:                                            break;
0589:                                        case 1:
0590:                                            topPts[ptIdx] = pt;
0591:                                            break;
0592:                                        case 2:
0593:                                            topPts[ptIdx + 1] = pt;
0594:                                            break;
0595:                                        case 3:
0596:                                            botPts[ptIdx + 1] = pt;
0597:                                            break;
0598:                                        }
0599:                                    }
0600:                                } else if (type == PathIterator.SEG_CLOSE) {
0601:                                    // Close in the wrong spot?
0602:                                    if ((count < 4) || (count > 5))
0603:                                        break;
0604:                                } else {
0605:                                    // QUADTO or CUBETO
0606:                                    break;
0607:                                }
0608:
0609:                                count++;
0610:                                pi.next();
0611:                            }
0612:                            if (pi.isDone()) {
0613:                                // Sucessfully Expressed as a quadralateral...
0614:                                if ((botPts[ptIdx] != null)
0615:                                        && ((topPts[ptIdx].x != topPts[ptIdx + 1].x) || (topPts[ptIdx].y != topPts[ptIdx + 1].y)))
0616:                                    // box isn't empty so use it's points...
0617:                                    ptIdx += 2;
0618:                            } else {
0619:                                // System.out.println("Type: " + type +
0620:                                //                    " count: " + count);
0621:                                // Wasn't a quadralateral so just add it don't try
0622:                                // and merge it...
0623:                                addPtsToPath(shape, topPts, botPts, ptIdx);
0624:                                ptIdx = 0;
0625:                                shape.append(gbounds, false);
0626:                            }
0627:                        }
0628:                    }
0629:                    currentChar += getCharacterCount(i, i);
0630:                    if (currentChar >= charMap.length)
0631:                        currentChar = charMap.length - 1;
0632:                }
0633:                addPtsToPath(shape, topPts, botPts, ptIdx);
0634:
0635:                return shape;
0636:            }
0637:
0638:            public static final double eps = 0.00001;
0639:
0640:            public static boolean epsEQ(double a, double b) {
0641:                return ((a + eps > b) && (a - eps < b));
0642:            }
0643:
0644:            public static int makeConvexHull(Point2D.Float[] pts, int numPts) {
0645:                // Sort the Pts in X...
0646:                Point2D.Float tmp;
0647:                // System.out.print("Sorting...");
0648:                for (int i = 1; i < numPts; i++) {
0649:                    // Simple bubble sort (numPts should be small so shouldn't
0650:                    // be too bad.).
0651:                    if ((pts[i].x < pts[i - 1].x)
0652:                            || ((pts[i].x == pts[i - 1].x) && (pts[i].y < pts[i - 1].y))) {
0653:                        tmp = pts[i];
0654:                        pts[i] = pts[i - 1];
0655:                        pts[i - 1] = tmp;
0656:                        i = 0;
0657:                        continue;
0658:                    }
0659:                }
0660:
0661:                // System.out.println("Sorted");
0662:
0663:                Point2D.Float pt0 = pts[0];
0664:                Point2D.Float pt1 = pts[numPts - 1];
0665:                Point2D.Float dxdy = new Point2D.Float(pt1.x - pt0.x, pt1.y
0666:                        - pt0.y);
0667:                float soln, c = dxdy.y * pt0.x - dxdy.x * pt0.y;
0668:
0669:                Point2D.Float[] topList = new Point2D.Float[numPts];
0670:                Point2D.Float[] botList = new Point2D.Float[numPts];
0671:                botList[0] = topList[0] = pts[0];
0672:                int nTopPts = 1;
0673:                int nBotPts = 1;
0674:                for (int i = 1; i < numPts - 1; i++) {
0675:                    Point2D.Float pt = pts[i];
0676:                    soln = dxdy.x * pt.y - dxdy.y * pt.x + c;
0677:                    if (soln < 0) {
0678:                        // Below line goes into bot pt list...
0679:                        while (nBotPts >= 2) {
0680:                            pt0 = botList[nBotPts - 2];
0681:                            pt1 = botList[nBotPts - 1];
0682:                            float dx = pt1.x - pt0.x;
0683:                            float dy = pt1.y - pt0.y;
0684:                            float c0 = dy * pt0.x - dx * pt0.y;
0685:                            soln = dx * pt.y - dy * pt.x + c0;
0686:                            if (soln > eps) // Left turn add and we are done..
0687:                                break;
0688:                            if (soln > -eps) {
0689:                                // On line take lowest Y of two and keep going
0690:                                if (pt1.y < pt.y)
0691:                                    pt = pt1;
0692:                                nBotPts--;
0693:                                break;
0694:                            }
0695:                            // right turn drop prev pt;
0696:                            nBotPts--;
0697:                        }
0698:                        botList[nBotPts++] = pt;
0699:                    } else {
0700:                        // Above line goes into top pt list...
0701:                        while (nTopPts >= 2) {
0702:                            pt0 = topList[nTopPts - 2];
0703:                            pt1 = topList[nTopPts - 1];
0704:                            float dx = pt1.x - pt0.x;
0705:                            float dy = pt1.y - pt0.y;
0706:                            float c0 = dy * pt0.x - dx * pt0.y;
0707:                            soln = dx * pt.y - dy * pt.x + c0;
0708:                            if (soln < -eps) // Right turn add and check next point.
0709:                                break;
0710:                            if (soln < eps) {
0711:                                // On line take greatest Y of two and keep going
0712:                                if (pt1.y > pt.y)
0713:                                    pt = pt1;
0714:                                nTopPts--;
0715:                                break;
0716:                            }
0717:                            // left turn drop prev pt;
0718:                            nTopPts--;
0719:                        }
0720:                        topList[nTopPts++] = pt;
0721:                    }
0722:                }
0723:
0724:                // Check last point in both sets...
0725:                Point2D.Float pt = pts[numPts - 1];
0726:                while (nBotPts >= 2) {
0727:                    pt0 = botList[nBotPts - 2];
0728:                    pt1 = botList[nBotPts - 1];
0729:                    float dx = pt1.x - pt0.x;
0730:                    float dy = pt1.y - pt0.y;
0731:                    float c0 = dy * pt0.x - dx * pt0.y;
0732:                    soln = dx * pt.y - dy * pt.x + c0;
0733:                    if (soln > eps)
0734:                        // Left turn add and we are done..
0735:                        break;
0736:                    if (soln > -eps) {
0737:                        // On line take lowest Y of two and keep going
0738:                        if (pt1.y >= pt.y)
0739:                            nBotPts--;
0740:                        break;
0741:                    }
0742:                    // right turn drop prev pt;
0743:                    nBotPts--;
0744:                }
0745:
0746:                while (nTopPts >= 2) {
0747:                    pt0 = topList[nTopPts - 2];
0748:                    pt1 = topList[nTopPts - 1];
0749:                    float dx = pt1.x - pt0.x;
0750:                    float dy = pt1.y - pt0.y;
0751:                    float c0 = dy * pt0.x - dx * pt0.y;
0752:                    soln = dx * pt.y - dy * pt.x + c0;
0753:                    if (soln < -eps)
0754:                        // Right turn done...
0755:                        break;
0756:                    if (soln < eps) {
0757:                        // On line take lowest Y of two and keep going
0758:                        if (pt1.y <= pt.y)
0759:                            nTopPts--;
0760:                        break;
0761:                    }
0762:                    // left turn drop prev pt;
0763:                    nTopPts--;
0764:                }
0765:
0766:                System.arraycopy(topList, 0, pts, 0, nTopPts);
0767:                int i = nTopPts;
0768:
0769:                // We always include the 'last' point as it is always on convex hull.
0770:                pts[i++] = pts[numPts - 1];
0771:
0772:                // don't include botList[0] since it is the same as topList[0].
0773:                for (int n = nBotPts - 1; n > 0; n--, i++)
0774:                    pts[i] = botList[n];
0775:
0776:                // System.out.println("CHull has " + i + " pts");
0777:                return i;
0778:            }
0779:
0780:            public static void addPtsToPath(GeneralPath shape,
0781:                    Point2D.Float[] topPts, Point2D.Float[] botPts, int numPts) {
0782:                if (numPts < 2)
0783:                    return;
0784:                if (numPts == 2) {
0785:                    shape.moveTo(topPts[0].x, topPts[0].y);
0786:                    shape.lineTo(topPts[1].x, topPts[1].y);
0787:                    shape.lineTo(botPts[1].x, botPts[1].y);
0788:                    shape.lineTo(botPts[0].x, botPts[0].y);
0789:                    shape.lineTo(topPts[0].x, topPts[0].y);
0790:                    return;
0791:                }
0792:
0793:                // Here we 'connect the dots' the best way we know how...
0794:                // What I do is construct a convex hull between adjacent
0795:                // character boxes, then I union that into the shape.  this
0796:                // does a good job of bridging between adjacent characters,
0797:                // but still closely tracking to text boxes.  The use of the
0798:                // Area class is fairly heavy weight but it seems to keep up
0799:                // in this instanace (probably because all the shapes are very
0800:                // simple polygons).
0801:                Point2D.Float[] boxes = new Point2D.Float[8];
0802:                Point2D.Float[] chull = new Point2D.Float[8];
0803:                boxes[4] = topPts[0];
0804:                boxes[5] = topPts[1];
0805:                boxes[6] = botPts[1];
0806:                boxes[7] = botPts[0];
0807:                Area[] areas = new Area[numPts / 2];
0808:                int nAreas = 0;
0809:                for (int i = 2; i < numPts; i += 2) {
0810:                    boxes[0] = boxes[4];
0811:                    boxes[1] = boxes[5];
0812:                    boxes[2] = boxes[6];
0813:                    boxes[3] = boxes[7];
0814:                    boxes[4] = topPts[i];
0815:                    boxes[5] = topPts[i + 1];
0816:                    boxes[6] = botPts[i + 1];
0817:                    boxes[7] = botPts[i];
0818:
0819:                    float delta, sz, dist;
0820:                    delta = boxes[2].x - boxes[0].x;
0821:                    dist = delta * delta;
0822:                    delta = boxes[2].y - boxes[0].y;
0823:                    dist += delta * delta;
0824:                    sz = (float) Math.sqrt(dist);
0825:
0826:                    delta = boxes[6].x - boxes[4].x;
0827:                    dist = delta * delta;
0828:                    delta = boxes[6].y - boxes[4].y;
0829:                    dist += delta * delta;
0830:                    sz += (float) Math.sqrt(dist);
0831:
0832:                    delta = ((boxes[0].x + boxes[1].x + boxes[2].x + boxes[3].x) - (boxes[4].x
0833:                            + boxes[5].x + boxes[6].x + boxes[7].x)) / 4;
0834:                    dist = delta * delta;
0835:                    delta = ((boxes[0].y + boxes[1].y + boxes[2].y + boxes[3].y) - (boxes[4].y
0836:                            + boxes[5].y + boxes[6].y + boxes[7].y)) / 4;
0837:                    dist += delta * delta;
0838:                    dist = (float) Math.sqrt(dist);
0839:                    // Note here that dist is the distance between center
0840:                    // points, and sz is the sum of the length of the
0841:                    // diagonals of the letter boxes.  In normal cases one
0842:                    // would expect dist to be approximately equal to sz/2.
0843:                    // So here we merge if the two characters are within four
0844:                    // character widths of each other. If they are farther
0845:                    // apart than that chances are it's a 'line break' or
0846:                    // something similar where we will get better results
0847:                    // merging seperately, and anyways with this much space
0848:                    // between them the extra outline shouldn't hurt..
0849:                    GeneralPath gp = new GeneralPath();
0850:                    if (dist < sz) {
0851:                        // Close enough to merge with previous char...
0852:                        System.arraycopy(boxes, 0, chull, 0, 8);
0853:                        int npts = makeConvexHull(chull, 8);
0854:                        gp.moveTo(chull[0].x, chull[0].y);
0855:                        for (int n = 1; n < npts; n++)
0856:                            gp.lineTo(chull[n].x, chull[n].y);
0857:                        gp.closePath();
0858:                    } else {
0859:                        // Merge all previous areas
0860:                        mergeAreas(shape, areas, nAreas);
0861:                        nAreas = 0; // Start fresh...
0862:
0863:                        // Then just add box (add the previous char box if first pts)
0864:                        if (i == 2) {
0865:                            gp.moveTo(boxes[0].x, boxes[0].y);
0866:                            gp.lineTo(boxes[1].x, boxes[1].y);
0867:                            gp.lineTo(boxes[2].x, boxes[2].y);
0868:                            gp.lineTo(boxes[3].x, boxes[3].y);
0869:                            gp.closePath();
0870:                            shape.append(gp, false);
0871:                            gp.reset();
0872:                        }
0873:                        gp.moveTo(boxes[4].x, boxes[4].y);
0874:                        gp.lineTo(boxes[5].x, boxes[5].y);
0875:                        gp.lineTo(boxes[6].x, boxes[6].y);
0876:                        gp.lineTo(boxes[7].x, boxes[7].y);
0877:                        gp.closePath();
0878:                    }
0879:                    areas[nAreas++] = new Area(gp);
0880:                }
0881:
0882:                mergeAreas(shape, areas, nAreas);
0883:            }
0884:
0885:            public static void mergeAreas(GeneralPath shape, Area[] shapes,
0886:                    int nShapes) {
0887:                // Merge areas hierarchically, this means that while there are
0888:                // the same number of Area.add calls (n-1) the great majority
0889:                // of them are very simple combinations.  This helps to speed
0890:                // things up a tad...
0891:                while (nShapes > 1) {
0892:                    int n = 0;
0893:                    for (int i = 1; i < nShapes; i += 2) {
0894:                        shapes[i - 1].add(shapes[i]);
0895:                        shapes[n++] = shapes[i - 1];
0896:                        shapes[i] = null;
0897:                    }
0898:
0899:                    // make sure we include the last one if odd.
0900:                    if ((nShapes & 0x1) == 1)
0901:                        shapes[n - 1].add(shapes[nShapes - 1]);
0902:                    nShapes = nShapes / 2;
0903:                }
0904:                if (nShapes == 1)
0905:                    shape.append(shapes[0], false);
0906:            }
0907:
0908:            /**
0909:             * Perform hit testing for coordinate at x, y.
0910:             *
0911:             * @param x the x coordinate of the point to be tested.
0912:             * @param y the y coordinate of the point to be tested.
0913:             *
0914:             * @return a TextHit object encapsulating the character index for
0915:             *     successful hits and whether the hit is on the character
0916:             *     leading edge.
0917:             */
0918:            public TextHit hitTestChar(float x, float y) {
0919:                syncLayout();
0920:
0921:                TextHit textHit = null;
0922:
0923:                int currentChar = 0;
0924:                for (int i = 0; i < gv.getNumGlyphs(); i++) {
0925:                    Shape gbounds = gv.getGlyphLogicalBounds(i);
0926:                    if (gbounds != null) {
0927:                        Rectangle2D gbounds2d = gbounds.getBounds2D();
0928:                        // System.out.println("Hit Test: [" + x + ", " + y + "] - " +
0929:                        //                    gbounds2d);
0930:                        if (gbounds.contains(x, y)) {
0931:                            boolean isRightHalf = (x > (gbounds2d.getX() + (gbounds2d
0932:                                    .getWidth() / 2d)));
0933:                            boolean isLeadingEdge = !isRightHalf;
0934:                            int charIndex = charMap[currentChar];
0935:                            textHit = new TextHit(charIndex, isLeadingEdge);
0936:                            return textHit;
0937:                        }
0938:                    }
0939:                    currentChar += getCharacterCount(i, i);
0940:                    if (currentChar >= charMap.length)
0941:                        currentChar = charMap.length - 1;
0942:                }
0943:                return textHit;
0944:            }
0945:
0946:            //protected
0947:
0948:            /**
0949:             * Returns the GVTFont to use when rendering the specified
0950:             * character iterator.  This should already be set as an attribute
0951:             * on the aci.
0952:             *
0953:             * @return The GVTFont to use.
0954:             */
0955:            protected GVTFont getFont() {
0956:                aci.first();
0957:                GVTFont gvtFont = (GVTFont) aci.getAttribute(GVT_FONT);
0958:
0959:                if (gvtFont != null)
0960:                    return gvtFont;
0961:
0962:                // shouldn't get here
0963:                return new AWTGVTFont(aci.getAttributes());
0964:            }
0965:
0966:            /**
0967:             * Returns a shape describing the overline decoration for a given ACI.
0968:             */
0969:            protected Shape getOverlineShape() {
0970:                double y = metrics.getOverlineOffset();
0971:                float overlineThickness = metrics.getOverlineThickness();
0972:
0973:                // need to move the overline a bit lower,
0974:                // not sure if this is correct behaviour or not
0975:                y += overlineThickness;
0976:
0977:                // Not certain what should be done here...
0978:                aci.first();
0979:                Float dy = (Float) aci.getAttribute(DY);
0980:                if (dy != null)
0981:                    y += dy.floatValue();
0982:
0983:                Stroke overlineStroke = new BasicStroke(overlineThickness);
0984:                Rectangle2D logicalBounds = gv.getLogicalBounds();
0985:
0986:                return overlineStroke.createStrokedShape(new Line2D.Double(
0987:                        logicalBounds.getMinX() + overlineThickness / 2.0,
0988:                        offset.getY() + y, logicalBounds.getMaxX()
0989:                                - overlineThickness / 2.0, offset.getY() + y));
0990:            }
0991:
0992:            /**
0993:             * Returns a shape describing the undeline decoration for a given ACI.
0994:             */
0995:            protected Shape getUnderlineShape() {
0996:
0997:                double y = metrics.getUnderlineOffset();
0998:                float underlineThickness = metrics.getUnderlineThickness();
0999:
1000:                // need to move the underline a bit lower,
1001:                // not sure if this is correct behaviour or not
1002:                y += underlineThickness * 1.5;
1003:
1004:                BasicStroke underlineStroke = new BasicStroke(
1005:                        underlineThickness);
1006:
1007:                // Not certain what should be done here...
1008:                aci.first();
1009:                Float dy = (Float) aci.getAttribute(DY);
1010:                if (dy != null)
1011:                    y += dy.floatValue();
1012:
1013:                Rectangle2D logicalBounds = gv.getLogicalBounds();
1014:
1015:                return underlineStroke.createStrokedShape(new Line2D.Double(
1016:                        logicalBounds.getMinX() + underlineThickness / 2.0,
1017:                        offset.getY() + y, logicalBounds.getMaxX()
1018:                                - underlineThickness / 2.0, offset.getY() + y));
1019:            }
1020:
1021:            /**
1022:             * Returns a shape describing the strikethrough line for a given ACI.
1023:             */
1024:            protected Shape getStrikethroughShape() {
1025:                double y = metrics.getStrikethroughOffset();
1026:                float strikethroughThickness = metrics
1027:                        .getStrikethroughThickness();
1028:
1029:                Stroke strikethroughStroke = new BasicStroke(
1030:                        strikethroughThickness);
1031:
1032:                // Not certain what should be done here...
1033:                aci.first();
1034:                Float dy = (Float) aci.getAttribute(DY);
1035:                if (dy != null)
1036:                    y += dy.floatValue();
1037:
1038:                Rectangle2D logicalBounds = gv.getLogicalBounds();
1039:                return strikethroughStroke
1040:                        .createStrokedShape(new Line2D.Double(logicalBounds
1041:                                .getMinX()
1042:                                + strikethroughThickness / 2.0, offset.getY()
1043:                                + y, logicalBounds.getMaxX()
1044:                                - strikethroughThickness / 2.0, offset.getY()
1045:                                + y));
1046:            }
1047:
1048:            /**
1049:             * Explicitly lays out each of the glyphs in the glyph
1050:             * vector. This will handle any glyph position adjustments such as
1051:             * dx, dy and baseline offsets.  It will also handle vertical
1052:             * layouts.
1053:             */
1054:            protected void doExplicitGlyphLayout() {
1055:
1056:                this .gv.performDefaultLayout();
1057:
1058:                float baselineAscent = vertical ? (float) gv.getLogicalBounds()
1059:                        .getWidth() : (metrics.getAscent() + Math.abs(metrics
1060:                        .getDescent()));
1061:
1062:                int numGlyphs = gv.getNumGlyphs();
1063:                // System.out.println("NumGlyphs: " + numGlyphs);
1064:
1065:                float[] gp = gv.getGlyphPositions(0, numGlyphs + 1, null);
1066:                float verticalFirstOffset = 0f;
1067:                float horizontalFirstOffset = 0f;
1068:
1069:                boolean glyphOrientationAuto = isGlyphOrientationAuto();
1070:                int glyphOrientationAngle = 0;
1071:                if (!glyphOrientationAuto) {
1072:                    glyphOrientationAngle = getGlyphOrientationAngle();
1073:                }
1074:                int i = 0;
1075:                int aciStart = aci.getBeginIndex();
1076:                int aciIndex = 0;
1077:                char ch = aci.first();
1078:                int runLimit = aciIndex + aciStart;
1079:
1080:                Float x = null, y = null, dx = null, dy = null, rotation = null;
1081:                Object baseline = null;
1082:
1083:                float shift_x_pos = 0;
1084:                float shift_y_pos = 0;
1085:                float curr_x_pos = (float) offset.getX();
1086:                float curr_y_pos = (float) offset.getY();
1087:
1088:                Point2D.Float pos = new Point2D.Float();
1089:                boolean hasArabicTransparent = false;
1090:
1091:                while (i < numGlyphs) {
1092:                    //System.out.println("limit: " + runLimit + ", " + aciIndex);
1093:                    if (aciIndex + aciStart >= runLimit) {
1094:                        runLimit = aci.getRunLimit(runAtts);
1095:                        x = (Float) aci.getAttribute(X);
1096:                        y = (Float) aci.getAttribute(Y);
1097:                        dx = (Float) aci.getAttribute(DX);
1098:                        dy = (Float) aci.getAttribute(DY);
1099:                        rotation = (Float) aci.getAttribute(ROTATION);
1100:                        baseline = aci.getAttribute(BASELINE_SHIFT);
1101:                    }
1102:
1103:                    GVTGlyphMetrics gm = gv.getGlyphMetrics(i);
1104:
1105:                    if (i == 0) {
1106:                        if (isVertical()) {
1107:                            if (glyphOrientationAuto) {
1108:                                if (isLatinChar(ch)) {
1109:                                    // it will be rotated 90
1110:                                    verticalFirstOffset = 0f;
1111:                                } else {
1112:                                    // it won't be rotated
1113:                                    float advY = gm.getVerticalAdvance();
1114:                                    float asc = metrics.getAscent();
1115:                                    float dsc = metrics.getDescent();
1116:                                    verticalFirstOffset = asc
1117:                                            + (advY - (asc + dsc)) / 2;
1118:                                }
1119:                            } else {
1120:                                if (glyphOrientationAngle == 0) {
1121:                                    float advY = gm.getVerticalAdvance();
1122:                                    float asc = metrics.getAscent();
1123:                                    float dsc = metrics.getDescent();
1124:                                    verticalFirstOffset = asc
1125:                                            + (advY - (asc + dsc)) / 2;
1126:                                } else {
1127:                                    // 90, 180, 270
1128:                                    verticalFirstOffset = 0f;
1129:                                }
1130:                            }
1131:                        } else { // not vertical
1132:                            if ((glyphOrientationAngle == 270)) {
1133:                                horizontalFirstOffset = (float) gm
1134:                                        .getBounds2D().getHeight();
1135:                            } else {
1136:                                // 0, 90, 180
1137:                                horizontalFirstOffset = 0;
1138:                            }
1139:                        }
1140:                    } else { // not the first char
1141:                        if (glyphOrientationAuto && (verticalFirstOffset == 0f)
1142:                                && !isLatinChar(ch)) {
1143:                            float advY = gm.getVerticalAdvance();
1144:                            float asc = metrics.getAscent();
1145:                            float dsc = metrics.getDescent();
1146:                            verticalFirstOffset = asc + (advY - (asc + dsc))
1147:                                    / 2;
1148:                        }
1149:                    }
1150:
1151:                    // ox and oy are origin adjustments for each glyph,
1152:                    // computed on the basis of baseline-shifts, etc.
1153:                    float ox = 0f;
1154:                    float oy = 0f;
1155:                    float glyphOrientationRotation = 0f;
1156:                    float glyphRotation = 0f;
1157:
1158:                    if (ch != CharacterIterator.DONE) {
1159:                        if (vertical) {
1160:                            if (glyphOrientationAuto) {
1161:                                if (isLatinChar(ch)) {
1162:                                    // If character is Latin, then rotate by
1163:                                    // 90 degrees
1164:                                    glyphOrientationRotation = (float) (Math.PI / 2f);
1165:                                } else {
1166:                                    glyphOrientationRotation = 0f;
1167:                                }
1168:                            } else {
1169:                                glyphOrientationRotation = (float) Math
1170:                                        .toRadians(glyphOrientationAngle);
1171:                            }
1172:                            if (textPath != null) {
1173:                                // if vertical and on a path, any x's are ignored
1174:                                x = null;
1175:                            }
1176:                        } else {
1177:                            glyphOrientationRotation = (float) Math
1178:                                    .toRadians(glyphOrientationAngle);
1179:                            if (textPath != null) {
1180:                                // if horizontal and on a path, any y's are ignored
1181:                                y = null;
1182:                            }
1183:                        }
1184:
1185:                        // calculate the total rotation for this glyph
1186:                        if (rotation == null || rotation.isNaN()) {
1187:                            glyphRotation = glyphOrientationRotation;
1188:                        } else {
1189:                            glyphRotation = (rotation.floatValue() + glyphOrientationRotation);
1190:                        }
1191:
1192:                        if ((x != null) && !x.isNaN()) {
1193:                            if (i == 0)
1194:                                shift_x_pos = (float) (x.floatValue() - offset
1195:                                        .getX());
1196:                            curr_x_pos = x.floatValue() - shift_x_pos;
1197:                        }
1198:                        if (dx != null && !dx.isNaN()) {
1199:                            curr_x_pos += dx.floatValue();
1200:                        }
1201:
1202:                        if ((y != null) && !y.isNaN()) {
1203:                            if (i == 0)
1204:                                shift_y_pos = (float) (y.floatValue() - offset
1205:                                        .getY());
1206:                            curr_y_pos = y.floatValue() - shift_y_pos;
1207:                        }
1208:                        if (dy != null && !dy.isNaN()) {
1209:                            curr_y_pos += dy.floatValue();
1210:                        } else if (i > 0) {
1211:                            curr_y_pos += gp[i * 2 + 1] - gp[i * 2 - 1];
1212:                        }
1213:
1214:                        float baselineAdjust = 0f;
1215:                        if (baseline != null) {
1216:                            if (baseline instanceof  Integer) {
1217:                                if (baseline == TextAttribute.SUPERSCRIPT_SUPER) {
1218:                                    baselineAdjust = baselineAscent * 0.5f;
1219:                                } else if (baseline == TextAttribute.SUPERSCRIPT_SUB) {
1220:                                    baselineAdjust = -baselineAscent * 0.5f;
1221:                                }
1222:                            } else if (baseline instanceof  Float) {
1223:                                baselineAdjust = ((Float) baseline)
1224:                                        .floatValue();
1225:                            }
1226:                            if (vertical) {
1227:                                ox = baselineAdjust;
1228:                            } else {
1229:                                oy = -baselineAdjust;
1230:                            }
1231:                        }
1232:
1233:                        if (vertical) {
1234:                            // offset due to rotation of first character
1235:                            oy += verticalFirstOffset;
1236:
1237:                            if (glyphOrientationAuto) {
1238:                                if (isLatinChar(ch)) {
1239:                                    ox += metrics.getStrikethroughOffset();
1240:                                } else {
1241:                                    Rectangle2D glyphBounds = gv
1242:                                            .getGlyphVisualBounds(i)
1243:                                            .getBounds2D();
1244:                                    ox -= (float) ((glyphBounds.getMaxX() - gp[2 * i]) - glyphBounds
1245:                                            .getWidth() / 2);
1246:                                }
1247:                            } else {
1248:                                // center the character if it's not auto orient
1249:                                Rectangle2D glyphBounds = gv
1250:                                        .getGlyphVisualBounds(i).getBounds2D();
1251:                                if (glyphOrientationAngle == 0) {
1252:                                    ox -= (float) ((glyphBounds.getMaxX() - gp[2 * i]) - glyphBounds
1253:                                            .getWidth() / 2);
1254:                                } else if (glyphOrientationAngle == 180) {
1255:                                    ox += (float) ((glyphBounds.getMaxX() - gp[2 * i]) - glyphBounds
1256:                                            .getWidth() / 2);
1257:                                } else if (glyphOrientationAngle == 90) {
1258:                                    ox += metrics.getStrikethroughOffset();
1259:                                } else { // 270
1260:                                    ox -= metrics.getStrikethroughOffset();
1261:                                }
1262:                            }
1263:                        } else {
1264:                            ox += horizontalFirstOffset;
1265:                            if (glyphOrientationAngle == 90) {
1266:                                oy -= gm.getHorizontalAdvance();
1267:                            } else if (glyphOrientationAngle == 180) {
1268:                                oy -= metrics.getAscent();
1269:                            }
1270:                        }
1271:                    }
1272:
1273:                    // set the new glyph position
1274:                    pos.x = curr_x_pos + ox;
1275:                    pos.y = curr_y_pos + oy;
1276:                    gv.setGlyphPosition(i, pos);
1277:
1278:                    // calculate the position of the next glyph
1279:                    if (ArabicTextHandler.arabicCharTransparent(ch)) {
1280:                        hasArabicTransparent = true;
1281:                    } else {
1282:                        // Apply the advance if the current char is not transparent
1283:                        if (vertical) {
1284:                            float advanceY = 0;
1285:                            if (glyphOrientationAuto) {
1286:                                if (isLatinChar(ch)) {
1287:                                    advanceY = gm.getHorizontalAdvance();
1288:                                } else {
1289:                                    advanceY = gm.getVerticalAdvance();
1290:                                }
1291:                            } else {
1292:                                if ((glyphOrientationAngle == 0)
1293:                                        || (glyphOrientationAngle == 180)) {
1294:                                    advanceY = gm.getVerticalAdvance();
1295:                                } else if (glyphOrientationAngle == 90) {
1296:                                    advanceY = gm.getHorizontalAdvance();
1297:                                } else { // 270
1298:                                    advanceY = gm.getHorizontalAdvance();
1299:                                    // need to translate so that the spacing
1300:                                    // between chars is correct
1301:                                    gv.setGlyphTransform(i, AffineTransform
1302:                                            .getTranslateInstance(0, advanceY));
1303:                                }
1304:                            }
1305:                            curr_y_pos += advanceY;
1306:                        } else {
1307:                            float advanceX = 0;
1308:                            if (glyphOrientationAngle == 0) {
1309:                                advanceX = gm.getHorizontalAdvance();
1310:                            } else if (glyphOrientationAngle == 180) {
1311:                                advanceX = gm.getHorizontalAdvance();
1312:                                // need to translate so that the spacing
1313:                                // between chars is correct
1314:                                gv.setGlyphTransform(i, AffineTransform
1315:                                        .getTranslateInstance(advanceX, 0));
1316:                            } else {
1317:                                // 90, 270
1318:                                advanceX = gm.getVerticalAdvance();
1319:                            }
1320:                            curr_x_pos += advanceX;
1321:                        }
1322:                    }
1323:
1324:                    // rotate the glyph
1325:                    if (!epsEQ(glyphRotation, 0)) {
1326:                        AffineTransform glyphTransform = gv
1327:                                .getGlyphTransform(i);
1328:                        if (glyphTransform == null) {
1329:                            glyphTransform = new AffineTransform();
1330:                        }
1331:                        AffineTransform rotAt;
1332:                        // Make the 90Deg rotations slightly 'snap to'.
1333:                        // Also use explicit matrix to avoid round-off.
1334:                        if (epsEQ(glyphRotation, Math.PI / 2)) {
1335:                            rotAt = new AffineTransform(0, 1, -1, 0, 0, 0);
1336:                        } else if (epsEQ(glyphRotation, Math.PI)) {
1337:                            rotAt = new AffineTransform(-1, 0, 0, -1, 0, 0);
1338:                        } else if (epsEQ(glyphRotation, 3 * Math.PI / 2)) {
1339:                            rotAt = new AffineTransform(0, -1, 1, 0, 0, 0);
1340:                        } else {
1341:                            rotAt = AffineTransform
1342:                                    .getRotateInstance(glyphRotation);
1343:                        }
1344:                        glyphTransform.concatenate(rotAt);
1345:                        gv.setGlyphTransform(i, glyphTransform);
1346:                    }
1347:
1348:                    aciIndex += gv.getCharacterCount(i, i);
1349:                    if (aciIndex >= charMap.length)
1350:                        aciIndex = charMap.length - 1;
1351:                    ch = aci.setIndex(aciIndex + aciStart);
1352:                    i++;
1353:                }
1354:                // Update last glyph pos
1355:                pos.x = curr_x_pos;
1356:                pos.y = curr_y_pos;
1357:                gv.setGlyphPosition(i, pos);
1358:
1359:                advance = new Point2D.Float(
1360:                        (float) (curr_x_pos - offset.getX()),
1361:                        (float) (curr_y_pos - offset.getY()));
1362:
1363:                // Do a last pass positioning the transparent/mark glyphs on the
1364:                // base glyphs.
1365:                if (hasArabicTransparent) {
1366:                    ch = aci.first();
1367:                    aciIndex = 0;
1368:                    i = 0;
1369:                    int transparentStart = -1;
1370:                    while (i < numGlyphs) {
1371:                        if (ArabicTextHandler.arabicCharTransparent(ch)) {
1372:                            if (transparentStart == -1)
1373:                                transparentStart = i;
1374:                        } else {
1375:                            if (transparentStart != -1) {
1376:                                Point2D loc = gv.getGlyphPosition(i);
1377:                                GVTGlyphMetrics gm = gv.getGlyphMetrics(i);
1378:                                int tyS = 0, txS = 0;
1379:                                float advX = 0, advY = 0;
1380:                                if (vertical) {
1381:                                    if (glyphOrientationAuto
1382:                                            || (glyphOrientationAngle == 90))
1383:                                        advY = gm.getHorizontalAdvance();
1384:                                    else if (glyphOrientationAngle == 270)
1385:                                        advY = 0;
1386:                                    else if (glyphOrientationAngle == 0)
1387:                                        advX = gm.getHorizontalAdvance();
1388:                                    else
1389:                                        // 180
1390:                                        advX = -gm.getHorizontalAdvance();
1391:                                } else {
1392:                                    if (glyphOrientationAngle == 0)
1393:                                        advX = gm.getHorizontalAdvance();
1394:                                    else if (glyphOrientationAngle == 90)
1395:                                        advY = gm.getHorizontalAdvance();
1396:                                    else if (glyphOrientationAngle == 180)
1397:                                        advX = 0;
1398:                                    else
1399:                                        // 270
1400:                                        advY = -gm.getHorizontalAdvance();
1401:                                }
1402:                                float baseX = (float) (loc.getX() + advX);
1403:                                float baseY = (float) (loc.getY() + advY);
1404:                                for (int j = transparentStart; j < i; j++) {
1405:                                    Point2D locT = gv.getGlyphPosition(j);
1406:                                    GVTGlyphMetrics gmT = gv.getGlyphMetrics(j);
1407:                                    float locX = (float) locT.getX();
1408:                                    float locY = (float) locT.getY();
1409:                                    float tx = 0, ty = 0;
1410:                                    float advT = gmT.getHorizontalAdvance();
1411:                                    if (vertical) {
1412:                                        if (glyphOrientationAuto
1413:                                                || (glyphOrientationAngle == 90))
1414:                                            locY = baseY - advT;
1415:                                        else if (glyphOrientationAngle == 270)
1416:                                            locY = baseY + advT;
1417:                                        else if (glyphOrientationAngle == 0)
1418:                                            locX = baseX - advT;
1419:                                        else
1420:                                            // 180deg
1421:                                            locX = baseX + advT;
1422:                                    } else {
1423:                                        if (glyphOrientationAngle == 0)
1424:                                            locX = baseX - advT;
1425:                                        else if (glyphOrientationAngle == 90)
1426:                                            locY = baseY - advT;
1427:                                        else if (glyphOrientationAngle == 180)
1428:                                            locX = baseX + advT;
1429:                                        else
1430:                                            // 270
1431:                                            locY = baseY + advT;
1432:                                    }
1433:
1434:                                    locT = new Point2D.Double(locX, locY);
1435:                                    gv.setGlyphPosition(j, locT);
1436:                                    if ((txS != 0) || (tyS != 0)) {
1437:                                        AffineTransform at;
1438:                                        at = AffineTransform
1439:                                                .getTranslateInstance(tx, ty);
1440:                                        at.concatenate(gv.getGlyphTransform(i));
1441:                                        gv.setGlyphTransform(i, at);
1442:                                    }
1443:                                }
1444:                                transparentStart = -1;
1445:                            }
1446:                        }
1447:                        aciIndex += gv.getCharacterCount(i, i);
1448:                        if (aciIndex >= charMap.length)
1449:                            aciIndex = charMap.length - 1;
1450:                        ch = aci.setIndex(aciIndex + aciStart);
1451:                        i++;
1452:                    }
1453:
1454:                }
1455:
1456:                layoutApplied = true;
1457:                spacingApplied = false;
1458:                glyphAdvances = null;
1459:                pathApplied = false;
1460:            }
1461:
1462:            /**
1463:             * Does any spacing adjustments that may have been specified.
1464:             */
1465:            protected void adjustTextSpacing() {
1466:
1467:                if (spacingApplied)
1468:                    // Nothing to do...
1469:                    return;
1470:
1471:                if (!layoutApplied)
1472:                    // Must have clean layout to do spacing...
1473:                    doExplicitGlyphLayout();
1474:
1475:                aci.first();
1476:                Boolean customSpacing = (Boolean) aci
1477:                        .getAttribute(GVTAttributedCharacterIterator.TextAttribute.CUSTOM_SPACING);
1478:                if ((customSpacing != null) && customSpacing.booleanValue()) {
1479:                    advance = doSpacing(
1480:                            (Float) aci
1481:                                    .getAttribute(GVTAttributedCharacterIterator.TextAttribute.KERNING),
1482:                            (Float) aci
1483:                                    .getAttribute(GVTAttributedCharacterIterator.TextAttribute.LETTER_SPACING),
1484:                            (Float) aci
1485:                                    .getAttribute(GVTAttributedCharacterIterator.TextAttribute.WORD_SPACING));
1486:                    // Basic layout is now messed up...
1487:                    layoutApplied = false;
1488:                }
1489:
1490:                // This will clear layoutApplied if it mucks with the current
1491:                // character positions.
1492:                applyStretchTransform(!adjSpacing);
1493:
1494:                spacingApplied = true;
1495:                pathApplied = false;
1496:            }
1497:
1498:            /**
1499:             * Performs any spacing adjustments required and returns the new advance
1500:             * value.
1501:             *
1502:             * @param kern The kerning adjustment to apply to the space
1503:             * between each char.
1504:             * @param letterSpacing The amount of spacing required between each char.
1505:             * @param wordSpacing The amount of spacing required between each word.  */
1506:            protected Point2D doSpacing(Float kern, Float letterSpacing,
1507:                    Float wordSpacing) {
1508:                boolean autoKern = true;
1509:                boolean doWordSpacing = false;
1510:                boolean doLetterSpacing = false;
1511:                float kernVal = 0f;
1512:                float letterSpacingVal = 0f;
1513:
1514:                if ((kern != null) && (!kern.isNaN())) {
1515:                    kernVal = kern.floatValue();
1516:                    autoKern = false;
1517:                    //System.out.println("KERNING: "+kernVal);
1518:                }
1519:                if ((letterSpacing != null) && (!letterSpacing.isNaN())) {
1520:                    letterSpacingVal = letterSpacing.floatValue();
1521:                    doLetterSpacing = true;
1522:                    //System.out.println("LETTER-SPACING: "+letterSpacingVal);
1523:                }
1524:                if ((wordSpacing != null) && (!wordSpacing.isNaN())) {
1525:                    doWordSpacing = true;
1526:                }
1527:
1528:                int numGlyphs = gv.getNumGlyphs();
1529:
1530:                float dx = 0f;
1531:                float dy = 0f;
1532:                Point2D[] newPositions = new Point2D[numGlyphs + 1];
1533:                Point2D prevPos = gv.getGlyphPosition(0);
1534:                int prevCode = gv.getGlyphCode(0);
1535:                float x = (float) prevPos.getX();
1536:                float y = (float) prevPos.getY();
1537:
1538:                Point2D lastCharAdvance = new Point2D.Double(
1539:                        advance.getX()
1540:                                - (gv.getGlyphPosition(numGlyphs - 1).getX() - x),
1541:                        advance.getY()
1542:                                - (gv.getGlyphPosition(numGlyphs - 1).getY() - y));
1543:
1544:                try {
1545:                    GVTFont font = gv.getFont();
1546:                    // do letter spacing first
1547:                    if ((numGlyphs > 1) && (doLetterSpacing || !autoKern)) {
1548:                        for (int i = 1; i <= numGlyphs; ++i) {
1549:                            Point2D gpos = gv.getGlyphPosition(i);
1550:                            int currCode;
1551:                            currCode = (i == numGlyphs) ? -1 : gv
1552:                                    .getGlyphCode(i);
1553:                            dx = (float) gpos.getX() - (float) prevPos.getX();
1554:                            dy = (float) gpos.getY() - (float) prevPos.getY();
1555:                            if (autoKern) {
1556:                                if (vertical)
1557:                                    dy += letterSpacingVal;
1558:                                else
1559:                                    dx += letterSpacingVal;
1560:                            } else {
1561:                                // apply explicit kerning adjustments,
1562:                                // removing any auto-kern values
1563:                                if (vertical) {
1564:                                    float vKern = 0;
1565:                                    if (currCode != -1)
1566:                                        vKern = font.getVKern(prevCode,
1567:                                                currCode);
1568:                                    dy += kernVal - vKern + letterSpacingVal;
1569:                                } else {
1570:                                    float hKern = 0;
1571:                                    if (currCode != -1)
1572:                                        hKern = font.getHKern(prevCode,
1573:                                                currCode);
1574:                                    dx += kernVal - hKern + letterSpacingVal;
1575:                                }
1576:                            }
1577:                            x += dx;
1578:                            y += dy;
1579:                            newPositions[i] = new Point2D.Float(x, y);
1580:                            prevPos = gpos;
1581:                            prevCode = currCode;
1582:                        }
1583:
1584:                        for (int i = 1; i <= numGlyphs; ++i) { // assign the new positions
1585:                            if (newPositions[i] != null) {
1586:                                gv.setGlyphPosition(i, newPositions[i]);
1587:                            }
1588:                        }
1589:                    }
1590:
1591:                    // adjust the advance of the last character
1592:                    if (vertical) {
1593:                        lastCharAdvance.setLocation(lastCharAdvance.getX(),
1594:                                lastCharAdvance.getY() + kernVal
1595:                                        + letterSpacingVal);
1596:                    } else {
1597:                        lastCharAdvance.setLocation(lastCharAdvance.getX()
1598:                                + kernVal + letterSpacingVal, lastCharAdvance
1599:                                .getY());
1600:                    }
1601:
1602:                    // now do word spacing
1603:                    dx = 0f;
1604:                    dy = 0f;
1605:                    prevPos = gv.getGlyphPosition(0);
1606:                    x = (float) prevPos.getX();
1607:                    y = (float) prevPos.getY();
1608:
1609:                    if ((numGlyphs > 1) && (doWordSpacing)) {
1610:                        for (int i = 1; i < numGlyphs; i++) {
1611:                            Point2D gpos = gv.getGlyphPosition(i);
1612:                            dx = (float) gpos.getX() - (float) prevPos.getX();
1613:                            dy = (float) gpos.getY() - (float) prevPos.getY();
1614:                            boolean inWS = false;
1615:                            // while this is whitespace, increment
1616:                            int beginWS = i;
1617:                            int endWS = i;
1618:                            GVTGlyphMetrics gm = gv.getGlyphMetrics(i);
1619:
1620:                            // BUG: gm.isWhitespace() fails for latin SPACE glyph!
1621:                            while ((gm.getBounds2D().getWidth() < 0.01d)
1622:                                    || gm.isWhitespace()) {
1623:                                if (!inWS)
1624:                                    inWS = true;
1625:                                if (i == numGlyphs - 1) {
1626:                                    // white space at the end
1627:                                    break;
1628:                                }
1629:                                ++i;
1630:                                ++endWS;
1631:                                gpos = gv.getGlyphPosition(i);
1632:                                gm = gv.getGlyphMetrics(i);
1633:                            }
1634:
1635:                            if (inWS) { // apply wordSpacing
1636:                                int nWS = endWS - beginWS;
1637:                                float px = (float) prevPos.getX();
1638:                                float py = (float) prevPos.getY();
1639:                                dx = (float) (gpos.getX() - px) / (nWS + 1);
1640:                                dy = (float) (gpos.getY() - py) / (nWS + 1);
1641:                                if (vertical) {
1642:                                    dy += wordSpacing.floatValue() / (nWS + 1);
1643:                                } else {
1644:                                    dx += wordSpacing.floatValue() / (nWS + 1);
1645:                                }
1646:                                for (int j = beginWS; j <= endWS; ++j) {
1647:                                    x += dx;
1648:                                    y += dy;
1649:                                    newPositions[j] = new Point2D.Float(x, y);
1650:                                }
1651:                            } else {
1652:                                dx = (float) (gpos.getX() - prevPos.getX());
1653:                                dy = (float) (gpos.getY() - prevPos.getY());
1654:                                x += dx;
1655:                                y += dy;
1656:                                newPositions[i] = new Point2D.Float(x, y);
1657:                            }
1658:                            prevPos = gpos;
1659:                        }
1660:                        Point2D gPos = gv.getGlyphPosition(numGlyphs);
1661:                        x += (float) (gPos.getX() - prevPos.getX());
1662:                        y += (float) (gPos.getY() - prevPos.getY());
1663:                        newPositions[numGlyphs] = new Point2D.Float(x, y);
1664:
1665:                        for (int i = 1; i <= numGlyphs; ++i) { // assign the new positions
1666:                            if (newPositions[i] != null) {
1667:                                gv.setGlyphPosition(i, newPositions[i]);
1668:                            }
1669:                        }
1670:                    }
1671:
1672:                } catch (Exception e) {
1673:                    e.printStackTrace();
1674:                }
1675:
1676:                // calculate the new advance
1677:                double advX = gv.getGlyphPosition(numGlyphs - 1).getX()
1678:                        - gv.getGlyphPosition(0).getX();
1679:                double advY = gv.getGlyphPosition(numGlyphs - 1).getY()
1680:                        - gv.getGlyphPosition(0).getY();
1681:                Point2D newAdvance = new Point2D.Double(advX
1682:                        + lastCharAdvance.getX(), advY + lastCharAdvance.getY());
1683:                return newAdvance;
1684:            }
1685:
1686:            /**
1687:             * Stretches the text so that it becomes the specified length.
1688:             *
1689:             * @param stretchGlyphs if true xScale, yScale will be applied to
1690:             *                      each glyphs transform.
1691:             */
1692:            protected void applyStretchTransform(boolean stretchGlyphs) {
1693:                if ((xScale == 1) && (yScale == 1))
1694:                    return;
1695:
1696:                AffineTransform scaleAT = AffineTransform.getScaleInstance(
1697:                        xScale, yScale);
1698:
1699:                int numGlyphs = gv.getNumGlyphs();
1700:                float[] gp = gv.getGlyphPositions(0, numGlyphs + 1, null);
1701:
1702:                float initX = gp[0];
1703:                float initY = gp[1];
1704:                Point2D.Float pos = new Point2D.Float();
1705:                for (int i = 0; i <= numGlyphs; i++) {
1706:                    float dx = gp[2 * i] - initX;
1707:                    float dy = gp[2 * i + 1] - initY;
1708:                    pos.x = initX + dx * xScale;
1709:                    pos.y = initY + dy * yScale;
1710:                    gv.setGlyphPosition(i, pos);
1711:
1712:                    if ((stretchGlyphs) && (i != numGlyphs)) {
1713:                        // stretch the glyph
1714:                        AffineTransform glyphTransform = gv
1715:                                .getGlyphTransform(i);
1716:                        if (glyphTransform != null) {
1717:                            glyphTransform.preConcatenate(scaleAT);
1718:                            gv.setGlyphTransform(i, glyphTransform);
1719:                        } else {
1720:                            gv.setGlyphTransform(i, scaleAT);
1721:                        }
1722:                    }
1723:                }
1724:
1725:                advance = new Point2D.Float((float) (advance.getX() * xScale),
1726:                        (float) (advance.getY() * yScale));
1727:                // Basic layout is now messed up...
1728:                layoutApplied = false;
1729:            }
1730:
1731:            /**
1732:             * If this layout is on a text path, positions the characters
1733:             * along the path.
1734:             */
1735:            protected void doPathLayout() {
1736:                if (pathApplied)
1737:                    return;
1738:
1739:                if (!spacingApplied)
1740:                    // This will layout the text if needed.
1741:                    adjustTextSpacing();
1742:
1743:                getGlyphAdvances();
1744:
1745:                // if doesn't have an attached text path, just return
1746:                if (textPath == null) {
1747:                    // We applied the empty path (i.e. do nothing).
1748:                    pathApplied = true;
1749:                    return;
1750:                }
1751:
1752:                boolean horizontal = !isVertical();
1753:
1754:                boolean glyphOrientationAuto = isGlyphOrientationAuto();
1755:                int glyphOrientationAngle = 0;
1756:                if (!glyphOrientationAuto) {
1757:                    glyphOrientationAngle = getGlyphOrientationAngle();
1758:                }
1759:
1760:                float pathLength = textPath.lengthOfPath();
1761:                float startOffset = textPath.getStartOffset();
1762:                int numGlyphs = gv.getNumGlyphs();
1763:
1764:                // make sure all glyphs visible again, this maybe just a change in
1765:                // offset so they may have been made invisible in a previous
1766:                // pathLayout call
1767:                for (int i = 0; i < numGlyphs; i++) {
1768:                    gv.setGlyphVisible(i, true);
1769:                }
1770:
1771:                // calculate the total length of the glyphs, this will become be
1772:                // the length along the path that is used by the text
1773:                float glyphsLength;
1774:                if (horizontal) {
1775:                    glyphsLength = (float) gv.getLogicalBounds().getWidth();
1776:                } else {
1777:                    glyphsLength = (float) gv.getLogicalBounds().getHeight();
1778:                }
1779:
1780:                // check that pathLength and glyphsLength are not 0
1781:                if (pathLength == 0f || glyphsLength == 0f) {
1782:                    // We applied the empty path.
1783:                    pathApplied = true;
1784:                    textPathAdvance = advance;
1785:                    return;
1786:                }
1787:
1788:                // the current start point of the character on the path
1789:                // calculate the offset of the first glyph the offset will be
1790:                // 0 if the glyph is on the path (ie. not adjusted by a dy or
1791:                // dx)
1792:                Point2D firstGlyphPosition = gv.getGlyphPosition(0);
1793:                float glyphOffset = 0; // offset perpendicular to path
1794:                float currentPosition;
1795:                if (horizontal) {
1796:                    glyphOffset = (float) (firstGlyphPosition.getY());
1797:                    currentPosition = (float) (firstGlyphPosition.getX() + startOffset);
1798:                } else {
1799:                    glyphOffset = (float) (firstGlyphPosition.getX());
1800:                    currentPosition = (float) (firstGlyphPosition.getY() + startOffset);
1801:                }
1802:
1803:                char ch = aci.first();
1804:                int start = aci.getBeginIndex();
1805:                int currentChar = 0;
1806:                int lastGlyphDrawn = -1;
1807:                float lastGlyphAdvance = 0;
1808:                // iterate through the GlyphVector placing each glyph
1809:                for (int i = 0; i < numGlyphs; i++) {
1810:
1811:                    Point2D currentGlyphPos = gv.getGlyphPosition(i);
1812:
1813:                    // calculate the advance and offset for the next glyph, do it
1814:                    // now before we modify the current glyph position
1815:
1816:                    float glyphAdvance = 0; // along path
1817:                    float nextGlyphOffset = 0; // perpendicular to path eg dy or dx
1818:                    Point2D nextGlyphPosition = gv.getGlyphPosition(i + 1);
1819:                    if (horizontal) {
1820:                        glyphAdvance = (float) (nextGlyphPosition.getX() - currentGlyphPos
1821:                                .getX());
1822:                        nextGlyphOffset = (float) (nextGlyphPosition.getY() - currentGlyphPos
1823:                                .getY());
1824:                    } else {
1825:                        glyphAdvance = (float) (nextGlyphPosition.getY() - currentGlyphPos
1826:                                .getY());
1827:                        nextGlyphOffset = (float) (nextGlyphPosition.getX() - currentGlyphPos
1828:                                .getX());
1829:                    }
1830:
1831:                    // calculate the center line position for the glyph
1832:                    Rectangle2D glyphBounds = gv.getGlyphOutline(i)
1833:                            .getBounds2D();
1834:                    float glyphWidth = (float) glyphBounds.getWidth();
1835:                    float glyphHeight = (float) glyphBounds.getHeight();
1836:                    float glyphMidX = 0;
1837:                    if (glyphWidth > 0) {
1838:                        glyphMidX = (float) (glyphBounds.getX() + glyphWidth / 2f);
1839:                        glyphMidX -= (float) currentGlyphPos.getX();
1840:                    }
1841:
1842:                    float glyphMidY = 0;
1843:                    if (glyphHeight > 0) {
1844:                        glyphMidY = (float) (glyphBounds.getY() + glyphHeight / 2f);
1845:                        glyphMidY -= (float) currentGlyphPos.getY();
1846:                    }
1847:
1848:                    // System.err.println("GMX: " + glyphMidX +
1849:                    //                    " W2: " + (glyphWidth/2) +
1850:                    //                    " PosX: " + currentGlyphPos.getX() +
1851:                    //                    " BX: "   + glyphBounds.getX());
1852:                    //
1853:                    // System.err.println("GMY: " + glyphMidY +
1854:                    //                    " H2: " + (glyphHeight/2) +
1855:                    //                    " PosY: " + currentGlyphPos.getY() +
1856:                    //                    " BY: "   + glyphBounds.getY());
1857:
1858:                    float charMidPos;
1859:                    if (horizontal) {
1860:                        charMidPos = currentPosition + glyphMidX;
1861:                    } else {
1862:                        charMidPos = currentPosition + glyphMidY;
1863:                    }
1864:
1865:                    // Calculate the actual point to place the glyph around
1866:                    Point2D charMidPoint = textPath.pointAtLength(charMidPos);
1867:
1868:                    // Check if the glyph is actually on the path
1869:                    if (charMidPoint != null) {
1870:
1871:                        // Calculate the normal to the path (midline of glyph)
1872:                        float angle = textPath.angleAtLength(charMidPos);
1873:
1874:                        // Define the transform of the glyph
1875:                        AffineTransform glyphPathTransform = new AffineTransform();
1876:
1877:                        // rotate midline of glyph to be normal to path
1878:                        if (horizontal) {
1879:                            glyphPathTransform.rotate(angle);
1880:                        } else {
1881:                            glyphPathTransform.rotate(angle - (Math.PI / 2));
1882:                        }
1883:
1884:                        // re-apply any offset eg from tspan, or spacing adjust
1885:                        if (horizontal) {
1886:                            glyphPathTransform.translate(0, glyphOffset);
1887:                        } else {
1888:                            glyphPathTransform.translate(glyphOffset, 0);
1889:                        }
1890:
1891:                        // translate glyph backwards so we rotate about the
1892:                        // center of the glyph
1893:                        if (horizontal) {
1894:                            glyphPathTransform.translate(-glyphMidX, 0f);
1895:                        } else {
1896:                            glyphPathTransform.translate(0f, -glyphMidY);
1897:                        }
1898:
1899:                        // set the new glyph position and transform
1900:                        AffineTransform glyphTransform = gv
1901:                                .getGlyphTransform(i);
1902:                        if (glyphTransform != null) {
1903:                            glyphPathTransform.concatenate(glyphTransform);
1904:                        }
1905:
1906:                        gv.setGlyphTransform(i, glyphPathTransform);
1907:                        gv.setGlyphPosition(i, charMidPoint);
1908:                        // keep track of the last glyph drawn to make calculating the
1909:                        // textPathAdvance value easier later
1910:                        lastGlyphDrawn = i;
1911:                        lastGlyphAdvance = glyphAdvance;
1912:
1913:                    } else {
1914:                        // not on path so don't render
1915:                        gv.setGlyphVisible(i, false);
1916:                    }
1917:                    currentPosition += glyphAdvance;
1918:                    glyphOffset += nextGlyphOffset;
1919:                    currentChar += gv.getCharacterCount(i, i);
1920:                    if (currentChar >= charMap.length)
1921:                        currentChar = charMap.length - 1;
1922:                    ch = aci.setIndex(currentChar + start);
1923:                }
1924:
1925:                // store the position where a following glyph should be drawn,
1926:                // note: this will only be used if the following text layout is not
1927:                //       on a text path
1928:                if (lastGlyphDrawn > -1) {
1929:                    Point2D lastGlyphPos = gv.getGlyphPosition(lastGlyphDrawn);
1930:                    if (horizontal) {
1931:                        textPathAdvance = new Point2D.Double(lastGlyphPos
1932:                                .getX()
1933:                                + lastGlyphAdvance, lastGlyphPos.getY());
1934:                    } else {
1935:                        textPathAdvance = new Point2D.Double(lastGlyphPos
1936:                                .getX(), lastGlyphPos.getY() + lastGlyphAdvance);
1937:                    }
1938:                } else {
1939:                    textPathAdvance = new Point2D.Double(0, 0);
1940:                }
1941:
1942:                // The default layout is junk now...
1943:                layoutApplied = false;
1944:                // The spacing stuff is junk now.
1945:                spacingApplied = false;
1946:                pathApplied = true;
1947:            }
1948:
1949:            /**
1950:             * Returns true if the specified character is within one of the Latin
1951:             * unicode character blocks.
1952:             *
1953:             * @param c The char to test.
1954:             *
1955:             * @return True if c is latin.
1956:             */
1957:            protected boolean isLatinChar(char c) {
1958:
1959:                if (c < 255 && Character.isLetterOrDigit(c)) {
1960:                    // cheap quick check, should catch most lation-chars
1961:                    return true;
1962:                }
1963:
1964:                Character.UnicodeBlock block = Character.UnicodeBlock.of(c);
1965:
1966:                if (block == Character.UnicodeBlock.BASIC_LATIN
1967:                        || block == Character.UnicodeBlock.LATIN_1_SUPPLEMENT
1968:                        || block == Character.UnicodeBlock.LATIN_EXTENDED_ADDITIONAL
1969:                        || block == Character.UnicodeBlock.LATIN_EXTENDED_A
1970:                        || block == Character.UnicodeBlock.LATIN_EXTENDED_B
1971:                        || block == Character.UnicodeBlock.ARABIC
1972:                        || block == Character.UnicodeBlock.ARABIC_PRESENTATION_FORMS_A
1973:                        || block == Character.UnicodeBlock.ARABIC_PRESENTATION_FORMS_B) {
1974:                    return true;
1975:                }
1976:                return false;
1977:            }
1978:
1979:            /**
1980:             * Returns whether or not the vertical glyph orientation value is "auto".
1981:             */
1982:            protected boolean isGlyphOrientationAuto() {
1983:                if (!isVertical())
1984:                    return false;
1985:                aci.first();
1986:                Integer vOrient = (Integer) aci
1987:                        .getAttribute(VERTICAL_ORIENTATION);
1988:                if (vOrient != null) {
1989:                    return (vOrient == ORIENTATION_AUTO);
1990:                }
1991:                return true;
1992:            }
1993:
1994:            /**
1995:             * Returns the value of the vertical glyph orientation angle. This will be
1996:             * one of 0, 90, 180 or 270.
1997:             */
1998:            protected int getGlyphOrientationAngle() {
1999:
2000:                int glyphOrientationAngle = 0;
2001:
2002:                aci.first();
2003:                Float angle;
2004:
2005:                if (isVertical()) {
2006:                    angle = (Float) aci
2007:                            .getAttribute(VERTICAL_ORIENTATION_ANGLE);
2008:                } else {
2009:                    angle = (Float) aci
2010:                            .getAttribute(HORIZONTAL_ORIENTATION_ANGLE);
2011:                }
2012:
2013:                if (angle != null) {
2014:                    glyphOrientationAngle = (int) angle.floatValue();
2015:                }
2016:
2017:                // if not one of 0, 90, 180 or 270, round to nearest value
2018:                if ((glyphOrientationAngle != 0)
2019:                        || (glyphOrientationAngle != 90)
2020:                        || (glyphOrientationAngle != 180)
2021:                        || (glyphOrientationAngle != 270)) {
2022:
2023:                    while (glyphOrientationAngle < 0) {
2024:                        glyphOrientationAngle += 360;
2025:                    }
2026:
2027:                    while (glyphOrientationAngle >= 360) {
2028:                        glyphOrientationAngle -= 360;
2029:                    }
2030:
2031:                    if ((glyphOrientationAngle <= 45)
2032:                            || (glyphOrientationAngle > 315)) {
2033:                        glyphOrientationAngle = 0;
2034:                    } else if ((glyphOrientationAngle > 45)
2035:                            && (glyphOrientationAngle <= 135)) {
2036:                        glyphOrientationAngle = 90;
2037:                    } else if ((glyphOrientationAngle > 135)
2038:                            && (glyphOrientationAngle <= 225)) {
2039:                        glyphOrientationAngle = 180;
2040:                    } else {
2041:                        glyphOrientationAngle = 270;
2042:                    }
2043:                }
2044:                return glyphOrientationAngle;
2045:            }
2046:
2047:            /**
2048:             * Return true is the character index is represented by glyphs
2049:             * in this layout.
2050:             *
2051:             * @param index index of the character in the ACI.
2052:             * @return true if the layout represents that character.
2053:             */
2054:            public boolean hasCharacterIndex(int index) {
2055:
2056:                for (int n = 0; n < charMap.length; n++) {
2057:                    if (index == charMap[n])
2058:                        return true;
2059:                }
2060:                return false;
2061:            }
2062:
2063:            /**
2064:             * Return true if this text run represents
2065:             * an alt glyph.
2066:             */
2067:            public boolean isAltGlyph() {
2068:                return this.isAltGlyph;
2069:            }
2070:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.