KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > java > awt > font > TextLine


1 /*
2  * @(#)TextLine.java 1.52 03/12/19
3  *
4  * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7
8 /*
9  * (C) Copyright IBM Corp. 1998-2003, All Rights Reserved
10  *
11  */

12
13 package java.awt.font;
14
15 import java.awt.Font JavaDoc;
16 import java.awt.Graphics2D JavaDoc;
17 import java.awt.Shape JavaDoc;
18
19 import java.awt.geom.Rectangle2D JavaDoc;
20 import java.awt.geom.AffineTransform JavaDoc;
21 import java.awt.geom.GeneralPath JavaDoc;
22
23 import java.awt.im.InputMethodHighlight JavaDoc;
24
25 import java.text.CharacterIterator JavaDoc;
26 import java.text.AttributedCharacterIterator JavaDoc;
27 import java.text.Annotation JavaDoc;
28 import java.text.Bidi JavaDoc;
29
30 import java.util.Map JavaDoc;
31 import java.util.Hashtable JavaDoc;
32
33 import sun.font.BidiUtils;
34 import sun.font.CoreMetrics;
35 import sun.font.Decoration;
36 import sun.font.FontLineMetrics;
37 import sun.font.FontResolver;
38 import sun.font.GraphicComponent;
39 import sun.font.TextLabelFactory;
40 import sun.font.TextLineComponent;
41 import sun.text.CodePointIterator;
42
43 final class TextLine {
44
45     static final class TextLineMetrics {
46         public final float ascent;
47         public final float descent;
48         public final float leading;
49         public final float advance;
50
51         public TextLineMetrics(float ascent,
52                            float descent,
53                            float leading,
54                            float advance) {
55             this.ascent = ascent;
56             this.descent = descent;
57             this.leading = leading;
58             this.advance = advance;
59         }
60     }
61
62     private TextLineComponent[] fComponents;
63     private float[] fBaselineOffsets;
64     private int[] fComponentVisualOrder; // if null, ltr
65
private float[] locs; // x,y pairs for components in visual order
66
private char[] fChars;
67     private int fCharsStart;
68     private int fCharsLimit;
69     private int[] fCharVisualOrder; // if null, ltr
70
private int[] fCharLogicalOrder; // if null, ltr
71
private byte[] fCharLevels; // if null, 0
72
private boolean fIsDirectionLTR;
73
74     private TextLineMetrics fMetrics = null; // built on demand in getMetrics
75

76     public TextLine(TextLineComponent[] components,
77                     float[] baselineOffsets,
78                     char[] chars,
79                     int charsStart,
80                     int charsLimit,
81                     int[] charLogicalOrder,
82                     byte[] charLevels,
83                     boolean isDirectionLTR) {
84
85         int[] componentVisualOrder = computeComponentOrder(components,
86                                                            charLogicalOrder);
87
88         fComponents = components;
89         fBaselineOffsets = baselineOffsets;
90         fComponentVisualOrder = componentVisualOrder;
91         fChars = chars;
92         fCharsStart = charsStart;
93         fCharsLimit = charsLimit;
94         fCharLogicalOrder = charLogicalOrder;
95         fCharLevels = charLevels;
96         fIsDirectionLTR = isDirectionLTR;
97         checkCtorArgs();
98
99         init();
100     }
101
102     private void checkCtorArgs() {
103
104         int checkCharCount = 0;
105         for (int i=0; i < fComponents.length; i++) {
106             checkCharCount += fComponents[i].getNumCharacters();
107         }
108
109         if (checkCharCount != this.characterCount()) {
110             throw new IllegalArgumentException JavaDoc("Invalid TextLine! " +
111                                 "char count is different from " +
112                                 "sum of char counts of components.");
113         }
114     }
115
116     private void init() {
117
118     // first, we need to check for graphic components on the TOP or BOTTOM baselines. So
119
// we perform the work that used to be in getMetrics here.
120

121     float ascent = 0;
122     float descent = 0;
123     float leading = 0;
124     float advance = 0;
125
126     // ascent + descent must not be less than this value
127
float maxGraphicHeight = 0;
128     float maxGraphicHeightWithLeading = 0;
129
130     // walk through EGA's
131
TextLineComponent tlc;
132     boolean fitTopAndBottomGraphics = false;
133
134     for (int i = 0; i < fComponents.length; i++) {
135         tlc = fComponents[i];
136
137         CoreMetrics cm = tlc.getCoreMetrics();
138         byte baseline = (byte)cm.baselineIndex;
139
140         if (baseline >= 0) {
141         float baselineOffset = fBaselineOffsets[baseline];
142
143         ascent = Math.max(ascent, -baselineOffset + cm.ascent);
144
145         float gd = baselineOffset + cm.descent;
146         descent = Math.max(descent, gd);
147
148         leading = Math.max(leading, gd + cm.leading);
149         }
150         else {
151         fitTopAndBottomGraphics = true;
152         float graphicHeight = cm.ascent + cm.descent;
153         float graphicHeightWithLeading = graphicHeight + cm.leading;
154         maxGraphicHeight = Math.max(maxGraphicHeight, graphicHeight);
155         maxGraphicHeightWithLeading = Math.max(maxGraphicHeightWithLeading,
156                                graphicHeightWithLeading);
157         }
158     }
159
160     if (fitTopAndBottomGraphics) {
161         if (maxGraphicHeight > ascent + descent) {
162         descent = maxGraphicHeight - ascent;
163         }
164         if (maxGraphicHeightWithLeading > ascent + leading) {
165         leading = maxGraphicHeightWithLeading - ascent;
166         }
167     }
168
169     leading -= descent;
170
171     // we now know enough to compute the locs, but we need the final loc
172
// for the advance before we can create the metrics object
173

174     if (fitTopAndBottomGraphics) {
175         // we have top or bottom baselines, so expand the baselines array
176
// full offsets are needed by CoreMetrics.effectiveBaselineOffset
177
fBaselineOffsets = new float[] {
178         fBaselineOffsets[0],
179         fBaselineOffsets[1],
180         fBaselineOffsets[2],
181         descent,
182         -ascent
183         };
184     }
185         
186         float x = 0;
187     float y = 0;
188         CoreMetrics pcm = null;
189
190         locs = new float[fComponents.length * 2 + 2];
191
192         for (int i = 0, n = 0; i < fComponents.length; ++i, n += 2) {
193             int vi = fComponentVisualOrder == null ? i : fComponentVisualOrder[i];
194             
195             tlc = fComponents[vi];
196             CoreMetrics cm = tlc.getCoreMetrics();
197
198             if ((pcm != null) &&
199                 (pcm.italicAngle != 0 || cm.italicAngle != 0) && // adjust because of italics
200
(pcm.italicAngle != cm.italicAngle ||
201          pcm.baselineIndex != cm.baselineIndex ||
202          pcm.ssOffset != cm.ssOffset)) {
203
204                         // 1) compute the area of overlap - min effective ascent and min effective descent
205
// 2) compute the x positions along italic angle of ascent and descent for left and right
206
// 3) compute maximum left - right, adjust right position by this value
207
// this is a crude form of kerning between textcomponents
208

209                         // 1)
210
float pb = pcm.effectiveBaselineOffset(fBaselineOffsets);
211                         float pa = pb - pcm.ascent;
212                         float pd = pb + pcm.descent;
213                         pb += pcm.ssOffset;
214                         
215             float cb = cm.effectiveBaselineOffset(fBaselineOffsets);
216                         float ca = cb - cm.ascent;
217                         float cd = cb + cm.descent;
218                         cb += cm.ssOffset;
219
220                         float a = Math.max(pa, ca);
221                         float d = Math.min(pd, cd);
222
223                         // 2)
224
float pax = pcm.italicAngle * (pb - a);
225                         float pdx = pcm.italicAngle * (pb - d);
226
227                         float cax = cm.italicAngle * (cb - a);
228                         float cdx = cm.italicAngle * (cb - d);
229
230                         // 3)
231
float dax = pax - cax;
232                         float ddx = pdx - cdx;
233                         float dx = Math.max(dax, ddx);
234
235                         x += dx;
236             y = cb;
237             } else {
238         // no italic adjustment for x, but still need to compute y
239
y = cm.effectiveBaselineOffset(fBaselineOffsets) + cm.ssOffset;
240         }
241
242             locs[n] = x;
243             locs[n+1] = y;
244
245             x += tlc.getAdvance();
246             pcm = cm;
247         }
248
249     // do we want italic padding at the right of the line?
250
if (pcm.italicAngle != 0) {
251         float pb = pcm.effectiveBaselineOffset(fBaselineOffsets);
252         float pa = pb - pcm.ascent;
253         float pd = pb + pcm.descent;
254         pb += pcm.ssOffset;
255
256             float d;
257         if (pcm.italicAngle > 0) {
258         d = pb + pcm.ascent;
259         } else {
260         d = pb - pcm.descent;
261         }
262         d *= pcm.italicAngle;
263
264         x += d;
265     }
266     locs[locs.length - 2] = x;
267     // locs[locs.length - 1] = 0; // final offset is always back on baseline
268

269     // ok, build fMetrics since we have the final advance
270
advance = x;
271     fMetrics = new TextLineMetrics(ascent, descent, leading, advance);
272     }
273
274     private abstract static class Function {
275
276         abstract float computeFunction(TextLine JavaDoc line,
277                                        int componentIndex,
278                                        int indexInArray);
279     }
280
281     private static Function fgPosAdvF = new Function() {
282         float computeFunction(TextLine JavaDoc line,
283                               int componentIndex,
284                               int indexInArray) {
285
286             TextLineComponent tlc = line.fComponents[componentIndex];
287             int vi = line.fComponentVisualOrder == null
288                 ? componentIndex
289                 : line.fComponentVisualOrder[componentIndex];
290             return line.locs[vi * 2] + tlc.getCharX(indexInArray) + tlc.getCharAdvance(indexInArray);
291         }
292     };
293
294     private static Function fgAdvanceF = new Function() {
295
296         float computeFunction(TextLine JavaDoc line,
297                               int componentIndex,
298                               int indexInArray) {
299
300             TextLineComponent tlc = line.fComponents[componentIndex];
301             return tlc.getCharAdvance(indexInArray);
302         }
303     };
304
305     private static Function fgXPositionF = new Function() {
306
307         float computeFunction(TextLine JavaDoc line,
308                               int componentIndex,
309                               int indexInArray) {
310
311             int vi = line.fComponentVisualOrder == null
312                 ? componentIndex
313                 : line.fComponentVisualOrder[componentIndex];
314             TextLineComponent tlc = line.fComponents[componentIndex];
315             return line.locs[vi * 2] + tlc.getCharX(indexInArray);
316         }
317     };
318
319     private static Function fgYPositionF = new Function() {
320
321         float computeFunction(TextLine JavaDoc line,
322                               int componentIndex,
323                               int indexInArray) {
324
325             TextLineComponent tlc = line.fComponents[componentIndex];
326             float charPos = tlc.getCharY(indexInArray);
327
328             // charPos is relative to the component - adjust for
329
// baseline
330

331             return charPos + line.getComponentShift(componentIndex);
332         }
333     };
334
335     public int characterCount() {
336
337         return fCharsLimit - fCharsStart;
338     }
339
340     public boolean isDirectionLTR() {
341
342         return fIsDirectionLTR;
343     }
344
345     public TextLineMetrics getMetrics() {
346     return fMetrics;
347     }
348
349     public int visualToLogical(int visualIndex) {
350
351         if (fCharLogicalOrder == null) {
352         return visualIndex;
353     }
354
355     if (fCharVisualOrder == null) {
356         fCharVisualOrder = BidiUtils.createInverseMap(fCharLogicalOrder);
357     }
358  
359         return fCharVisualOrder[visualIndex];
360     }
361
362     public int logicalToVisual(int logicalIndex) {
363
364         return (fCharLogicalOrder == null)?
365             logicalIndex : fCharLogicalOrder[logicalIndex];
366     }
367
368     public byte getCharLevel(int logicalIndex) {
369
370         return fCharLevels==null? 0 : fCharLevels[logicalIndex];
371     }
372
373     public boolean isCharLTR(int logicalIndex) {
374
375         return (getCharLevel(logicalIndex) & 0x1) == 0;
376     }
377
378     public int getCharType(int logicalIndex) {
379
380         return Character.getType(fChars[logicalIndex + fCharsStart]);
381     }
382
383     public boolean isCharSpace(int logicalIndex) {
384
385         return Character.isSpaceChar(fChars[logicalIndex + fCharsStart]);
386     }
387
388     public boolean isCharWhitespace(int logicalIndex) {
389
390         return Character.isWhitespace(fChars[logicalIndex + fCharsStart]);
391     }
392
393     public float getCharAngle(int logicalIndex) {
394
395         return getCoreMetricsAt(logicalIndex).italicAngle;
396     }
397
398     public CoreMetrics getCoreMetricsAt(int logicalIndex) {
399
400         if (logicalIndex < 0) {
401             throw new IllegalArgumentException JavaDoc("Negative logicalIndex.");
402         }
403
404         if (logicalIndex > fCharsLimit - fCharsStart) {
405             throw new IllegalArgumentException JavaDoc("logicalIndex too large.");
406         }
407
408         int currentTlc = 0;
409         int tlcStart = 0;
410         int tlcLimit = 0;
411
412         do {
413             tlcLimit += fComponents[currentTlc].getNumCharacters();
414             if (tlcLimit > logicalIndex) {
415                 break;
416             }
417             ++currentTlc;
418             tlcStart = tlcLimit;
419         } while(currentTlc < fComponents.length);
420
421         return fComponents[currentTlc].getCoreMetrics();
422     }
423
424     public float getCharAscent(int logicalIndex) {
425
426         return getCoreMetricsAt(logicalIndex).ascent;
427     }
428
429     public float getCharDescent(int logicalIndex) {
430
431         return getCoreMetricsAt(logicalIndex).descent;
432     }
433
434     public float getCharShift(int logicalIndex) {
435
436         return getCoreMetricsAt(logicalIndex).ssOffset;
437     }
438
439     private float applyFunctionAtIndex(int logicalIndex, Function f) {
440
441         if (logicalIndex < 0) {
442             throw new IllegalArgumentException JavaDoc("Negative logicalIndex.");
443         }
444
445         int tlcStart = 0;
446
447         for(int i=0; i < fComponents.length; i++) {
448
449             int tlcLimit = tlcStart + fComponents[i].getNumCharacters();
450             if (tlcLimit > logicalIndex) {
451                 return f.computeFunction(this, i, logicalIndex - tlcStart);
452             }
453             else {
454                 tlcStart = tlcLimit;
455             }
456         }
457
458         throw new IllegalArgumentException JavaDoc("logicalIndex too large.");
459     }
460
461     public float getCharAdvance(int logicalIndex) {
462
463         return applyFunctionAtIndex(logicalIndex, fgAdvanceF);
464     }
465
466     public float getCharXPosition(int logicalIndex) {
467
468         return applyFunctionAtIndex(logicalIndex, fgXPositionF);
469     }
470
471     public float getCharYPosition(int logicalIndex) {
472
473         return applyFunctionAtIndex(logicalIndex, fgYPositionF);
474     }
475
476     public float getCharLinePosition(int logicalIndex) {
477
478         return getCharXPosition(logicalIndex);
479     }
480
481     public float getCharLinePosition(int logicalIndex, boolean leading) {
482         Function f = isCharLTR(logicalIndex) == leading ? fgXPositionF : fgPosAdvF;
483         return applyFunctionAtIndex(logicalIndex, f);
484     }
485
486     public boolean caretAtOffsetIsValid(int offset) {
487
488         if (offset < 0) {
489             throw new IllegalArgumentException JavaDoc("Negative offset.");
490         }
491
492         int tlcStart = 0;
493
494         for(int i=0; i < fComponents.length; i++) {
495
496             int tlcLimit = tlcStart + fComponents[i].getNumCharacters();
497             if (tlcLimit > offset) {
498                 return fComponents[i].caretAtOffsetIsValid(offset-tlcStart);
499             }
500             else {
501                 tlcStart = tlcLimit;
502             }
503         }
504
505         throw new IllegalArgumentException JavaDoc("logicalIndex too large.");
506     }
507
508     public Rectangle2D JavaDoc getCharBounds(int logicalIndex) {
509
510         if (logicalIndex < 0) {
511             throw new IllegalArgumentException JavaDoc("Negative logicalIndex.");
512         }
513
514         int tlcStart = 0;
515
516         for (int i=0; i < fComponents.length; i++) {
517
518             int tlcLimit = tlcStart + fComponents[i].getNumCharacters();
519             if (tlcLimit > logicalIndex) {
520
521                 TextLineComponent tlc = fComponents[i];
522                 int indexInTlc = logicalIndex - tlcStart;
523                 Rectangle2D JavaDoc chBounds = tlc.getCharVisualBounds(indexInTlc);
524
525                 int vi = fComponentVisualOrder == null ? i : fComponentVisualOrder[i];
526                 chBounds.setRect(chBounds.getX() + locs[vi * 2],
527                                  chBounds.getY() + locs[vi * 2 + 1],
528                                  chBounds.getWidth(),
529                                  chBounds.getHeight());
530                 return chBounds;
531             }
532             else {
533                 tlcStart = tlcLimit;
534             }
535         }
536
537         throw new IllegalArgumentException JavaDoc("logicalIndex too large.");
538     }
539
540     private float getComponentShift(int index) {
541     CoreMetrics cm = fComponents[index].getCoreMetrics();
542     return cm.effectiveBaselineOffset(fBaselineOffsets);
543     }
544
545     public void draw(Graphics2D JavaDoc g2, float x, float y) {
546
547         for (int i = 0, n = 0; i < fComponents.length; i++, n += 2) {
548             int vi = fComponentVisualOrder==null? i : fComponentVisualOrder[i];
549             TextLineComponent tlc = fComponents[vi];
550             tlc.draw(g2, locs[n] + x, locs[n+1] + y);
551         }
552     }
553
554     /** return the union of the visual bounds of all the components */
555
556     public Rectangle2D JavaDoc getBounds() {
557
558         float left = Float.MAX_VALUE, right = -Float.MAX_VALUE;
559         float top = Float.MAX_VALUE, bottom = -Float.MAX_VALUE;
560
561         for (int i=0, n = 0; i < fComponents.length; i++, n += 2) {
562             int vi = fComponentVisualOrder==null? i : fComponentVisualOrder[i];
563             TextLineComponent tlc = fComponents[vi];
564
565             Rectangle2D JavaDoc tlcBounds = tlc.getVisualBounds();
566             float x = locs[n];
567             float y = locs[n+1];
568
569             left = Math.min(left, x + (float)tlcBounds.getX());
570             right = Math.max(right, x + (float)tlcBounds.getMaxX());
571
572             top = Math.min(top, y + (float)tlcBounds.getY());
573             bottom = Math.max(bottom, y + (float)tlcBounds.getMaxY());
574         }
575
576         return new Rectangle2D.Float JavaDoc(left, top, right-left, bottom-top);
577     }
578
579     public Rectangle2D JavaDoc getItalicBounds() {
580         
581         float left = Float.MAX_VALUE, right = -Float.MAX_VALUE;
582         float top = Float.MAX_VALUE, bottom = -Float.MAX_VALUE;
583
584         for (int i=0, n = 0; i < fComponents.length; i++, n += 2) {
585             int vi = fComponentVisualOrder==null? i : fComponentVisualOrder[i];
586             TextLineComponent tlc = fComponents[vi];
587
588             Rectangle2D JavaDoc tlcBounds = tlc.getItalicBounds();
589             float x = locs[n];
590             float y = locs[n+1];
591
592             left = Math.min(left, x + (float)tlcBounds.getX());
593             right = Math.max(right, x + (float)tlcBounds.getMaxX());
594
595             top = Math.min(top, y + (float)tlcBounds.getY());
596             bottom = Math.max(bottom, y + (float)tlcBounds.getMaxY());
597         }
598
599         return new Rectangle2D.Float JavaDoc(left, top, right-left, bottom-top);
600     }
601
602     public Shape JavaDoc getOutline(AffineTransform JavaDoc tx) {
603
604         GeneralPath JavaDoc dstShape = new GeneralPath JavaDoc(GeneralPath.WIND_NON_ZERO);
605
606         for (int i=0, n = 0; i < fComponents.length; i++, n += 2) {
607             int vi = fComponentVisualOrder==null? i : fComponentVisualOrder[i];
608             TextLineComponent tlc = fComponents[vi];
609
610             dstShape.append(tlc.getOutline(locs[n], locs[n+1]), false);
611         }
612
613         if (tx != null) {
614             dstShape.transform(tx);
615         }
616         return dstShape;
617     }
618
619     public int hashCode() {
620         return (fComponents.length << 16) ^
621                     (fComponents[0].hashCode() << 3) ^ (fCharsLimit-fCharsStart);
622     }
623
624     public String JavaDoc toString() {
625         StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
626
627         for (int i = 0; i < fComponents.length; i++) {
628             buf.append(fComponents[i]);
629         }
630
631         return buf.toString();
632     }
633
634     /**
635      * Create a TextLine from the text. The Font must be able to
636      * display all of the text.
637      * attributes==null is equivalent to using an empty Map for
638      * attributes
639      */

640     public static TextLine JavaDoc fastCreateTextLine(FontRenderContext JavaDoc frc,
641                                               char[] chars,
642                                               Font JavaDoc font,
643                                               CoreMetrics lm,
644                                               Map JavaDoc attributes) {
645
646         boolean isDirectionLTR = true;
647         byte[] levels = null;
648         int[] charsLtoV = null;
649         Bidi JavaDoc bidi = null;
650         int characterCount = chars.length;
651
652         boolean requiresBidi = false;
653         boolean directionKnown = false;
654         byte[] embs = null;
655         if (attributes != null) {
656           try {
657             Boolean JavaDoc runDirection = (Boolean JavaDoc)attributes.get(TextAttribute.RUN_DIRECTION);
658             if (runDirection != null) {
659               directionKnown = true;
660               isDirectionLTR = TextAttribute.RUN_DIRECTION_LTR.equals(runDirection);
661               requiresBidi = !isDirectionLTR;
662             }
663           }
664           catch (ClassCastException JavaDoc e) {
665           }
666
667           try {
668             Integer JavaDoc embeddingLevel = (Integer JavaDoc)attributes.get(TextAttribute.BIDI_EMBEDDING);
669             if (embeddingLevel != null) {
670               int intLevel = embeddingLevel.intValue();
671               if (intLevel >= -61 && intLevel < 62) {
672                 byte level = (byte)intLevel;
673                 requiresBidi = true;
674                 embs = new byte[characterCount];
675                 for (int i = 0; i < embs.length; ++i) {
676                   embs[i] = level;
677                 }
678               }
679             }
680           }
681           catch (ClassCastException JavaDoc e) {
682           }
683         }
684
685         if (!requiresBidi) {
686         requiresBidi = Bidi.requiresBidi(chars, 0, chars.length);
687         }
688
689         if (requiresBidi) {
690       int bidiflags = Bidi.DIRECTION_DEFAULT_LEFT_TO_RIGHT;
691           if (directionKnown) {
692           if (isDirectionLTR) {
693           bidiflags = Bidi.DIRECTION_LEFT_TO_RIGHT;
694           } else {
695           bidiflags = Bidi.DIRECTION_RIGHT_TO_LEFT;
696           }
697           }
698
699           bidi = new Bidi JavaDoc(chars, 0, embs, 0, chars.length, bidiflags);
700       if (!bidi.isLeftToRight()) {
701           levels = BidiUtils.getLevels(bidi);
702           int[] charsVtoL = BidiUtils.createVisualToLogicalMap(levels);
703           charsLtoV = BidiUtils.createInverseMap(charsVtoL);
704           isDirectionLTR = bidi.baseIsLeftToRight();
705       }
706         }
707
708         Decoration decorator;
709         if (attributes != null) {
710             decorator = Decoration.getDecoration(StyledParagraph.addInputMethodAttrs(attributes));
711         }
712         else {
713             decorator = Decoration.getPlainDecoration();
714         }
715     int layoutFlags = 0; // no extra info yet, bidi determines run and line direction
716
TextLabelFactory factory = new TextLabelFactory(frc, chars, bidi, layoutFlags);
717
718         TextLineComponent[] components = new TextLineComponent[1];
719         
720         components = createComponentsOnRun(0, chars.length,
721                                            chars,
722                                            charsLtoV, levels,
723                                            factory, font, lm,
724                                            frc,
725                                            decorator,
726                                            components,
727                                            0);
728                                            
729         int numComponents = components.length;
730         while (components[numComponents-1] == null) {
731             numComponents -= 1;
732         }
733         
734         if (numComponents != components.length) {
735             TextLineComponent[] temp = new TextLineComponent[numComponents];
736             System.arraycopy(components, 0, temp, 0, numComponents);
737             components = temp;
738         }
739         
740         return new TextLine JavaDoc(components, lm.baselineOffsets,
741                             chars, 0, chars.length, charsLtoV, levels, isDirectionLTR);
742     }
743
744     private static TextLineComponent[] expandArray(TextLineComponent[] orig) {
745
746         TextLineComponent[] newComponents = new TextLineComponent[orig.length + 8];
747         System.arraycopy(orig, 0, newComponents, 0, orig.length);
748
749         return newComponents;
750     }
751
752     /**
753      * Returns an array in logical order of the TextLineComponents on
754      * the text in the given range, with the given attributes.
755      */

756     public static TextLineComponent[] createComponentsOnRun(int runStart,
757                                                             int runLimit,
758                                                             char[] chars,
759                                                             int[] charsLtoV,
760                                                             byte[] levels,
761                                                             TextLabelFactory factory,
762                                                             Font JavaDoc font,
763                                                             CoreMetrics cm,
764                                                             FontRenderContext JavaDoc frc,
765                                                             Decoration decorator,
766                                                             TextLineComponent[] components,
767                                                             int numComponents) {
768
769         int pos = runStart;
770         do {
771             int chunkLimit = firstVisualChunk(charsLtoV, levels, pos, runLimit); // <= displayLimit
772

773             do {
774                 int startPos = pos;
775                 int lmCount;
776                 
777                 if (cm == null) {
778                     LineMetrics JavaDoc lineMetrics = font.getLineMetrics(chars, startPos, chunkLimit, frc);
779                     cm = CoreMetrics.get(lineMetrics);
780                     lmCount = lineMetrics.getNumChars();
781                 }
782                 else {
783                     lmCount = (chunkLimit-startPos);
784                 }
785
786                 TextLineComponent nextComponent =
787                     factory.createExtended(font, cm, decorator, startPos, startPos + lmCount);
788
789                 ++numComponents;
790                 if (numComponents >= components.length) {
791                     components = expandArray(components);
792                 }
793
794                 components[numComponents-1] = nextComponent;
795                 
796                 pos += lmCount;
797             } while (pos < chunkLimit);
798
799         } while (pos < runLimit);
800         
801         return components;
802     }
803
804     /**
805      * Returns an array (in logical order) of the TextLineComponents representing
806      * the text. The components are both logically and visually contiguous.
807      */

808     public static TextLineComponent[] getComponents(StyledParagraph JavaDoc styledParagraph,
809                                                     char[] chars,
810                                                     int textStart,
811                                                     int textLimit,
812                                                     int[] charsLtoV,
813                                                     byte[] levels,
814                                                     TextLabelFactory factory) {
815
816         FontRenderContext JavaDoc frc = factory.getFontRenderContext();
817
818         int numComponents = 0;
819         TextLineComponent[] tempComponents = new TextLineComponent[1];
820
821         int pos = textStart;
822         do {
823             int runLimit = Math.min(styledParagraph.getRunLimit(pos), textLimit);
824             
825             Decoration decorator = styledParagraph.getDecorationAt(pos);
826
827             Object JavaDoc graphicOrFont = styledParagraph.getFontOrGraphicAt(pos);
828
829             if (graphicOrFont instanceof GraphicAttribute JavaDoc) {
830                 
831                 GraphicAttribute JavaDoc graphicAttribute = (GraphicAttribute JavaDoc) graphicOrFont;
832                 do {
833                     int chunkLimit = firstVisualChunk(charsLtoV, levels,
834                                     pos, runLimit);
835
836                     GraphicComponent nextGraphic =
837                             new GraphicComponent(graphicAttribute, decorator, charsLtoV, levels, pos, chunkLimit);
838                     pos = chunkLimit;
839
840                     ++numComponents;
841                     if (numComponents >= tempComponents.length) {
842                         tempComponents = expandArray(tempComponents);
843                     }
844
845                     tempComponents[numComponents-1] = nextGraphic;
846
847                 } while(pos < runLimit);
848             }
849             else {
850                 Font JavaDoc font = (Font JavaDoc) graphicOrFont;
851
852                 tempComponents = createComponentsOnRun(pos, runLimit,
853                                                         chars,
854                                                         charsLtoV, levels,
855                                                         factory, font, null,
856                                                         frc,
857                                                         decorator,
858                                                         tempComponents,
859                                                         numComponents);
860                 pos = runLimit;
861                 numComponents = tempComponents.length;
862                 while (tempComponents[numComponents-1] == null) {
863                     numComponents -= 1;
864                 }
865             }
866
867         } while (pos < textLimit);
868
869         TextLineComponent[] components;
870         if (tempComponents.length == numComponents) {
871             components = tempComponents;
872         }
873         else {
874             components = new TextLineComponent[numComponents];
875             System.arraycopy(tempComponents, 0, components, 0, numComponents);
876         }
877
878         return components;
879     }
880
881     /**
882      * Create a TextLine from the Font and character data over the
883      * range. The range is relative to both the StyledParagraph and the
884      * character array.
885      */

886     public static TextLine JavaDoc createLineFromText(char[] chars,
887                                               StyledParagraph JavaDoc styledParagraph,
888                                               TextLabelFactory factory,
889                                               boolean isDirectionLTR,
890                                               float[] baselineOffsets) {
891
892         factory.setLineContext(0, chars.length);
893
894         Bidi JavaDoc lineBidi = factory.getLineBidi();
895         int[] charsLtoV = null;
896         byte[] levels = null;
897
898         if (lineBidi != null) {
899             levels = BidiUtils.getLevels(lineBidi);
900         int[] charsVtoL = BidiUtils.createVisualToLogicalMap(levels);
901             charsLtoV = BidiUtils.createInverseMap(charsVtoL);
902         }
903
904         TextLineComponent[] components =
905             getComponents(styledParagraph, chars, 0, chars.length, charsLtoV, levels, factory);
906
907         return new TextLine JavaDoc(components, baselineOffsets,
908                             chars, 0, chars.length, charsLtoV, levels, isDirectionLTR);
909     }
910
911     /**
912      * Compute the components order from the given components array and
913      * logical-to-visual character mapping. May return null if canonical.
914      */

915     private static int[] computeComponentOrder(TextLineComponent[] components,
916                                                int[] charsLtoV) {
917
918         /*
919          * Create a visual ordering for the glyph sets. The important thing
920          * here is that the values have the proper rank with respect to
921          * each other, not the exact values. For example, the first glyph
922          * set that appears visually should have the lowest value. The last
923          * should have the highest value. The values are then normalized
924          * to map 1-1 with positions in glyphs.
925          *
926          */

927         int[] componentOrder = null;
928         if (charsLtoV != null && components.length > 1) {
929             componentOrder = new int[components.length];
930             int gStart = 0;
931             for (int i = 0; i < components.length; i++) {
932                 componentOrder[i] = charsLtoV[gStart];
933                 gStart += components[i].getNumCharacters();
934             }
935
936             componentOrder = BidiUtils.createContiguousOrder(componentOrder);
937             componentOrder = BidiUtils.createInverseMap(componentOrder);
938         }
939         return componentOrder;
940     }
941
942
943     /**
944      * Create a TextLine from the text. chars is just the text in the iterator.
945      */

946     public static TextLine JavaDoc standardCreateTextLine(FontRenderContext JavaDoc frc,
947                                                   AttributedCharacterIterator JavaDoc text,
948                                                   char[] chars,
949                                                   float[] baselineOffsets) {
950
951         StyledParagraph JavaDoc styledParagraph = new StyledParagraph JavaDoc(text, chars);
952         Bidi JavaDoc bidi = new Bidi JavaDoc(text);
953     if (bidi.isLeftToRight()) {
954         bidi = null;
955     }
956         int layoutFlags = 0; // no extra info yet, bidi determines run and line direction
957
TextLabelFactory factory = new TextLabelFactory(frc, chars, bidi, layoutFlags);
958
959         boolean isDirectionLTR = true;
960         if (bidi != null) {
961             isDirectionLTR = bidi.baseIsLeftToRight();
962         }
963         return createLineFromText(chars, styledParagraph, factory, isDirectionLTR, baselineOffsets);
964     }
965
966
967
968     /*
969      * A utility to get a range of text that is both logically and visually
970      * contiguous.
971      * If the entire range is ok, return limit, otherwise return the first
972      * directional change after start. We could do better than this, but
973      * it doesn't seem worth it at the moment.
974     private static int firstVisualChunk(int order[], byte direction[],
975                                         int start, int limit)
976     {
977         if (order != null) {
978             int min = order[start];
979             int max = order[start];
980             int count = limit - start;
981             for (int i = start + 1; i < limit; i++) {
982                 min = Math.min(min, order[i]);
983                 max = Math.max(max, order[i]);
984                 if (max - min >= count) {
985                     if (direction != null) {
986                         byte baseLevel = direction[start];
987                         for (int j = start + 1; j < i; j++) {
988                             if (direction[j] != baseLevel) {
989                                 return j;
990                             }
991                         }
992                     }
993                     return i;
994                 }
995             }
996         }
997         return limit;
998     }
999      */

1000    
1001    /**
1002     * When this returns, the ACI's current position will be at the start of the
1003     * first run which does NOT contain a GraphicAttribute. If no such run exists
1004     * the ACI's position will be at the end, and this method will return false.
1005     */

1006    static boolean advanceToFirstFont(AttributedCharacterIterator JavaDoc aci) {
1007        
1008        for (char ch = aci.first(); ch != aci.DONE; ch = aci.setIndex(aci.getRunLimit())) {
1009
1010            if (aci.getAttribute(TextAttribute.CHAR_REPLACEMENT) == null) {
1011                return true;
1012            }
1013        }
1014        
1015        return false;
1016    }
1017    
1018    static float[] getNormalizedOffsets(float[] baselineOffsets, byte baseline) {
1019        
1020        if (baselineOffsets[baseline] != 0) {
1021            float base = baselineOffsets[baseline];
1022            float[] temp = new float[baselineOffsets.length];
1023            for (int i = 0; i < temp.length; i++)
1024                temp[i] = baselineOffsets[i] - base;
1025            baselineOffsets = temp;
1026        }
1027        return baselineOffsets;
1028    }
1029    
1030    static Font JavaDoc getFontAtCurrentPos(AttributedCharacterIterator JavaDoc aci) {
1031        
1032        Object JavaDoc value = aci.getAttribute(TextAttribute.FONT);
1033        if (value != null) {
1034            return (Font JavaDoc) value;
1035        }
1036        if (aci.getAttribute(TextAttribute.FAMILY) != null) {
1037            return Font.getFont(aci.getAttributes());
1038        }
1039
1040        int ch = CodePointIterator.create(aci).next();
1041    if (ch != CodePointIterator.DONE) {
1042        FontResolver resolver = FontResolver.getInstance();
1043        return resolver.getFont(resolver.getFontIndex(ch), aci.getAttributes());
1044    }
1045    return null;
1046    }
1047    
1048    /**
1049     * Utility method for getting justification ratio from attributes.
1050     */

1051    static float getJustifyRatio(Map JavaDoc attributes) {
1052        
1053        Object JavaDoc value = attributes.get(TextAttribute.JUSTIFICATION);
1054        
1055        if (value == null) {
1056            return 1;
1057        }
1058        
1059        float justifyRatio = ((Float JavaDoc)value).floatValue();
1060        if (justifyRatio < 0) {
1061            justifyRatio = 0;
1062        }
1063        else if (justifyRatio > 1) {
1064            justifyRatio = 1;
1065        }
1066        
1067        return justifyRatio;
1068    }
1069
1070  /*
1071   * The new version requires that chunks be at the same level.
1072   */

1073    private static int firstVisualChunk(int order[], byte direction[],
1074                                        int start, int limit)
1075    {
1076        if (order != null && direction != null) {
1077          byte dir = direction[start];
1078          while (++start < limit && direction[start] == dir) {}
1079          return start;
1080        }
1081        return limit;
1082    }
1083
1084  /*
1085   * create a new line with characters between charStart and charLimit
1086   * justified using the provided width and ratio.
1087   */

1088    public TextLine JavaDoc getJustifiedLine(float justificationWidth, float justifyRatio, int justStart, int justLimit) {
1089
1090        TextLineComponent[] newComponents = new TextLineComponent[fComponents.length];
1091        System.arraycopy(fComponents, 0, newComponents, 0, fComponents.length);
1092
1093        float leftHang = 0;
1094        float adv = 0;
1095        float justifyDelta = 0;
1096        boolean rejustify = false;
1097        do {
1098            adv = getAdvanceBetween(newComponents, 0, characterCount());
1099
1100            // all characters outside the justification range must be in the base direction
1101
// of the layout, otherwise justification makes no sense.
1102

1103            float justifyAdvance = getAdvanceBetween(newComponents, justStart, justLimit);
1104
1105            // get the actual justification delta
1106
justifyDelta = (justificationWidth - justifyAdvance) * justifyRatio;
1107
1108            // generate an array of GlyphJustificationInfo records to pass to
1109
// the justifier. Array is visually ordered.
1110

1111            // get positions that each component will be using
1112
int[] infoPositions = new int[newComponents.length];
1113            int infoCount = 0;
1114            for (int visIndex = 0; visIndex < newComponents.length; visIndex++) {
1115                int logIndex = fComponentVisualOrder == null ? visIndex : fComponentVisualOrder[visIndex];
1116                infoPositions[logIndex] = infoCount;
1117                infoCount += newComponents[logIndex].getNumJustificationInfos();
1118            }
1119            GlyphJustificationInfo JavaDoc[] infos = new GlyphJustificationInfo JavaDoc[infoCount];
1120
1121            // get justification infos
1122
int compStart = 0;
1123            for (int i = 0; i < newComponents.length; i++) {
1124                TextLineComponent comp = newComponents[i];
1125                int compLength = comp.getNumCharacters();
1126                int compLimit = compStart + compLength;
1127                if (compLimit > justStart) {
1128                    int rangeMin = Math.max(0, justStart - compStart);
1129                    int rangeMax = Math.min(compLength, justLimit - compStart);
1130                    comp.getJustificationInfos(infos, infoPositions[i], rangeMin, rangeMax);
1131
1132                    if (compLimit >= justLimit) {
1133                        break;
1134                    }
1135                }
1136            }
1137
1138            // records are visually ordered, and contiguous, so start and end are
1139
// simply the places where we didn't fetch records
1140
int infoStart = 0;
1141            int infoLimit = infoCount;
1142            while (infoStart < infoLimit && infos[infoStart] == null) {
1143                ++infoStart;
1144            }
1145
1146            while (infoLimit > infoStart && infos[infoLimit - 1] == null) {
1147                --infoLimit;
1148            }
1149
1150            // invoke justifier on the records
1151
TextJustifier JavaDoc justifier = new TextJustifier JavaDoc(infos, infoStart, infoLimit);
1152
1153            float[] deltas = justifier.justify(justifyDelta);
1154
1155            boolean canRejustify = rejustify == false;
1156            boolean wantRejustify = false;
1157            boolean[] flags = new boolean[1];
1158
1159            // apply justification deltas
1160
compStart = 0;
1161            for (int i = 0; i < newComponents.length; i++) {
1162                TextLineComponent comp = newComponents[i];
1163                int compLength = comp.getNumCharacters();
1164                int compLimit = compStart + compLength;
1165                if (compLimit > justStart) {
1166                    int rangeMin = Math.max(0, justStart - compStart);
1167                    int rangeMax = Math.min(compLength, justLimit - compStart);
1168                    newComponents[i] = comp.applyJustificationDeltas(deltas, infoPositions[i] * 2, flags);
1169
1170                    wantRejustify |= flags[0];
1171
1172                    if (compLimit >= justLimit) {
1173                        break;
1174                    }
1175                }
1176            }
1177
1178            rejustify = wantRejustify && !rejustify; // only make two passes
1179
} while (rejustify);
1180
1181        return new TextLine JavaDoc(newComponents, fBaselineOffsets, fChars, fCharsStart,
1182                            fCharsLimit, fCharLogicalOrder, fCharLevels,
1183                            fIsDirectionLTR);
1184    }
1185
1186    // return the sum of the advances of text between the logical start and limit
1187
public static float getAdvanceBetween(TextLineComponent[] components, int start, int limit) {
1188        float advance = 0;
1189
1190        int tlcStart = 0;
1191        for(int i = 0; i < components.length; i++) {
1192            TextLineComponent comp = components[i];
1193
1194            int tlcLength = comp.getNumCharacters();
1195            int tlcLimit = tlcStart + tlcLength;
1196            if (tlcLimit > start) {
1197                int measureStart = Math.max(0, start - tlcStart);
1198                int measureLimit = Math.min(tlcLength, limit - tlcStart);
1199                advance += comp.getAdvanceBetween(measureStart, measureLimit);
1200                if (tlcLimit >= limit) {
1201                    break;
1202                }
1203            }
1204
1205            tlcStart = tlcLimit;
1206        }
1207
1208        return advance;
1209    }
1210}
1211
Popular Tags