Source Code Cross Referenced for TextRunBreaker.java in  » Apache-Harmony-Java-SE » org-package » org » apache » harmony » awt » gl » font » 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 » Apache Harmony Java SE » org package » org.apache.harmony.awt.gl.font 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


001:        /*
002:         *  Licensed to the Apache Software Foundation (ASF) under one or more
003:         *  contributor license agreements.  See the NOTICE file distributed with
004:         *  this work for additional information regarding copyright ownership.
005:         *  The ASF licenses this file to You under the Apache License, Version 2.0
006:         *  (the "License"); you may not use this file except in compliance with
007:         *  the License.  You may obtain a copy of the License at
008:         *
009:         *     http://www.apache.org/licenses/LICENSE-2.0
010:         *
011:         *  Unless required by applicable law or agreed to in writing, software
012:         *  distributed under the License is distributed on an "AS IS" BASIS,
013:         *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014:         *  See the License for the specific language governing permissions and
015:         *  limitations under the License.
016:         */
017:        /**
018:         * @author Oleg V. Khaschansky
019:         * @version $Revision$
020:         *
021:         */package org.apache.harmony.awt.gl.font;
022:
023:        import java.awt.geom.GeneralPath;
024:        import java.awt.geom.Rectangle2D;
025:        import java.awt.im.InputMethodHighlight;
026:        import java.awt.font.*;
027:        import java.awt.*;
028:        import java.text.AttributedCharacterIterator;
029:        import java.text.Annotation;
030:        import java.text.AttributedCharacterIterator.Attribute;
031:        import java.util.*;
032:
033:        import org.apache.harmony.awt.gl.font.TextDecorator.Decoration;
034:        import org.apache.harmony.awt.internal.nls.Messages;
035:        import org.apache.harmony.misc.HashCode;
036:
037:        // TODO - bidi not implemented yet
038:
039:        /**
040:         * This class is responsible for breaking the text into the run segments
041:         * with constant font, style, other text attributes and direction.
042:         * It also stores the created text run segments and covers functionality
043:         * related to the operations on the set of segments, like calculating metrics,
044:         * rendering, justification, hit testing, etc.
045:         */
046:        public class TextRunBreaker implements  Cloneable {
047:            AttributedCharacterIterator aci;
048:            FontRenderContext frc;
049:
050:            char[] text;
051:
052:            byte[] levels;
053:
054:            HashMap<Integer, Object> fonts;
055:            HashMap<Integer, Decoration> decorations;
056:
057:            // Related to default font substitution
058:            int forcedFontRunStarts[];
059:
060:            ArrayList<TextRunSegment> runSegments = new ArrayList<TextRunSegment>();
061:
062:            // For fast retrieving of the segment containing
063:            // character with known logical index
064:            int logical2segment[];
065:            int segment2visual[]; // Visual order of segments TODO - implement
066:            int visual2segment[];
067:            int logical2visual[];
068:            int visual2logical[];
069:
070:            SegmentsInfo storedSegments;
071:            private boolean haveAllSegments = false;
072:            int segmentsStart, segmentsEnd;
073:
074:            float justification = 1.0f;
075:
076:            public TextRunBreaker(AttributedCharacterIterator aci,
077:                    FontRenderContext frc) {
078:                this .aci = aci;
079:                this .frc = frc;
080:
081:                segmentsStart = aci.getBeginIndex();
082:                segmentsEnd = aci.getEndIndex();
083:
084:                int len = segmentsEnd - segmentsStart;
085:                text = new char[len];
086:                aci.setIndex(segmentsEnd);
087:                while (len-- != 0) { // Going in backward direction is faster? Simplier checks here?
088:                    text[len] = aci.previous();
089:                }
090:
091:                createStyleRuns();
092:            }
093:
094:            /**
095:             * Visual order of text segments may differ from the logical order.
096:             * This method calculates visual position of the segment from its logical position.
097:             * @param segmentNum - logical position of the segment
098:             * @return visual position of the segment
099:             */
100:            int getVisualFromSegmentOrder(int segmentNum) {
101:                return (segment2visual == null) ? segmentNum
102:                        : segment2visual[segmentNum];
103:            }
104:
105:            /**
106:             * Visual order of text segments may differ from the logical order.
107:             * This method calculates logical position of the segment from its visual position.
108:             * @param visual - visual position of the segment
109:             * @return logical position of the segment
110:             */
111:            int getSegmentFromVisualOrder(int visual) {
112:                return (visual2segment == null) ? visual
113:                        : visual2segment[visual];
114:            }
115:
116:            /**
117:             * Visual order of the characters may differ from the logical order.
118:             * This method calculates visual position of the character from its logical position.
119:             * @param logical - logical position of the character
120:             * @return visual position
121:             */
122:            int getVisualFromLogical(int logical) {
123:                return (logical2visual == null) ? logical
124:                        : logical2visual[logical];
125:            }
126:
127:            /**
128:             * Visual order of the characters may differ from the logical order.
129:             * This method calculates logical position of the character from its visual position.
130:             * @param visual - visual position
131:             * @return logical position
132:             */
133:            int getLogicalFromVisual(int visual) {
134:                return (visual2logical == null) ? visual
135:                        : visual2logical[visual];
136:            }
137:
138:            /**
139:             * Calculates the end index of the level run, limited by the given text run.
140:             * @param runStart - run start
141:             * @param runEnd - run end
142:             * @return end index of the level run
143:             */
144:            int getLevelRunLimit(int runStart, int runEnd) {
145:                if (levels == null) {
146:                    return runEnd;
147:                }
148:                int endLevelRun = runStart + 1;
149:                byte level = levels[runStart];
150:
151:                while (endLevelRun <= runEnd && levels[endLevelRun] == level) {
152:                    endLevelRun++;
153:                }
154:
155:                return endLevelRun;
156:            }
157:
158:            /**
159:             * Adds InputMethodHighlight to the attributes
160:             * @param attrs - text attributes
161:             * @return patched text attributes
162:             */
163:            Map<? extends Attribute, ?> unpackAttributes(
164:                    Map<? extends Attribute, ?> attrs) {
165:                if (attrs.containsKey(TextAttribute.INPUT_METHOD_HIGHLIGHT)) {
166:                    Map<TextAttribute, ?> styles = null;
167:
168:                    Object val = attrs
169:                            .get(TextAttribute.INPUT_METHOD_HIGHLIGHT);
170:
171:                    if (val instanceof  Annotation) {
172:                        val = ((Annotation) val).getValue();
173:                    }
174:
175:                    if (val instanceof  InputMethodHighlight) {
176:                        InputMethodHighlight ihl = ((InputMethodHighlight) val);
177:                        styles = ihl.getStyle();
178:
179:                        if (styles == null) {
180:                            Toolkit tk = Toolkit.getDefaultToolkit();
181:                            styles = tk.mapInputMethodHighlight(ihl);
182:                        }
183:                    }
184:
185:                    if (styles != null) {
186:                        HashMap<Attribute, Object> newAttrs = new HashMap<Attribute, Object>();
187:                        newAttrs.putAll(attrs);
188:                        newAttrs.putAll(styles);
189:                        return newAttrs;
190:                    }
191:                }
192:
193:                return attrs;
194:            }
195:
196:            /**
197:             * Breaks the text into separate style runs.
198:             */
199:            void createStyleRuns() {
200:                // TODO - implement fast and simple case
201:                fonts = new HashMap<Integer, Object>();
202:                decorations = new HashMap<Integer, Decoration>();
203:                ////
204:
205:                ArrayList<Integer> forcedFontRunStartsList = null;
206:
207:                Map<? extends Attribute, ?> attributes = null;
208:
209:                // Check justification attribute
210:                Object val = aci.getAttribute(TextAttribute.JUSTIFICATION);
211:                if (val != null) {
212:                    justification = ((Float) val).floatValue();
213:                }
214:
215:                for (int index = segmentsStart, nextRunStart = segmentsStart; index < segmentsEnd; index = nextRunStart, aci
216:                        .setIndex(index)) {
217:                    nextRunStart = aci.getRunLimit();
218:                    attributes = unpackAttributes(aci.getAttributes());
219:
220:                    TextDecorator.Decoration d = TextDecorator
221:                            .getDecoration(attributes);
222:                    decorations.put(new Integer(index), d);
223:
224:                    // Find appropriate font or place GraphicAttribute there
225:
226:                    // 1. Try to pick up CHAR_REPLACEMENT (compatibility)
227:                    Object value = (GraphicAttribute) attributes
228:                            .get(TextAttribute.CHAR_REPLACEMENT);
229:
230:                    if (value == null) {
231:                        // 2. Try to Get FONT
232:                        value = (Font) attributes.get(TextAttribute.FONT);
233:
234:                        if (value == null) {
235:                            // 3. Try to create font from FAMILY
236:                            if (attributes.get(TextAttribute.FAMILY) != null) {
237:                                value = Font.getFont(attributes);
238:                            }
239:
240:                            if (value == null) {
241:                                // 4. No attributes found, using default.
242:                                if (forcedFontRunStartsList == null) {
243:                                    forcedFontRunStartsList = new ArrayList<Integer>();
244:                                }
245:                                FontFinder.findFonts(text, index, nextRunStart,
246:                                        forcedFontRunStartsList, fonts);
247:                                value = fonts.get(new Integer(index));
248:                            }
249:                        }
250:                    }
251:
252:                    fonts.put(new Integer(index), value);
253:                }
254:
255:                // We have added some default fonts, so we have some extra runs in text
256:                if (forcedFontRunStartsList != null) {
257:                    forcedFontRunStarts = new int[forcedFontRunStartsList
258:                            .size()];
259:                    for (int i = 0; i < forcedFontRunStartsList.size(); i++) {
260:                        forcedFontRunStarts[i] = forcedFontRunStartsList.get(i)
261:                                .intValue();
262:                    }
263:                }
264:            }
265:
266:            /**
267:             * Starting from the current position looks for the end of the text run with
268:             * constant text attributes.
269:             * @param runStart - start position
270:             * @param maxPos - position where to stop if no run limit found
271:             * @return style run limit
272:             */
273:            int getStyleRunLimit(int runStart, int maxPos) {
274:                try {
275:                    aci.setIndex(runStart);
276:                } catch (IllegalArgumentException e) { // Index out of bounds
277:                    if (runStart < segmentsStart) {
278:                        aci.first();
279:                    } else {
280:                        aci.last();
281:                    }
282:                }
283:
284:                // If we have some extra runs we need to check for their limits
285:                if (forcedFontRunStarts != null) {
286:                    for (int element : forcedFontRunStarts) {
287:                        if (element > runStart) {
288:                            maxPos = Math.min(element, maxPos);
289:                            break;
290:                        }
291:                    }
292:                }
293:
294:                return Math.min(aci.getRunLimit(), maxPos);
295:            }
296:
297:            /**
298:             * Creates segments for the text run with
299:             * constant decoration, font and bidi level
300:             * @param runStart - run start
301:             * @param runEnd - run end
302:             */
303:            public void createSegments(int runStart, int runEnd) {
304:                int endStyleRun, endLevelRun;
305:
306:                // TODO - update levels
307:
308:                int pos = runStart, levelPos;
309:
310:                aci.setIndex(pos);
311:                final int firstRunStart = aci.getRunStart();
312:                Object tdd = decorations.get(new Integer(firstRunStart));
313:                Object fontOrGAttr = fonts.get(new Integer(firstRunStart));
314:
315:                logical2segment = new int[runEnd - runStart];
316:
317:                do {
318:                    endStyleRun = getStyleRunLimit(pos, runEnd);
319:
320:                    // runStart can be non-zero, but all arrays will be indexed from 0
321:                    int ajustedPos = pos - runStart;
322:                    int ajustedEndStyleRun = endStyleRun - runStart;
323:                    levelPos = ajustedPos;
324:                    do {
325:                        endLevelRun = getLevelRunLimit(levelPos,
326:                                ajustedEndStyleRun);
327:
328:                        if (fontOrGAttr instanceof  GraphicAttribute) {
329:                            runSegments
330:                                    .add(new TextRunSegmentImpl.TextRunSegmentGraphic(
331:                                            (GraphicAttribute) fontOrGAttr,
332:                                            endLevelRun - levelPos, levelPos
333:                                                    + runStart));
334:                            Arrays.fill(logical2segment, levelPos, endLevelRun,
335:                                    runSegments.size() - 1);
336:                        } else {
337:                            TextRunSegmentImpl.TextSegmentInfo i = new TextRunSegmentImpl.TextSegmentInfo(
338:                                    levels == null ? 0 : levels[ajustedPos],
339:                                    (Font) fontOrGAttr, frc, text, levelPos
340:                                            + runStart, endLevelRun + runStart);
341:
342:                            runSegments
343:                                    .add(new TextRunSegmentImpl.TextRunSegmentCommon(
344:                                            i, (TextDecorator.Decoration) tdd));
345:                            Arrays.fill(logical2segment, levelPos, endLevelRun,
346:                                    runSegments.size() - 1);
347:                        }
348:
349:                        levelPos = endLevelRun;
350:                    } while (levelPos < ajustedEndStyleRun);
351:
352:                    // Prepare next iteration
353:                    pos = endStyleRun;
354:                    tdd = decorations.get(new Integer(pos));
355:                    fontOrGAttr = fonts.get(new Integer(pos));
356:                } while (pos < runEnd);
357:            }
358:
359:            /**
360:             * Checks if text run segments are up to date and creates the new segments if not.
361:             */
362:            public void createAllSegments() {
363:                if (!haveAllSegments
364:                        && (logical2segment == null || logical2segment.length != segmentsEnd
365:                                - segmentsStart)) { // Check if we don't have all segments yet
366:                    resetSegments();
367:                    createSegments(segmentsStart, segmentsEnd);
368:                }
369:
370:                haveAllSegments = true;
371:            }
372:
373:            /**
374:             * Calculates position where line should be broken without
375:             * taking into account word boundaries.
376:             * @param start - start index
377:             * @param maxAdvance - maximum advance, width of the line
378:             * @return position where to break
379:             */
380:            public int getLineBreakIndex(int start, float maxAdvance) {
381:                int breakIndex;
382:                TextRunSegment s = null;
383:
384:                for (int segmentIndex = logical2segment[start]; segmentIndex < runSegments
385:                        .size(); segmentIndex++) {
386:                    s = runSegments.get(segmentIndex);
387:                    breakIndex = s.getCharIndexFromAdvance(maxAdvance, start);
388:
389:                    if (breakIndex < s.getEnd()) {
390:                        return breakIndex;
391:                    }
392:                    maxAdvance -= s.getAdvanceDelta(start, s.getEnd());
393:                    start = s.getEnd();
394:                }
395:
396:                return s.getEnd();
397:            }
398:
399:            /**
400:             * Inserts character into the managed text.
401:             * @param newParagraph - new character iterator
402:             * @param insertPos - insertion position
403:             */
404:            public void insertChar(AttributedCharacterIterator newParagraph,
405:                    int insertPos) {
406:                aci = newParagraph;
407:
408:                char insChar = aci.setIndex(insertPos);
409:
410:                Integer key = new Integer(insertPos);
411:
412:                insertPos -= aci.getBeginIndex();
413:
414:                char newText[] = new char[text.length + 1];
415:                System.arraycopy(text, 0, newText, 0, insertPos);
416:                newText[insertPos] = insChar;
417:                System.arraycopy(text, insertPos, newText, insertPos + 1,
418:                        text.length - insertPos);
419:                text = newText;
420:
421:                if (aci.getRunStart() == key.intValue()
422:                        && aci.getRunLimit() == key.intValue() + 1) {
423:                    createStyleRuns(); // We have to create one new run, could be optimized
424:                } else {
425:                    shiftStyleRuns(key, 1);
426:                }
427:
428:                resetSegments();
429:
430:                segmentsEnd++;
431:            }
432:
433:            /**
434:             * Deletes character from the managed text.
435:             * @param newParagraph - new character iterator
436:             * @param deletePos - deletion position
437:             */
438:            public void deleteChar(AttributedCharacterIterator newParagraph,
439:                    int deletePos) {
440:                aci = newParagraph;
441:
442:                Integer key = new Integer(deletePos);
443:
444:                deletePos -= aci.getBeginIndex();
445:
446:                char newText[] = new char[text.length - 1];
447:                System.arraycopy(text, 0, newText, 0, deletePos);
448:                System.arraycopy(text, deletePos + 1, newText, deletePos,
449:                        newText.length - deletePos);
450:                text = newText;
451:
452:                if (fonts.get(key) != null) {
453:                    fonts.remove(key);
454:                }
455:
456:                shiftStyleRuns(key, -1);
457:
458:                resetSegments();
459:
460:                segmentsEnd--;
461:            }
462:
463:            /**
464:             * Shift all runs after specified position, needed to perfom insertion
465:             * or deletion in the managed text
466:             * @param pos - position where to start
467:             * @param shift - shift, could be negative
468:             */
469:            private void shiftStyleRuns(Integer pos, final int shift) {
470:                ArrayList<Integer> keys = new ArrayList<Integer>();
471:
472:                Integer key, oldkey;
473:                for (Iterator<Integer> it = fonts.keySet().iterator(); it
474:                        .hasNext();) {
475:                    oldkey = it.next();
476:                    if (oldkey.intValue() > pos.intValue()) {
477:                        keys.add(oldkey);
478:                    }
479:                }
480:
481:                for (int i = 0; i < keys.size(); i++) {
482:                    oldkey = keys.get(i);
483:                    key = new Integer(shift + oldkey.intValue());
484:                    fonts.put(key, fonts.remove(oldkey));
485:                    decorations.put(key, decorations.remove(oldkey));
486:                }
487:            }
488:
489:            /**
490:             * Resets state of the class
491:             */
492:            private void resetSegments() {
493:                runSegments = new ArrayList<TextRunSegment>();
494:                logical2segment = null;
495:                segment2visual = null;
496:                visual2segment = null;
497:                levels = null;
498:                haveAllSegments = false;
499:            }
500:
501:            private class SegmentsInfo {
502:                ArrayList<TextRunSegment> runSegments;
503:                int logical2segment[];
504:                int segment2visual[];
505:                int visual2segment[];
506:                byte levels[];
507:                int segmentsStart;
508:                int segmentsEnd;
509:            }
510:
511:            /**
512:             * Saves the internal state of the class
513:             * @param newSegStart - new start index in the text
514:             * @param newSegEnd - new end index in the text
515:             */
516:            public void pushSegments(int newSegStart, int newSegEnd) {
517:                storedSegments = new SegmentsInfo();
518:                storedSegments.runSegments = this .runSegments;
519:                storedSegments.logical2segment = this .logical2segment;
520:                storedSegments.segment2visual = this .segment2visual;
521:                storedSegments.visual2segment = this .visual2segment;
522:                storedSegments.levels = this .levels;
523:                storedSegments.segmentsStart = segmentsStart;
524:                storedSegments.segmentsEnd = segmentsEnd;
525:
526:                resetSegments();
527:
528:                segmentsStart = newSegStart;
529:                segmentsEnd = newSegEnd;
530:            }
531:
532:            /**
533:             * Restores the internal state of the class
534:             */
535:            public void popSegments() {
536:                if (storedSegments == null) {
537:                    return;
538:                }
539:
540:                this .runSegments = storedSegments.runSegments;
541:                this .logical2segment = storedSegments.logical2segment;
542:                this .segment2visual = storedSegments.segment2visual;
543:                this .visual2segment = storedSegments.visual2segment;
544:                this .levels = storedSegments.levels;
545:                this .segmentsStart = storedSegments.segmentsStart;
546:                this .segmentsEnd = storedSegments.segmentsEnd;
547:                storedSegments = null;
548:
549:                if (runSegments.size() == 0 && logical2segment == null) {
550:                    haveAllSegments = false;
551:                } else {
552:                    haveAllSegments = true;
553:                }
554:            }
555:
556:            @Override
557:            public Object clone() {
558:                try {
559:                    TextRunBreaker res = (TextRunBreaker) super .clone();
560:                    res.storedSegments = null;
561:                    ArrayList<TextRunSegment> newSegments = new ArrayList<TextRunSegment>(
562:                            runSegments.size());
563:                    for (int i = 0; i < runSegments.size(); i++) {
564:                        TextRunSegment seg = runSegments.get(i);
565:                        newSegments.add((TextRunSegment) seg.clone());
566:                    }
567:                    res.runSegments = newSegments;
568:                    return res;
569:                } catch (CloneNotSupportedException e) {
570:                    // awt.3E=Clone not supported
571:                    throw new UnsupportedOperationException(Messages
572:                            .getString("awt.3E")); //$NON-NLS-1$
573:                }
574:            }
575:
576:            @Override
577:            public boolean equals(Object obj) {
578:                if (!(obj instanceof  TextRunBreaker)) {
579:                    return false;
580:                }
581:
582:                TextRunBreaker br = (TextRunBreaker) obj;
583:
584:                if (br.getACI().equals(aci) && br.frc.equals(frc)) {
585:                    return true;
586:                }
587:
588:                return false;
589:            }
590:
591:            @Override
592:            public int hashCode() {
593:                return HashCode.combine(aci.hashCode(), frc.hashCode());
594:            }
595:
596:            /**
597:             * Renders the managed text
598:             * @param g2d - graphics where to render
599:             * @param xOffset - offset in X direction to the upper left corner
600:             * of the layout from the origin of the graphics
601:             * @param yOffset - offset in Y direction to the upper left corner
602:             * of the layout from the origin of the graphics
603:             */
604:            public void drawSegments(Graphics2D g2d, float xOffset,
605:                    float yOffset) {
606:                for (int i = 0; i < runSegments.size(); i++) {
607:                    runSegments.get(i).draw(g2d, xOffset, yOffset);
608:                }
609:            }
610:
611:            /**
612:             * Creates the black box bounds shape
613:             * @param firstEndpoint - start position
614:             * @param secondEndpoint - end position
615:             * @return black box bounds shape
616:             */
617:            public Shape getBlackBoxBounds(int firstEndpoint, int secondEndpoint) {
618:                GeneralPath bounds = new GeneralPath();
619:
620:                TextRunSegment segment;
621:
622:                for (int idx = firstEndpoint; idx < secondEndpoint; idx = segment
623:                        .getEnd()) {
624:                    segment = runSegments.get(logical2segment[idx]);
625:                    bounds.append(segment.getCharsBlackBoxBounds(idx,
626:                            secondEndpoint), false);
627:                }
628:
629:                return bounds;
630:            }
631:
632:            /**
633:             * Creates visual bounds shape
634:             * @return visual bounds rectangle
635:             */
636:            public Rectangle2D getVisualBounds() {
637:                Rectangle2D bounds = null;
638:
639:                for (int i = 0; i < runSegments.size(); i++) {
640:                    TextRunSegment s = runSegments.get(i);
641:                    if (bounds != null) {
642:                        Rectangle2D.union(bounds, s.getVisualBounds(), bounds);
643:                    } else {
644:                        bounds = s.getVisualBounds();
645:                    }
646:                }
647:
648:                return bounds;
649:            }
650:
651:            /**
652:             * Creates logical bounds shape
653:             * @return logical bounds rectangle
654:             */
655:            public Rectangle2D getLogicalBounds() {
656:                Rectangle2D bounds = null;
657:
658:                for (int i = 0; i < runSegments.size(); i++) {
659:                    TextRunSegment s = runSegments.get(i);
660:                    if (bounds != null) {
661:                        Rectangle2D.union(bounds, s.getLogicalBounds(), bounds);
662:                    } else {
663:                        bounds = s.getLogicalBounds();
664:                    }
665:                }
666:
667:                return bounds;
668:            }
669:
670:            public int getCharCount() {
671:                return segmentsEnd - segmentsStart;
672:            }
673:
674:            public byte getLevel(int idx) {
675:                if (levels == null) {
676:                    return 0;
677:                }
678:                return levels[idx];
679:            }
680:
681:            public int getBaseLevel() {
682:                return 0;
683:            }
684:
685:            public boolean isLTR() {
686:                return true;
687:            }
688:
689:            public char getChar(int index) {
690:                return text[index];
691:            }
692:
693:            public AttributedCharacterIterator getACI() {
694:                return aci;
695:            }
696:
697:            /**
698:             * Creates outline shape for the managed text
699:             * @return outline
700:             */
701:            public GeneralPath getOutline() {
702:                GeneralPath outline = new GeneralPath();
703:
704:                TextRunSegment segment;
705:
706:                for (int i = 0; i < runSegments.size(); i++) {
707:                    segment = runSegments.get(i);
708:                    outline.append(segment.getOutline(), false);
709:                }
710:
711:                return outline;
712:            }
713:
714:            /**
715:             * Calculates text hit info from the screen coordinates.
716:             * Current implementation totally ignores Y coordinate.
717:             * If X coordinate is outside of the layout boundaries, this
718:             * method returns leftmost or rightmost hit.
719:             * @param x - x coordinate of the hit
720:             * @param y - y coordinate of the hit
721:             * @return hit info
722:             */
723:            public TextHitInfo hitTest(float x, float y) {
724:                TextRunSegment segment;
725:
726:                double endOfPrevSeg = -1;
727:                for (int i = 0; i < runSegments.size(); i++) {
728:                    segment = runSegments.get(i);
729:                    Rectangle2D bounds = segment.getVisualBounds();
730:                    if ((bounds.getMinX() <= x && bounds.getMaxX() >= x) || // We are in the segment
731:                            (endOfPrevSeg < x && bounds.getMinX() > x)) { // We are somewhere between the segments
732:                        return segment.hitTest(x, y);
733:                    }
734:                    endOfPrevSeg = bounds.getMaxX();
735:                }
736:
737:                return isLTR() ? TextHitInfo.trailing(text.length)
738:                        : TextHitInfo.leading(0);
739:            }
740:
741:            public float getJustification() {
742:                return justification;
743:            }
744:
745:            /**
746:             * Calculates position of the last non whitespace character
747:             * in the managed text.
748:             * @return position of the last non whitespace character
749:             */
750:            public int getLastNonWhitespace() {
751:                int lastNonWhitespace = text.length;
752:
753:                while (lastNonWhitespace >= 0) {
754:                    lastNonWhitespace--;
755:                    if (!Character.isWhitespace(text[lastNonWhitespace])) {
756:                        break;
757:                    }
758:                }
759:
760:                return lastNonWhitespace;
761:            }
762:
763:            /**
764:             * Performs justification of the managed text by changing segment positions
765:             * and positions of the glyphs inside of the segments.
766:             * @param gap - amount of space which should be compensated by justification
767:             */
768:            public void justify(float gap) {
769:                // Ignore trailing logical whitespace
770:                int firstIdx = segmentsStart;
771:                int lastIdx = getLastNonWhitespace() + segmentsStart;
772:                JustificationInfo jInfos[] = new JustificationInfo[5];
773:                float gapLeft = gap;
774:
775:                int highestPriority = -1;
776:                // GlyphJustificationInfo.PRIORITY_KASHIDA is 0
777:                // GlyphJustificationInfo.PRIORITY_NONE is 3
778:                for (int priority = 0; priority <= GlyphJustificationInfo.PRIORITY_NONE + 1; priority++) {
779:                    JustificationInfo jInfo = new JustificationInfo();
780:                    jInfo.lastIdx = lastIdx;
781:                    jInfo.firstIdx = firstIdx;
782:                    jInfo.grow = gap > 0;
783:                    jInfo.gapToFill = gapLeft;
784:
785:                    if (priority <= GlyphJustificationInfo.PRIORITY_NONE) {
786:                        jInfo.priority = priority;
787:                    } else {
788:                        jInfo.priority = highestPriority; // Last pass
789:                    }
790:
791:                    for (int i = 0; i < runSegments.size(); i++) {
792:                        TextRunSegment segment = runSegments.get(i);
793:                        if (segment.getStart() <= lastIdx) {
794:                            segment.updateJustificationInfo(jInfo);
795:                        }
796:                    }
797:
798:                    if (jInfo.priority == highestPriority) {
799:                        jInfo.absorb = true;
800:                        jInfo.absorbedWeight = jInfo.weight;
801:                    }
802:
803:                    if (jInfo.weight != 0) {
804:                        if (highestPriority < 0) {
805:                            highestPriority = priority;
806:                        }
807:                        jInfos[priority] = jInfo;
808:                    } else {
809:                        continue;
810:                    }
811:
812:                    gapLeft -= jInfo.growLimit;
813:
814:                    if (((gapLeft > 0) ^ jInfo.grow) || gapLeft == 0) {
815:                        gapLeft = 0;
816:                        jInfo.gapPerUnit = jInfo.gapToFill / jInfo.weight;
817:                        break;
818:                    }
819:                    jInfo.useLimits = true;
820:
821:                    if (jInfo.absorbedWeight > 0) {
822:                        jInfo.absorb = true;
823:                        jInfo.absorbedGapPerUnit = (jInfo.gapToFill - jInfo.growLimit)
824:                                / jInfo.absorbedWeight;
825:                        break;
826:                    }
827:                }
828:
829:                float currJustificationOffset = 0;
830:                for (int i = 0; i < runSegments.size(); i++) {
831:                    TextRunSegment segment = runSegments
832:                            .get(getSegmentFromVisualOrder(i));
833:                    segment.x += currJustificationOffset;
834:                    currJustificationOffset += segment.doJustification(jInfos);
835:                }
836:
837:                justification = -1; // Make further justification impossible
838:            }
839:
840:            /**
841:             * This class represents the information collected before the actual
842:             * justification is started and needed to perform the justification.
843:             * This information is closely related to the information stored in the
844:             * GlyphJustificationInfo for the text represented by glyph vectors.
845:             */
846:            class JustificationInfo {
847:                boolean grow;
848:                boolean absorb = false;
849:                boolean useLimits = false;
850:                int priority = 0;
851:                float weight = 0;
852:                float absorbedWeight = 0;
853:                float growLimit = 0;
854:
855:                int lastIdx;
856:                int firstIdx;
857:
858:                float gapToFill;
859:
860:                float gapPerUnit = 0; // Precalculated value, gapToFill / weight
861:                float absorbedGapPerUnit = 0; // Precalculated value, gapToFill / weight
862:            }
863:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.