KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > batik > gvt > font > AWTGVTGlyphVector


1 /*
2
3    Copyright 2001-2003 The Apache Software Foundation
4
5    Licensed under the Apache License, Version 2.0 (the "License");
6    you may not use this file except in compliance with the License.
7    You may obtain a copy of the License at
8
9        http://www.apache.org/licenses/LICENSE-2.0
10
11    Unless required by applicable law or agreed to in writing, software
12    distributed under the License is distributed on an "AS IS" BASIS,
13    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14    See the License for the specific language governing permissions and
15    limitations under the License.
16
17  */

18 package org.apache.batik.gvt.font;
19
20 import java.awt.Color JavaDoc;
21 import java.awt.Graphics2D JavaDoc;
22 import java.awt.Paint JavaDoc;
23 import java.awt.RenderingHints JavaDoc;
24 import java.awt.Shape JavaDoc;
25 import java.awt.Stroke JavaDoc;
26 import java.awt.font.FontRenderContext JavaDoc;
27 import java.awt.font.GlyphJustificationInfo JavaDoc;
28 import java.awt.font.GlyphMetrics JavaDoc;
29 import java.awt.font.GlyphVector JavaDoc;
30 import java.awt.font.TextAttribute JavaDoc;
31 import java.awt.geom.AffineTransform JavaDoc;
32 import java.awt.geom.GeneralPath JavaDoc;
33 import java.awt.geom.Point2D JavaDoc;
34 import java.awt.geom.Rectangle2D JavaDoc;
35 import java.text.AttributedCharacterIterator JavaDoc;
36 import java.text.CharacterIterator JavaDoc;
37
38 import org.apache.batik.gvt.text.ArabicTextHandler;
39 import org.apache.batik.gvt.text.GVTAttributedCharacterIterator;
40 import org.apache.batik.gvt.text.TextPaintInfo;
41
42 /**
43  * This is a wrapper class for a java.awt.font.GlyphVector instance.
44  *
45  * @author <a HREF="mailto:bella.robinson@cmis.csiro.au">Bella Robinson</a>
46  * @version $Id: AWTGVTGlyphVector.java,v 1.33 2004/11/21 21:20:23 deweese Exp $
47  */

48 public class AWTGVTGlyphVector implements GVTGlyphVector {
49
50     public static final AttributedCharacterIterator.Attribute JavaDoc PAINT_INFO
51         = GVTAttributedCharacterIterator.TextAttribute.PAINT_INFO;
52
53     private GlyphVector JavaDoc awtGlyphVector;
54     private AWTGVTFont gvtFont;
55     private CharacterIterator JavaDoc ci;
56
57     // This contains the glyphPostions after doing a performDefaultLayout
58
private Point2D JavaDoc [] defaultGlyphPositions;
59     private Point2D.Float JavaDoc[] glyphPositions;
60
61     // need to keep track of the glyphTransforms since GlyphVector doesn't
62
// seem to
63
private AffineTransform JavaDoc[] glyphTransforms;
64
65     // these are for caching the glyph outlines
66
private Shape JavaDoc[] glyphOutlines;
67     private Shape JavaDoc[] glyphVisualBounds;
68     private Shape JavaDoc[] glyphLogicalBounds;
69     private boolean[] glyphVisible;
70     private GVTGlyphMetrics [] glyphMetrics;
71     private GeneralPath JavaDoc outline;
72     private Rectangle2D JavaDoc visualBounds;
73     private Rectangle2D JavaDoc logicalBounds;
74     private Rectangle2D JavaDoc bounds2D;
75     private float scaleFactor;
76     private float ascent;
77     private float descent;
78     private TextPaintInfo cacheTPI;
79
80     /**
81      * Creates and new AWTGVTGlyphVector from the specified GlyphVector and
82      * AWTGVTFont objects.
83      *
84      * @param glyphVector The glyph vector that this one will be based upon.
85      * @param font The font that is creating this glyph vector.
86      * @param scaleFactor The scale factor to apply to the glyph vector.
87      * IMPORTANT: This is only required because the GlyphVector class doesn't
88      * handle font sizes less than 1 correctly. By using the scale factor we
89      * can use a GlyphVector created by a larger font and then scale it down to
90      * the correct size.
91      * @param ci The character string that this glyph vector represents.
92      */

93     public AWTGVTGlyphVector(GlyphVector JavaDoc glyphVector,
94                              AWTGVTFont font,
95                              float scaleFactor,
96                              CharacterIterator JavaDoc ci) {
97
98         this.awtGlyphVector = glyphVector;
99         this.gvtFont = font;
100         this.scaleFactor = scaleFactor;
101         this.ci = ci;
102
103         GVTLineMetrics lineMetrics = gvtFont.getLineMetrics
104             ("By", awtGlyphVector.getFontRenderContext());
105
106         ascent = lineMetrics.getAscent();
107         descent = lineMetrics.getDescent();
108
109         outline = null;
110         visualBounds = null;
111         logicalBounds = null;
112         bounds2D = null;
113         int numGlyphs = glyphVector.getNumGlyphs();
114         glyphPositions = new Point2D.Float JavaDoc [numGlyphs+1];
115         glyphTransforms = new AffineTransform JavaDoc[numGlyphs];
116         glyphOutlines = new Shape JavaDoc [numGlyphs];
117         glyphVisualBounds = new Shape JavaDoc [numGlyphs];
118         glyphLogicalBounds = new Shape JavaDoc [numGlyphs];
119         glyphVisible = new boolean [numGlyphs];
120         glyphMetrics = new GVTGlyphMetrics[numGlyphs];
121
122         for (int i = 0; i < numGlyphs; i++) {
123             glyphVisible[i] = true;
124         }
125     }
126
127     /**
128      * Returns the GVTFont associated with this GVTGlyphVector.
129      */

130     public GVTFont getFont() {
131         return gvtFont;
132     }
133
134     /**
135      * Returns the FontRenderContext associated with this GlyphVector.
136      */

137     public FontRenderContext JavaDoc getFontRenderContext() {
138         return awtGlyphVector.getFontRenderContext();
139     }
140
141     /**
142      * Returns the glyphcode of the specified glyph.
143      */

144     public int getGlyphCode(int glyphIndex) {
145         return awtGlyphVector.getGlyphCode(glyphIndex);
146     }
147
148     /**
149      * Returns an array of glyphcodes for the specified glyphs.
150      */

151     public int[] getGlyphCodes(int beginGlyphIndex, int numEntries,
152                                int[] codeReturn) {
153         return awtGlyphVector.getGlyphCodes(beginGlyphIndex, numEntries,
154                                             codeReturn);
155     }
156
157     /**
158      * Returns the justification information for the glyph at the specified
159      * index into this GlyphVector.
160      */

161     public GlyphJustificationInfo JavaDoc getGlyphJustificationInfo(int glyphIndex) {
162         return awtGlyphVector.getGlyphJustificationInfo(glyphIndex);
163     }
164
165     /**
166      * Returns a tight bounds on the GylphVector including stroking.
167      */

168     public Rectangle2D JavaDoc getBounds2D(AttributedCharacterIterator JavaDoc aci) {
169         aci.first();
170         TextPaintInfo tpi = (TextPaintInfo)aci.getAttribute(PAINT_INFO);
171         if ((bounds2D != null) &&
172             TextPaintInfo.equivilent(tpi, cacheTPI))
173             return bounds2D;
174
175         if (tpi == null)
176             return null;
177         if (!tpi.visible)
178             return null;
179             
180         cacheTPI = new TextPaintInfo(tpi);
181         Shape JavaDoc outline = null;
182         if (tpi.fillPaint != null) {
183             outline = getOutline();
184             bounds2D = outline.getBounds2D();
185         }
186         
187         // check if we need to include the
188
// outline of this glyph
189
Stroke JavaDoc stroke = tpi.strokeStroke;
190         Paint JavaDoc paint = tpi.strokePaint;
191         if ((stroke != null) && (paint != null)) {
192             if (outline == null)
193                 outline = getOutline();
194             Rectangle2D JavaDoc strokeBounds
195                 = stroke.createStrokedShape(outline).getBounds2D();
196             if (bounds2D == null)
197                 bounds2D = strokeBounds;
198             else
199                 bounds2D = bounds2D.createUnion(strokeBounds);
200         }
201         if (bounds2D == null)
202             return null;
203
204         if ((bounds2D.getWidth() == 0) ||
205             (bounds2D.getHeight() == 0))
206             bounds2D = null;
207
208         return bounds2D;
209     }
210
211     /**
212      * Returns the logical bounds of this GlyphVector.
213      * This is a bound useful for hit detection and highlighting.
214      */

215     public Rectangle2D JavaDoc getLogicalBounds() {
216         if (logicalBounds == null) {
217             // This fills in logicalBounds...
218
computeGlyphLogicalBounds();
219         }
220         return logicalBounds;
221     }
222
223     /**
224      * Returns the logical bounds of the specified glyph within this
225      * GlyphVector.
226      */

227     public Shape JavaDoc getGlyphLogicalBounds(int glyphIndex) {
228         if (glyphLogicalBounds[glyphIndex] == null &&
229             glyphVisible[glyphIndex]) {
230
231             computeGlyphLogicalBounds();
232         }
233         return glyphLogicalBounds[glyphIndex];
234     }
235
236     /**
237      * Calculates the logical bounds for each glyph. The logical
238      * bounds are what is used for highlighting the glyphs when
239      * selected.
240      */

241     private void computeGlyphLogicalBounds() {
242
243         Shape JavaDoc[] tempLogicalBounds = new Shape JavaDoc[getNumGlyphs()];
244         boolean[] rotated = new boolean[getNumGlyphs()];
245
246         double maxWidth = -1;
247         double maxHeight = -1;
248         for (int i = 0; i < getNumGlyphs(); i++) {
249
250             if (!glyphVisible[i]) {
251                 // the glyph is not drawn
252
tempLogicalBounds[i] = null;
253                 continue;
254             }
255
256             AffineTransform JavaDoc glyphTransform = getGlyphTransform(i);
257             GVTGlyphMetrics glyphMetrics = getGlyphMetrics(i);
258
259             float glyphX = 0;
260             float glyphY = -ascent/scaleFactor;
261             float glyphWidth = (glyphMetrics.getHorizontalAdvance()/
262                                  scaleFactor);
263             float glyphHeight = (glyphMetrics.getVerticalAdvance()/
264                                  scaleFactor);
265             Rectangle2D JavaDoc glyphBounds = new Rectangle2D.Double JavaDoc(glyphX,
266                                                              glyphY,
267                                                              glyphWidth,
268                                                              glyphHeight);
269
270             if (glyphBounds.isEmpty()) {
271                 if (i > 0) {
272                     // can't tell if rotated or not, make it the same as
273
// the previous glyph
274
rotated [i] = rotated [i-1];
275                 } else {
276                     rotated [i] = true;
277                 }
278             } else {
279                 // get three corner points so we can determine
280
// whether the glyph is rotated
281
Point2D JavaDoc p1 = new Point2D.Double JavaDoc(glyphBounds.getMinX(),
282                                                 glyphBounds.getMinY());
283                 Point2D JavaDoc p2 = new Point2D.Double JavaDoc(glyphBounds.getMaxX(),
284                                                 glyphBounds.getMinY());
285                 Point2D JavaDoc p3 = new Point2D.Double JavaDoc(glyphBounds.getMinX(),
286                                                 glyphBounds.getMaxY());
287
288                 Point2D JavaDoc gpos = getGlyphPosition(i);
289                 AffineTransform JavaDoc tr = AffineTransform.getTranslateInstance
290                     (gpos.getX(), gpos.getY());
291
292                 if (glyphTransform != null)
293                     tr.concatenate(glyphTransform);
294                 tr.scale(scaleFactor, scaleFactor);
295
296                 tempLogicalBounds[i] = tr.createTransformedShape(glyphBounds);
297
298                 Point2D JavaDoc tp1 = new Point2D.Double JavaDoc();
299                 Point2D JavaDoc tp2 = new Point2D.Double JavaDoc();
300                 Point2D JavaDoc tp3 = new Point2D.Double JavaDoc();
301                 tr.transform(p1, tp1);
302                 tr.transform(p2, tp2);
303                 tr.transform(p3, tp3);
304                 double tdx12 = tp1.getX()-tp2.getX();
305                 double tdx13 = tp1.getX()-tp3.getX();
306                 double tdy12 = tp1.getY()-tp2.getY();
307                 double tdy13 = tp1.getY()-tp3.getY();
308
309                 if (((Math.abs(tdx12) < 0.001) && (Math.abs(tdy13) < 0.001)) ||
310                     ((Math.abs(tdx13) < 0.001) && (Math.abs(tdy12) < 0.001))) {
311                     // If either of these are zero then it is axially aligned
312
rotated[i] = false;
313                 } else {
314                     rotated [i] = true;
315                 }
316
317                 Rectangle2D JavaDoc rectBounds;
318                 rectBounds = tempLogicalBounds[i].getBounds2D();
319                 if (rectBounds.getWidth() > maxWidth)
320                     maxWidth = rectBounds.getWidth();
321                 if (rectBounds.getHeight() > maxHeight)
322                     maxHeight = rectBounds.getHeight();
323             }
324         }
325
326         // if appropriate, join adjacent glyph logical bounds
327
GeneralPath JavaDoc logicalBoundsPath = new GeneralPath JavaDoc();
328         for (int i = 0; i < getNumGlyphs(); i++) {
329             if (tempLogicalBounds[i] != null) {
330                 logicalBoundsPath.append(tempLogicalBounds[i], false);
331             }
332         }
333
334         logicalBounds = logicalBoundsPath.getBounds2D();
335
336         if (logicalBounds.getHeight() < maxHeight*1.5) {
337             // make all glyphs tops and bottoms the same as the full bounds
338
for (int i = 0; i < getNumGlyphs(); i++) {
339                 // first make sure that the glyph logical bounds are
340
// not rotated
341
if (rotated[i]) continue;
342                 if (tempLogicalBounds[i] == null) continue;
343
344                 Rectangle2D JavaDoc glyphBounds = tempLogicalBounds[i].getBounds2D();
345
346                 double x = glyphBounds.getMinX();
347                 double width = glyphBounds.getWidth();
348
349                 if ((i < getNumGlyphs()-1) &&
350                     (tempLogicalBounds[i+1] != null)) {
351                     // make this glyph extend to the start of the next one
352
Rectangle2D JavaDoc ngb = tempLogicalBounds[i+1].getBounds2D();
353
354                     if (ngb.getX() > x) {
355                         double nw = ngb.getX() - x;
356                         if ((nw < width*1.15) && (nw > width*.85)) {
357                             double delta = (nw-width)*.5;
358                             width += delta;
359                             ngb.setRect(ngb.getX()-delta, ngb.getY(),
360                                         ngb.getWidth()+delta, ngb.getHeight());
361                         }
362                     }
363                 }
364                 tempLogicalBounds[i] = new Rectangle2D.Double JavaDoc
365                     (x, logicalBounds.getMinY(),
366                      width, logicalBounds.getHeight());
367             }
368         } else if (logicalBounds.getWidth() < maxWidth*1.5) {
369             // make all glyphs left and right edges the same as the full bounds
370
for (int i = 0; i < getNumGlyphs(); i++) {
371                 // first make sure that the glyph logical bounds are
372
// not rotated
373
if (rotated[i]) continue;
374                 if (tempLogicalBounds[i] == null) continue;
375
376                 Rectangle2D JavaDoc glyphBounds = tempLogicalBounds[i].getBounds2D();
377                 double y = glyphBounds.getMinY();
378                 double height = glyphBounds.getHeight();
379
380                 if ((i < getNumGlyphs()-1) &&
381                     (tempLogicalBounds[i+1] != null)) {
382                     // make this glyph extend to the start of the next one
383
Rectangle2D JavaDoc ngb = tempLogicalBounds[i+1].getBounds2D();
384                     if (ngb.getY() > y) { // going top to bottom
385
double nh = ngb.getY() - y;
386                         if ((nh < height*1.15) && (nh > height*.85)) {
387                             double delta = (nh-height)*.5;
388                             height += delta;
389                             ngb.setRect(ngb.getX(), ngb.getY()-delta,
390                                         ngb.getWidth(), ngb.getHeight()+delta);
391                         }
392                     }
393                 }
394                 tempLogicalBounds[i] = new Rectangle2D.Double JavaDoc
395                     (logicalBounds.getMinX(), y,
396                      logicalBounds.getWidth(), height);
397             }
398         }
399
400         for (int i = 0; i < getNumGlyphs(); i++) {
401             glyphLogicalBounds[i] = tempLogicalBounds[i];
402         }
403     }
404
405     /**
406      * Returns the metrics of the glyph at the specified index into this
407      * GVTGlyphVector.
408      */

409     public GVTGlyphMetrics getGlyphMetrics(int glyphIndex) {
410         if (glyphMetrics[glyphIndex] != null)
411             return glyphMetrics[glyphIndex];
412
413         // -- start glyph cache code --
414
Point2D JavaDoc glyphPos = defaultGlyphPositions[glyphIndex];
415         char c = ci.setIndex(ci.getBeginIndex()+glyphIndex);
416         ci.setIndex(ci.getBeginIndex());
417         AWTGlyphGeometryCache.Value v = AWTGVTFont.getGlyphGeometry
418             (gvtFont, c, awtGlyphVector, glyphIndex, glyphPos);
419         Rectangle2D JavaDoc gmB = v.getBounds2D();
420         // -- end glyph cache code --
421

422         Rectangle2D JavaDoc bounds = new Rectangle2D.Double JavaDoc
423             (gmB.getX() * scaleFactor, gmB.getY() * scaleFactor,
424              gmB.getWidth() * scaleFactor, gmB.getHeight() * scaleFactor);
425
426         // defaultGlyphPositions has one more entry than glyphs
427
// the last entry stores the total advance for the
428
// glyphVector.
429
float adv = (float)(defaultGlyphPositions[glyphIndex+1].getX()-
430                             defaultGlyphPositions[glyphIndex] .getX());
431         glyphMetrics[glyphIndex] = new GVTGlyphMetrics
432             (adv*scaleFactor, (ascent+descent),
433              bounds, GlyphMetrics.STANDARD);
434
435         return glyphMetrics[glyphIndex];
436     }
437
438     /**
439      * Returns a Shape whose interior corresponds to the visual representation
440      * of the specified glyph within this GlyphVector.
441      */

442     public Shape JavaDoc getGlyphOutline(int glyphIndex) {
443         if (glyphOutlines[glyphIndex] == null) {
444 /*
445             Shape glyphOutline = awtGlyphVector.getGlyphOutline(glyphIndex);
446 */

447             // -- start glyph cache code --
448
Point2D JavaDoc glyphPos = defaultGlyphPositions[glyphIndex];
449             char c = ci.setIndex(ci.getBeginIndex()+glyphIndex);
450             ci.setIndex(ci.getBeginIndex());
451             AWTGlyphGeometryCache.Value v = AWTGVTFont.getGlyphGeometry
452                 (gvtFont, c, awtGlyphVector, glyphIndex, glyphPos);
453             Shape JavaDoc glyphOutline = v.getOutline();
454            // -- end glyph cache code --
455

456             AffineTransform JavaDoc tr = AffineTransform.getTranslateInstance
457                 (getGlyphPosition(glyphIndex).getX(),
458                  getGlyphPosition(glyphIndex).getY());
459
460             AffineTransform JavaDoc glyphTransform = getGlyphTransform(glyphIndex);
461
462             if (glyphTransform != null) {
463                 tr.concatenate(glyphTransform);
464             }
465             //
466
// <!> HACK
467
//
468
// GlyphVector.getGlyphOutline behavior changes between 1.3 and 1.4
469
//
470
// I've looked at this problem a bit more and the incorrect glyph
471
// positioning in Batik is definitely due to the change in
472
// behavior of GlyphVector.getGlyphOutline(glyphIndex). It used to
473
// return the outline of the glyph at position 0,0 which meant
474
// that we had to translate it to the actual glyph position before
475
// drawing it. Now, it returns the outline which has already been
476
// positioned.
477
//
478
// -- Bella
479
//
480
/*
481             if (outlinesPositioned()) {
482                 Point2D glyphPos = defaultGlyphPositions[glyphIndex];
483                 tr.translate(-glyphPos.getX(), -glyphPos.getY());
484             }
485 */

486             tr.scale(scaleFactor, scaleFactor);
487             glyphOutlines[glyphIndex]=tr.createTransformedShape(glyphOutline);
488         }
489
490         return glyphOutlines[glyphIndex];
491     }
492
493     // This is true if GlyphVector.getGlyphOutline returns glyph outlines
494
// that are positioned (if it is false the outlines are always at 0,0).
495
private static final boolean outlinesPositioned;
496     // This is true if Graphics2D.drawGlyphVector works for the
497
// current JDK/OS combination.
498
private static final boolean drawGlyphVectorWorks;
499     // This is true if Graphics2D.drawGlyphVector will correctly
500
// render Glyph Vectors with per glyph transforms.
501
private static final boolean glyphVectorTransformWorks;
502
503     static {
504         String JavaDoc s = System.getProperty("java.specification.version");
505         if ("1.4".compareTo(s) <= 0) {
506             outlinesPositioned = true;
507             drawGlyphVectorWorks = true;
508             glyphVectorTransformWorks = true;
509         } else if ("Mac OS X".equals(System.getProperty("os.name"))) {
510             outlinesPositioned = true;
511             drawGlyphVectorWorks = false;
512             glyphVectorTransformWorks = false;
513         } else {
514             outlinesPositioned = false;
515             drawGlyphVectorWorks = true;
516             glyphVectorTransformWorks = false;
517         }
518     }
519
520     // Returns true if GlyphVector.getGlyphOutlines returns glyph outlines
521
// that are positioned (otherwise they are always at 0,0).
522
static boolean outlinesPositioned() {
523         return outlinesPositioned;
524     }
525
526     /**
527      * Returns the position of the specified glyph within this GlyphVector.
528      */

529     public Point2D JavaDoc getGlyphPosition(int glyphIndex) {
530         return glyphPositions[glyphIndex];
531     }
532
533     /**
534      * Returns an array of glyph positions for the specified glyphs
535      */

536     public float[] getGlyphPositions(int beginGlyphIndex,
537                                      int numEntries,
538                                      float[] positionReturn) {
539
540         if (positionReturn == null) {
541             positionReturn = new float[numEntries*2];
542         }
543
544         for (int i = beginGlyphIndex; i < (beginGlyphIndex+numEntries); i++) {
545             Point2D JavaDoc glyphPos = getGlyphPosition(i);
546             positionReturn[(i-beginGlyphIndex)*2] = (float)glyphPos.getX();
547             positionReturn[(i-beginGlyphIndex)*2 + 1] = (float)glyphPos.getY();
548         }
549
550         return positionReturn;
551     }
552
553     /**
554      * Gets the transform of the specified glyph within this GlyphVector.
555      */

556     public AffineTransform JavaDoc getGlyphTransform(int glyphIndex) {
557         return glyphTransforms[glyphIndex];
558     }
559
560     /**
561      * Returns the visual bounds of the specified glyph within the GlyphVector.
562      */

563     public Shape JavaDoc getGlyphVisualBounds(int glyphIndex) {
564         if (glyphVisualBounds[glyphIndex] == null) {
565 /*
566             Shape glyphOutline = awtGlyphVector.getGlyphOutline(glyphIndex);
567             Rectangle2D glyphBounds = glyphOutline.getBounds2D();
568 */

569             // -- start glyph cache code --
570
Point2D JavaDoc glyphPos = defaultGlyphPositions[glyphIndex];
571             char c = ci.setIndex(ci.getBeginIndex()+glyphIndex);
572             ci.setIndex(ci.getBeginIndex());
573             AWTGlyphGeometryCache.Value v = AWTGVTFont.getGlyphGeometry
574                 (gvtFont, c, awtGlyphVector, glyphIndex, glyphPos);
575             Rectangle2D JavaDoc glyphBounds = v.getOutlineBounds2D();
576            // -- end glyph cache code --
577

578             AffineTransform JavaDoc tr = AffineTransform.getTranslateInstance
579                 (getGlyphPosition(glyphIndex).getX(),
580                  getGlyphPosition(glyphIndex).getY());
581
582             AffineTransform JavaDoc glyphTransform = getGlyphTransform(glyphIndex);
583             if (glyphTransform != null) {
584                 tr.concatenate(glyphTransform);
585             }
586             tr.scale(scaleFactor, scaleFactor);
587             glyphVisualBounds[glyphIndex] =
588                 tr.createTransformedShape(glyphBounds);
589         }
590
591         return glyphVisualBounds[glyphIndex];
592     }
593
594     /**
595      * Returns the number of glyphs in this GlyphVector.
596      */

597     public int getNumGlyphs() {
598         return awtGlyphVector.getNumGlyphs();
599     }
600
601     /**
602      * Returns a Shape whose interior corresponds to the visual representation
603      * of this GlyphVector.
604      */

605     public Shape JavaDoc getOutline() {
606         if (outline != null)
607             return outline;
608         
609         outline = new GeneralPath JavaDoc();
610         for (int i = 0; i < getNumGlyphs(); i++) {
611             if (glyphVisible[i]) {
612                 Shape JavaDoc glyphOutline = getGlyphOutline(i);
613                 outline.append(glyphOutline, false);
614             }
615         }
616         return outline;
617     }
618
619     /**
620      * Returns a Shape whose interior corresponds to the visual representation
621      * of this GlyphVector, offset to x, y.
622      */

623     public Shape JavaDoc getOutline(float x, float y) {
624         Shape JavaDoc outline = getOutline();
625         AffineTransform JavaDoc tr = AffineTransform.getTranslateInstance(x,y);
626         outline = tr.createTransformedShape(outline);
627         return outline;
628     }
629
630     /**
631      * Returns the visual bounds of this GlyphVector The visual bounds is the
632      * tightest rectangle enclosing all non-background pixels in the rendered
633      * representation of this GlyphVector.
634      */

635     public Rectangle2D JavaDoc getGeometricBounds() {
636         if (visualBounds == null) {
637             Shape JavaDoc outline = getOutline();
638             visualBounds = outline.getBounds2D();
639         }
640         return visualBounds;
641     }
642
643     /**
644      * Assigns default positions to each glyph in this GlyphVector.
645      */

646     public void performDefaultLayout() {
647         if (defaultGlyphPositions == null) {
648             awtGlyphVector.performDefaultLayout();
649             defaultGlyphPositions = new Point2D.Float JavaDoc[getNumGlyphs()+1];
650             for (int i = 0; i <= getNumGlyphs(); i++)
651                 defaultGlyphPositions[i] = awtGlyphVector.getGlyphPosition(i);
652         }
653
654         outline = null;
655         visualBounds = null;
656         logicalBounds = null;
657         bounds2D = null;
658         float shiftLeft = 0;
659         int i=0;
660         for (; i < getNumGlyphs(); i++) {
661             glyphTransforms [i] = null;
662             glyphVisualBounds [i] = null;
663             glyphLogicalBounds[i] = null;
664             glyphOutlines [i] = null;
665             glyphMetrics [i] = null;
666             Point2D JavaDoc glyphPos = defaultGlyphPositions[i];
667             float x = (float)((glyphPos.getX() * scaleFactor)-shiftLeft);
668             float y = (float) (glyphPos.getY() * scaleFactor);
669
670             // if c is a transparent arabic char then need to shift the
671
// following glyphs left so that the current glyph is overwritten
672
char c = ci.setIndex(i + ci.getBeginIndex());
673             /*
674             if (ArabicTextHandler.arabicCharTransparent(c)) {
675                 int j;
676                 shiftLeft += getGlyphMetrics(i).getHorizontalAdvance();
677                 for (j=i+1; j<getNumGlyphs(); j++) {
678                     char c2 = ci.setIndex(j+ci.getBeginIndex());
679                     if (!ArabicTextHandler.arabicCharTransparent(c2)) break;
680                     shiftLeft += getGlyphMetrics(j).getHorizontalAdvance();
681                 }
682                 if (j != getNumGlyphs()) {
683                     Point2D glyphPosBase = defaultGlyphPositions[j];
684                     double rEdge = glyphPosBase.getX()+getGlyphMetrics(j).getHorizontalAdvance();
685                     rEdge -= shiftLeft;
686                     for (int k=i; k<j; k++) {
687                         glyphTransforms [k] = null;
688                         glyphVisualBounds [k] = null;
689                         glyphLogicalBounds[k] = null;
690                         glyphOutlines [k] = null;
691                         glyphMetrics [k] = null;
692                         x = (float)rEdge-getGlyphMetrics(k).getHorizontalAdvance();
693                         y = (float) (defaultGlyphPositions[k].getY() * scaleFactor);
694                         if (glyphPositions[k] == null) {
695                             glyphPositions[k] = new Point2D.Float(x,y);
696                         } else {
697                             glyphPositions[k].x = x;
698                             glyphPositions[k].y = y;
699                         }
700                     }
701                     i = j-1;
702                 }
703             } else {
704             */

705                 if (glyphPositions[i] == null) {
706                     glyphPositions[i] = new Point2D.Float JavaDoc(x,y);
707                 } else {
708                     glyphPositions[i].x = x;
709                     glyphPositions[i].y = y;
710                 }
711                 // }
712

713         }
714
715         // Need glyph pos for point after last char...
716
Point2D JavaDoc glyphPos = defaultGlyphPositions[i];
717         glyphPositions[i] = new Point2D.Float JavaDoc
718                 ((float)((glyphPos.getX() * scaleFactor)-shiftLeft),
719                  (float) (glyphPos.getY() * scaleFactor));
720     }
721
722     /**
723      * Sets the position of the specified glyph within this GlyphVector.
724      */

725     public void setGlyphPosition(int glyphIndex, Point2D JavaDoc newPos) {
726         glyphPositions[glyphIndex].x = (float)newPos.getX();
727         glyphPositions[glyphIndex].y = (float)newPos.getY();
728         outline = null;
729         visualBounds = null;
730         logicalBounds = null;
731         bounds2D = null;
732
733         if (glyphIndex != getNumGlyphs()) {
734             glyphVisualBounds [glyphIndex] = null;
735             glyphLogicalBounds[glyphIndex] = null;
736             glyphOutlines [glyphIndex] = null;
737             glyphMetrics [glyphIndex] = null;
738         }
739     }
740
741     /**
742      * Sets the transform of the specified glyph within this GlyphVector.
743      */

744     public void setGlyphTransform(int glyphIndex, AffineTransform JavaDoc newTX) {
745         glyphTransforms[glyphIndex] = newTX;
746         outline = null;
747         visualBounds = null;
748         logicalBounds = null;
749         bounds2D = null;
750
751         glyphVisualBounds [glyphIndex] = null;
752         glyphLogicalBounds[glyphIndex] = null;
753         glyphOutlines [glyphIndex] = null;
754         glyphMetrics [glyphIndex] = null;
755     }
756
757     /**
758      * Tells the glyph vector whether or not to draw the specified glyph.
759      */

760     public void setGlyphVisible(int glyphIndex, boolean visible) {
761         if (visible == glyphVisible[glyphIndex])
762             return;
763         glyphVisible[glyphIndex] = visible;
764         outline = null;
765         visualBounds = null;
766         logicalBounds = null;
767         bounds2D = null;
768
769         glyphVisualBounds [glyphIndex] = null;
770         glyphLogicalBounds[glyphIndex] = null;
771         glyphOutlines [glyphIndex] = null;
772         glyphMetrics [glyphIndex] = null;
773     }
774
775     /**
776      * Returns true if specified glyph will be rendered.
777      */

778     public boolean isGlyphVisible(int glyphIndex) {
779         return glyphVisible[glyphIndex];
780     }
781
782     /**
783      * Returns the number of chars represented by the glyphs within the
784      * specified range.
785      *
786      * @param startGlyphIndex The index of the first glyph in the range.
787      * @param endGlyphIndex The index of the last glyph in the range.
788      * @return The number of chars.
789      */

790     public int getCharacterCount(int startGlyphIndex, int endGlyphIndex) {
791         if (startGlyphIndex < 0) {
792             startGlyphIndex = 0;
793         }
794         if (endGlyphIndex >= getNumGlyphs()) {
795             endGlyphIndex = getNumGlyphs()-1;
796         }
797         int charCount = 0;
798         int start = startGlyphIndex+ci.getBeginIndex();
799         int end = endGlyphIndex+ci.getBeginIndex();
800
801         for (char c = ci.setIndex(start); ci.getIndex() <= end; c=ci.next()) {
802             if (ArabicTextHandler.isLigature(c)) {
803                 charCount += ArabicTextHandler.getNumChars(c);
804             } else {
805                 charCount++;
806             }
807         }
808
809         return charCount;
810     }
811
812     /**
813      * Draws this glyph vector.
814      */

815     public void draw(Graphics2D JavaDoc graphics2D,
816                      AttributedCharacterIterator JavaDoc aci) {
817         int numGlyphs = getNumGlyphs();
818
819         aci.first();
820         TextPaintInfo tpi = (TextPaintInfo)aci.getAttribute
821             (GVTAttributedCharacterIterator.TextAttribute.PAINT_INFO);
822         if (tpi == null) return;
823         if (!tpi.visible) return;
824
825         Paint JavaDoc fillPaint = tpi.fillPaint;
826         Stroke JavaDoc stroke = tpi.strokeStroke;
827         Paint JavaDoc strokePaint = tpi.strokePaint;
828
829         if ((fillPaint == null) && ((strokePaint == null) ||
830                                     (stroke == null)))
831             return;
832
833         boolean useHinting = drawGlyphVectorWorks;
834         if (useHinting && (stroke != null) && (strokePaint != null))
835             // Can't stroke with drawGlyphVector.
836
useHinting = false;
837
838         if (useHinting &&
839             (fillPaint != null) && !(fillPaint instanceof Color JavaDoc))
840             // The coordinate system is different for drawGlyphVector.
841
// So complex paints aren't positioned properly.
842
useHinting = false;
843
844         if (useHinting) {
845             Object JavaDoc v1 = graphics2D.getRenderingHint
846                 (RenderingHints.KEY_TEXT_ANTIALIASING);
847             Object JavaDoc v2 = graphics2D.getRenderingHint
848                 (RenderingHints.KEY_STROKE_CONTROL);
849             // text-rendering = geometricPrecision so fill shapes.
850
if ((v1 == RenderingHints.VALUE_TEXT_ANTIALIAS_ON) &&
851                 (v2 == RenderingHints.VALUE_STROKE_PURE))
852                 useHinting = false;
853         }
854
855         final int typeGRot = AffineTransform.TYPE_GENERAL_ROTATION;
856         final int typeGTrans = AffineTransform.TYPE_GENERAL_TRANSFORM;
857
858         if (useHinting) {
859             // Check if usr->dev transform has general rotation,
860
// or shear..
861
AffineTransform JavaDoc at = graphics2D.getTransform();
862             int type = at.getType();
863             if (((type & typeGTrans) != 0) || ((type & typeGRot) != 0))
864                 useHinting = false;
865         }
866             
867         if (useHinting) {
868             for (int i=0; i<numGlyphs; i++) {
869                 if (!glyphVisible[i]) {
870                     useHinting = false;
871                     break;
872                 }
873                 AffineTransform JavaDoc at = glyphTransforms[i];
874                 if (at != null) {
875                     int type = at.getType();
876                     if ((type & ~AffineTransform.TYPE_TRANSLATION) == 0) {
877                         // Just translation
878
} else if (glyphVectorTransformWorks &&
879                                ((type & typeGTrans) == 0) &&
880                                ((type & typeGRot) == 0)) {
881                         // It's a simple 90Deg rotate, and we can put
882
// it into the GlyphVector.
883
} else {
884                         // we can't (or it doesn't make sense
885
// to use the GlyphVector.
886
useHinting = false;
887                         break;
888                     }
889                 }
890             }
891         }
892
893         if (useHinting) {
894             double sf = scaleFactor;
895             double [] mat = new double[6];
896             for (int i=0; i< numGlyphs; i++) {
897                 Point2D JavaDoc pos = glyphPositions[i];
898                 double x = pos.getX();
899                 double y = pos.getY();
900                 AffineTransform JavaDoc at = glyphTransforms[i];
901                 if (at != null) {
902                     // Scale the translate portion of matrix,
903
// and add it into the position.
904
at.getMatrix(mat);
905                     x += mat[4];
906                     y += mat[5];
907                     if ((mat[0] != 1) || (mat[1] != 0) ||
908                         (mat[2] != 0) || (mat[3] != 1)) {
909                         // More than just translation.
910
mat[4] = 0; mat[5] = 0;
911                         at = new AffineTransform JavaDoc(mat);
912                     } else {
913                         at = null;
914                     }
915                 }
916                 pos = new Point2D.Double JavaDoc(x/sf, y/sf);
917                 awtGlyphVector.setGlyphPosition(i, pos);
918                 awtGlyphVector.setGlyphTransform(i, at);
919             }
920             graphics2D.scale(sf, sf);
921             graphics2D.setPaint(fillPaint);
922             graphics2D.drawGlyphVector(awtGlyphVector, 0, 0);
923             graphics2D.scale(1/sf, 1/sf);
924
925             for (int i=0; i< numGlyphs; i++) {
926                 Point2D JavaDoc pos = defaultGlyphPositions[i];
927                 awtGlyphVector.setGlyphPosition(i, pos);
928                 awtGlyphVector.setGlyphTransform(i, null);
929             }
930
931         } else {
932             Shape JavaDoc outline = getOutline();
933
934             // check if we need to fill this glyph
935
if (fillPaint != null) {
936                 graphics2D.setPaint(fillPaint);
937                 graphics2D.fill(outline);
938             }
939
940             // check if we need to draw the outline of this glyph
941
if (stroke != null && strokePaint != null) {
942                 graphics2D.setStroke(stroke);
943                 graphics2D.setPaint(strokePaint);
944                 graphics2D.draw(outline);
945             }
946         }
947     }
948 }
949
Popular Tags