1/*
2 *  Licensed to the Apache Software Foundation (ASF) under one or more
3 *  contributor license agreements.  See the NOTICE file distributed with
4 *  this work for additional information regarding copyright ownership.
5 *  The ASF licenses this file to You under the Apache License, Version 2.0
6 *  (the "License"); you may not use this file except in compliance with
7 *  the License.  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 * @author Ilya S. Okomin
19 * @version $Revision$
20 */
21package org.apache.harmony.awt.gl.font;
22
23import java.awt.Font;
24import java.awt.Rectangle;
25import java.awt.Shape;
26import java.awt.font.FontRenderContext;
27import java.awt.font.GlyphJustificationInfo;
28import java.awt.font.GlyphMetrics;
29import java.awt.font.GlyphVector;
30import java.awt.geom.AffineTransform;
31import java.awt.geom.GeneralPath;
32import java.awt.geom.Point2D;
33import java.awt.geom.Rectangle2D;
34
35import org.apache.harmony.awt.internal.nls.Messages;
36
37/**
38 * GlyphVector implementation
39 */
40public class CommonGlyphVector extends GlyphVector {
41
42    // array of transforms of glyphs in GlyphVector
43    protected AffineTransform[] glsTransforms;
44
45    // array of chars defined in constructor
46    public char[] charVector;
47
48    // array of Glyph objects, that describe information about glyphs
49    public Glyph[] vector;
50
51    // array of default positions of glyphs in GlyphVector
52    // without applying GlyphVector's transform
53    float[] defaultPositions;
54
55    // array of logical positions of glyphs in GlyphVector
56
57    float[] logicalPositions;
58
59    // array of visual (real) positions of glyphs in GlyphVector
60    public float[] visualPositions;
61
62    // FontRenderContext for this vector.
63    protected FontRenderContext vectorFRC;
64
65    // layout flags mask
66    protected int layoutFlags = 0;
67
68    // array of cached glyph outlines
69    protected Shape[] gvShapes;
70
71    FontPeerImpl peer;
72
73    // font corresponding to the GlyphVector
74    Font font;
75
76    // ascent of the font
77    float ascent;
78
79    // height of the font
80    float height;
81
82    // leading of the font
83    float leading;
84
85    // descent of the font
86    float descent;
87
88    // transform of the GlyphVector
89    AffineTransform transform;
90
91    /**
92     * Creates new CommonGlyphVector object from the specified parameters.
93     *
94     * @param chars an array of chars
95     * @param frc FontRenderContext object
96     * @param fnt Font object
97     * @param flags layout flags
98     */
99    @SuppressWarnings("deprecation")
100    public CommonGlyphVector(char[] chars, FontRenderContext frc, Font fnt,
101            int flags) {
102        int len = chars.length;
103
104        this.font = fnt;
105        this.transform = fnt.getTransform();
106        this.peer = (FontPeerImpl) fnt.getPeer();
107
108        gvShapes = new Shape[len];
109
110        // !! As pointed in API documentation for the
111        // getGlyphPosisitions(int index,int numEntries, float[] positionReturn)
112        // and getGlyphPosition(int index) methods, if the index is equals to
113        // the number of glyphs the position after the last glyph must be
114        // returned, thus there are n+1 positions and last (n+1) position
115        // points to the end of GlyphVector.
116
117        logicalPositions = new float[(len+1)<<1];
118        visualPositions = new float[(len+1)<<1];
119        defaultPositions = new float[(len+1)<<1];
120
121        glsTransforms = new AffineTransform[len];
122
123        this.charVector = chars;
124        this.vectorFRC = frc;
125        //LineMetricsImpl lmImpl = (LineMetricsImpl)peer.getLineMetrics();
126
127        LineMetricsImpl lmImpl = (LineMetricsImpl)fnt.getLineMetrics(String.valueOf(chars), frc);
128
129        this.ascent = lmImpl.getAscent();
130        this.height = lmImpl.getHeight();
131        this.leading = lmImpl.getLeading();
132        this.descent = lmImpl.getDescent();
133        this.layoutFlags = flags;
134
135        if ((flags & Font.LAYOUT_RIGHT_TO_LEFT) != 0){
136            char vector[] = new char[len];
137            for(int i=0; i < len; i++){
138                vector[i] = chars[len-i-1];
139            }
140            this.vector = peer.getGlyphs(vector);
141
142        } else {
143            this.vector = peer.getGlyphs(chars);
144        }
145
146        this.glsTransforms = new AffineTransform[len];
147
148        setDefaultPositions();
149        performDefaultLayout();
150    }
151
152    /**
153     * Creates new CommonGlyphVector object from the specified parameters.
154     * Layout flags set to default.
155     *
156     * @param chars an array of chars
157     * @param frc FontRenderContext object
158     * @param fnt Font object
159     */
160    public CommonGlyphVector(char[] chars, FontRenderContext frc, Font fnt) {
161        this(chars, frc, fnt, 0);
162    }
163
164    /**
165     * Creates new CommonGlyphVector object from the specified parameters.
166     * Layout flags set to default.
167     *
168     * @param str specified string
169     * @param frc FontRenderContext object
170     * @param fnt Font object
171     */
172    public CommonGlyphVector(String str, FontRenderContext frc, Font fnt) {
173        this(str.toCharArray(), frc, fnt, 0);
174    }
175
176    /**
177     * Creates new CommonGlyphVector object from the specified parameters.
178     *
179     * @param str specified string
180     * @param frc FontRenderContext object
181     * @param fnt Font object
182     * @param flags layout flags
183     */
184    public CommonGlyphVector(String str, FontRenderContext frc, Font fnt, int flags) {
185        this(str.toCharArray(), frc, fnt, flags);
186    }
187
188    /**
189     * Set array of logical positions of the glyphs to
190     * default with their default advances and height.
191     */
192    void setDefaultPositions(){
193        int len = getNumGlyphs();
194
195        // First [x,y] is set into [0,0] position
196        // for this reason start index is 1
197        for (int i=1; i <= len; i++ ){
198                int idx = i << 1;
199                float advanceX = vector[i-1].getGlyphPointMetrics().getAdvanceX();
200                float advanceY = vector[i-1].getGlyphPointMetrics().getAdvanceY();
201
202                defaultPositions[idx] = defaultPositions[idx-2] + advanceX;
203                defaultPositions[idx+1] = defaultPositions[idx-1] + advanceY;
204
205        }
206        transform.transform(defaultPositions, 0, logicalPositions, 0, getNumGlyphs()+1);
207
208    }
209
210    /**
211     * Returnes the pixel bounds of this GlyphVector rendered at the
212     * specified x,y location with the given FontRenderContext.
213     *
214     * @param frc a FontRenderContext that is used
215     * @param x specified x coordinate value
216     * @param y specified y coordinate value
217     * @return a Rectangle that bounds pixels of this GlyphVector
218     */
219    @Override
220    public Rectangle getPixelBounds(FontRenderContext frc, float x, float y) {
221
222        double xM, yM, xm, ym;
223
224        double minX = 0;
225        double minY = 0;
226        double maxX = 0;
227        double maxY = 0;
228
229        for (int i = 0; i < this.getNumGlyphs(); i++) {
230            Rectangle glyphBounds = this.getGlyphPixelBounds(i, frc, 0, 0);
231            xm = glyphBounds.getMinX();
232            ym = glyphBounds.getMinY();
233            xM = glyphBounds.getMaxX();
234            yM = glyphBounds.getMaxY();
235
236            if (i == 0) {
237                minX = xm;
238                minY = ym;
239                maxX = xM;
240                maxY = yM;
241            }
242
243            if (minX > xm) {
244                minX = xm;
245            }
246            if (minY > ym) {
247                minY = ym;
248            }
249            if (maxX < xM) {
250                maxX = xM;
251            }
252            if (maxY < yM) {
253                maxY = yM;
254            }
255        }
256        return new Rectangle((int)(minX + x), (int)(minY + y), (int)(maxX - minX), (int)(maxY - minY));
257
258    }
259
260    /**
261     * Returns the visual bounds of this GlyphVector.
262     * The visual bounds is the bounds of the total outline of
263     * this GlyphVector.
264     * @return a Rectangle2D that id the visual bounds of this GlyphVector
265     */
266    @Override
267    public Rectangle2D getVisualBounds() {
268        float xM, yM, xm, ym;
269        float minX = 0;
270        float minY = 0;
271        float maxX = 0;
272        float maxY = 0;
273        boolean firstIteration = true;
274
275        for (int i = 0; i < this.getNumGlyphs(); i++) {
276            Rectangle2D bounds = this.getGlyphVisualBounds(i).getBounds2D();
277            if (bounds.getWidth() == 0){
278                continue;
279            }
280            xm = (float)bounds.getX();
281            ym = (float)bounds.getY();
282
283            xM = (float)(xm + bounds.getWidth());
284
285            yM = ym + (float) bounds.getHeight();
286
287            if (firstIteration) {
288                minX = xm;
289                minY = ym;
290                maxX = xM;
291                maxY = yM;
292                firstIteration = false;
293            } else {
294                if (minX > xm) {
295                    minX = xm;
296                }
297                if (minY > ym) {
298                    minY = ym;
299                }
300                if (maxX < xM) {
301                    maxX = xM;
302                }
303                if (maxY < yM) {
304                    maxY = yM;
305                }
306
307            }
308        }
309
310        return (this.getNumGlyphs() != 0) ? new Rectangle2D.Float(minX, minY,
311                (maxX - minX), (maxY - minY)) : null;
312    }
313
314    /**
315     * Sets new position to the specified glyph.
316     */
317    @Override
318    public void setGlyphPosition(int glyphIndex, Point2D newPos) {
319        if ((glyphIndex > vector.length) || (glyphIndex < 0)) {
320            // awt.43=glyphIndex is out of vector's limits
321            throw new IndexOutOfBoundsException(Messages.getString("awt.43")); //$NON-NLS-1$
322        }
323        float x = (float)newPos.getX();
324        float y = (float)newPos.getY();
325        int index = glyphIndex << 1;
326
327        if ((x != visualPositions[index]) || (y != visualPositions[index + 1])){
328            visualPositions[index] = x;
329            visualPositions[index+1] = y;
330            layoutFlags = layoutFlags | FLAG_HAS_POSITION_ADJUSTMENTS;
331        }
332
333    }
334
335    /**
336     * Returns the position of the specified glyph relative to the origin of
337     * this GlyphVector
338     * @return a Point2D that the origin of the glyph with specified index
339     */
340    @Override
341    public Point2D getGlyphPosition(int glyphIndex) {
342        if ((glyphIndex > vector.length) || (glyphIndex < 0)) {
343            // awt.43=glyphIndex is out of vector's limits
344            throw new IndexOutOfBoundsException(Messages.getString("awt.43")); //$NON-NLS-1$
345        }
346        int index = glyphIndex << 1;
347        Point2D pos = new Point2D.Float(visualPositions[index], visualPositions[index+1]);
348
349        // For last position we don't have to transform !!
350        if(glyphIndex==vector.length){
351            return pos;
352        }
353
354        AffineTransform at = getGlyphTransform(glyphIndex);
355        if ((at == null) || (at.isIdentity())){
356            return pos;
357        }
358
359        pos.setLocation(pos.getX() + at.getTranslateX(), pos.getY() + at.getTranslateY());
360
361        return pos;
362    }
363
364    /**
365     * Sets new transform to the specified glyph.
366     *
367     * @param glyphIndex specified index of the glyph
368     * @param trans AffineTransform of the glyph with specified index
369     */
370    @Override
371    public void setGlyphTransform(int glyphIndex, AffineTransform trans) {
372        if ((glyphIndex >= vector.length) || (glyphIndex < 0)) {
373            // awt.43=glyphIndex is out of vector's limits
374            throw new IndexOutOfBoundsException(Messages.getString("awt.43")); //$NON-NLS-1$
375        }
376
377        if ((trans == null) || (trans.isIdentity())) {
378            glsTransforms[glyphIndex] = null;
379        } else {
380            glsTransforms[glyphIndex] = new AffineTransform(trans);
381            layoutFlags = layoutFlags | FLAG_HAS_TRANSFORMS;
382        }
383    }
384
385    /**
386     * Returns the affine transform of the specified glyph.
387     *
388     * @param glyphIndex specified index of the glyph
389     * @return an AffineTransform of the glyph with specified index
390     */
391    @Override
392    public AffineTransform getGlyphTransform(int glyphIndex) {
393        if ((glyphIndex >= this.vector.length) || (glyphIndex < 0)) {
394            // awt.43=glyphIndex is out of vector's limits
395            throw new IndexOutOfBoundsException(Messages.getString("awt.43")); //$NON-NLS-1$
396        }
397        return this.glsTransforms[glyphIndex];
398    }
399
400    /**
401     * Returns the metrics of the specified glyph.
402     *
403     * @param glyphIndex specified index of the glyph
404     */
405    @Override
406    public GlyphMetrics getGlyphMetrics(int glyphIndex) {
407
408        if ((glyphIndex < 0) || ((glyphIndex) >= this.getNumGlyphs())) {
409            // awt.43=glyphIndex is out of vector's limits
410            throw new IndexOutOfBoundsException(Messages.getString("awt.43")); //$NON-NLS-1$
411        }
412        // TODO: is there a sence in GlyphMetrics
413        // if certain glyph or Font has a transform??
414        return this.vector[glyphIndex].getGlyphMetrics();
415    }
416
417    /**
418     * Returns a justification information for the glyph with specified glyph
419     * index.
420     * @param glyphIndex index of a glyph which GlyphJustificationInfo is to be
421     * received
422     * @return a GlyphJustificationInfo object that contains glyph justification
423     * properties of the specified glyph
424     */
425    @Override
426    public GlyphJustificationInfo getGlyphJustificationInfo(int glyphIndex) {
427        // TODO : Find out the source of Justification info
428        if (true) {
429            throw new RuntimeException("Method is not implemented"); //$NON-NLS-1$
430        }
431        return null;
432    }
433
434    /**
435     * Returns the FontRenderContext parameter of this GlyphVector.
436     */
437    @Override
438    public FontRenderContext getFontRenderContext() {
439        return this.vectorFRC;
440    }
441
442    /**
443     * Returns the visual bounds of the specified glyph.
444     *
445     * @param glyphIndex specified index of the glyph
446     */
447    @Override
448    public Shape getGlyphVisualBounds(int glyphIndex) {
449        if ((glyphIndex < 0) || (glyphIndex >= this.getNumGlyphs())) {
450            // awt.43=glyphIndex is out of vector's limits
451            throw new IndexOutOfBoundsException(Messages.getString("awt.43")); //$NON-NLS-1$
452        }
453
454        int idx  = glyphIndex << 1;
455
456        AffineTransform fontTransform = this.transform;
457        double xOffs = fontTransform.getTranslateX();
458        double yOffs = fontTransform.getTranslateY();
459
460        if (vector[glyphIndex].getWidth() == 0){
461            return new Rectangle2D.Float((float)xOffs, (float)yOffs, 0, 0);
462        }
463
464        AffineTransform at = AffineTransform.getTranslateInstance(xOffs, yOffs);
465        AffineTransform glyphTransform = getGlyphTransform(glyphIndex);
466
467        if (transform.isIdentity() && ((glyphTransform == null) || glyphTransform.isIdentity())){
468            Rectangle2D blackBox = vector[glyphIndex].getGlyphMetrics().getBounds2D();
469            at.translate(visualPositions[idx], visualPositions[idx+1]);
470            return(at.createTransformedShape(blackBox));
471        }
472
473        GeneralPath shape = (GeneralPath)this.getGlyphOutline(glyphIndex);
474        shape.transform(at);
475        return shape.getBounds2D();
476    }
477
478    /**
479     * Returnes the pixel bounds of the specified glyph within GlyphVector
480     * rendered at the specified x,y location.
481     *
482     * @param glyphIndex index of the glyph
483     * @param frc a FontRenderContext that is used
484     * @param x specified x coordinate value
485     * @param y specified y coordinate value
486     * @return a Rectangle that bounds pixels of the specified glyph
487     */
488    @Override
489    public Rectangle getGlyphPixelBounds(int glyphIndex, FontRenderContext frc,
490            float x, float y) {
491        // TODO : need to be implemented with FontRenderContext
492        if ((glyphIndex < 0) || (glyphIndex >= this.getNumGlyphs())) {
493            // awt.43=glyphIndex is out of vector's limits
494            throw new IndexOutOfBoundsException(Messages.getString("awt.43")); //$NON-NLS-1$
495        }
496
497        int idx  = glyphIndex << 1;
498
499        if (vector[glyphIndex].getWidth() == 0){
500            AffineTransform fontTransform = this.transform;
501            double xOffs = x + visualPositions[idx] + fontTransform.getTranslateX();
502            double yOffs = y + visualPositions[idx+1] + fontTransform.getTranslateY();
503            return new Rectangle((int)xOffs, (int)yOffs, 0, 0);
504        }
505
506        GeneralPath shape = (GeneralPath)this.getGlyphOutline(glyphIndex);
507
508        AffineTransform at = AffineTransform.getTranslateInstance(x, y);
509
510        if (frc != null){
511            at.concatenate(frc.getTransform());
512        }
513
514        shape.transform(at);
515
516        Rectangle bounds = shape.getBounds();
517        return new Rectangle((int)bounds.getX(), (int)bounds.getY(),
518                            (int)bounds.getWidth()-1, (int)bounds.getHeight()-1);
519        }
520
521    /**
522     * Returns a Shape that encloses specified glyph.
523     *
524     * @param glyphIndex specified index of the glyph
525     */
526    @Override
527    public Shape getGlyphOutline(int glyphIndex) {
528        if ((glyphIndex < 0) || (glyphIndex >= this.getNumGlyphs())) {
529            // awt.43=glyphIndex is out of vector's limits
530            throw new IndexOutOfBoundsException(Messages.getString("awt.43")); //$NON-NLS-1$
531        }
532
533        if (gvShapes[glyphIndex] == null) {
534            gvShapes[glyphIndex] = vector[glyphIndex].getShape();
535        }
536
537        GeneralPath gp = (GeneralPath)((GeneralPath)gvShapes[glyphIndex]).clone();
538
539        /* Applying GlyphVector font transform */
540        AffineTransform at = (AffineTransform)this.transform.clone();
541
542        /* Applying Glyph transform */
543        AffineTransform glyphAT = getGlyphTransform(glyphIndex);
544        if (glyphAT != null){
545            at.preConcatenate(glyphAT);
546        }
547
548        int idx  = glyphIndex << 1;
549
550        gp.transform(at);
551        gp.transform(AffineTransform.getTranslateInstance(visualPositions[idx], visualPositions[idx+1]));
552        return gp;
553    }
554
555
556    /**
557     * Returns a Shape that is the outline representation of this GlyphVector
558     * rendered at the specified x,y coordinates.
559     *
560     * @param x specified x coordinate value
561     * @param y specified y coordinate value
562     * @return a Shape object that is the outline of this GlyphVector
563     * at the specified coordinates.
564     */
565    @Override
566    public Shape getOutline(float x, float y) {
567        GeneralPath gp = new GeneralPath(GeneralPath.WIND_EVEN_ODD);
568        for (int i = 0; i < this.vector.length; i++) {
569            GeneralPath outline = (GeneralPath)getGlyphOutline(i);
570
571            /* Applying translation to actual visual bounds */
572            outline.transform(AffineTransform.getTranslateInstance(x, y));
573            gp.append(outline, false);
574        }
575
576        return gp;
577    }
578
579    /**
580     * Returns a Shape that is the outline representation of this GlyphVector.
581     *
582     * @return a Shape object that is the outline of this GlyphVector
583     */
584    @Override
585    public Shape getOutline() {
586        return this.getOutline(0, 0);
587    }
588
589    /**
590     * Returns an array of glyphcodes for the specified glyphs.
591     *
592     * @param beginGlyphIndex the start index
593     * @param numEntries the number of glyph codes to get
594     * @param codeReturn the array that receives glyph codes' values
595     * @return an array that receives glyph codes' values
596     */
597    @Override
598    public int[] getGlyphCodes(int beginGlyphIndex, int numEntries,
599            int[] codeReturn) {
600
601        if ((beginGlyphIndex < 0) || ((numEntries + beginGlyphIndex) > this.getNumGlyphs())) {
602            // awt.44=beginGlyphIndex is out of vector's range
603            throw new IndexOutOfBoundsException(Messages.getString("awt.44")); //$NON-NLS-1$
604        }
605
606        if (numEntries < 0) {
607            // awt.45=numEntries is out of vector's range
608            throw new IllegalArgumentException(Messages.getString("awt.45")); //$NON-NLS-1$
609        }
610
611        if (codeReturn == null) {
612            codeReturn = new int[numEntries];
613        }
614
615        for (int i = beginGlyphIndex; i < beginGlyphIndex + numEntries; i++) {
616            codeReturn[i-beginGlyphIndex] = this.vector[i].getGlyphCode();
617        }
618
619        return codeReturn;
620    }
621
622    /**
623     * Returns an array of numEntries character indices for the specified glyphs.
624     *
625     * @param beginGlyphIndex the start index
626     * @param numEntries the number of glyph codes to get
627     * @param codeReturn the array that receives glyph codes' values
628     * @return an array that receives glyph char indices
629     */
630    @Override
631    public int[] getGlyphCharIndices(int beginGlyphIndex, int numEntries,
632            int[] codeReturn) {
633        if ((beginGlyphIndex < 0) || (beginGlyphIndex >= this.getNumGlyphs())) {
634            // awt.44=beginGlyphIndex is out of vector's range
635            throw new IllegalArgumentException(Messages.getString("awt.44")); //$NON-NLS-1$
636        }
637
638        if ((numEntries < 0)
639                || ((numEntries + beginGlyphIndex) > this.getNumGlyphs())) {
640            // awt.45=numEntries is out of vector's range
641            throw new IllegalArgumentException(Messages.getString("awt.45")); //$NON-NLS-1$
642        }
643
644        if (codeReturn == null) {
645            codeReturn = new int[numEntries];
646        }
647
648        for (int i = 0; i < numEntries; i++) {
649            codeReturn[i] = this.getGlyphCharIndex(i + beginGlyphIndex);
650        }
651        return codeReturn;
652    }
653
654    /**
655     * Returns an array of numEntries glyphs positions from beginGlyphIndex
656     * glyph in Glyph Vector.
657     *
658     * @param beginGlyphIndex the start index
659     * @param numEntries the number of glyph codes to get
660     * @param positionReturn the array that receives glyphs' positions
661     * @return an array of floats that receives glyph char indices
662     */
663    @Override
664    public float[] getGlyphPositions(int beginGlyphIndex, int numEntries,
665            float[] positionReturn) {
666
667        int len = (this.getNumGlyphs()+1) << 1;
668        beginGlyphIndex *= 2;
669        numEntries *= 2;
670
671        if ((beginGlyphIndex < 0) || ((numEntries + beginGlyphIndex) > len)) {
672            // awt.44=beginGlyphIndex is out of vector's range
673            throw new IndexOutOfBoundsException(Messages.getString("awt.44")); //$NON-NLS-1$
674        }
675
676        if (numEntries < 0) {
677            // awt.45=numEntries is out of vector's range
678            throw new IllegalArgumentException(Messages.getString("awt.45")); //$NON-NLS-1$
679        }
680
681        if (positionReturn == null) {
682            positionReturn = new float[numEntries];
683        }
684
685        System.arraycopy(visualPositions, beginGlyphIndex, positionReturn, 0, numEntries);
686
687        return positionReturn;
688    }
689
690    /**
691     * Set numEntries elements of the visualPositions array from beginGlyphIndex
692     * of numEntries glyphs positions from beginGlyphIndex glyph in Glyph Vector.
693     *
694     * @param beginGlyphIndex the start index
695     * @param numEntries the number of glyph codes to get
696     * @param setPositions the array of positions to set
697     */
698    public void setGlyphPositions(int beginGlyphIndex, int numEntries,
699            float[] setPositions) {
700
701        int len = (this.getNumGlyphs()+1) << 1;
702        beginGlyphIndex *= 2;
703        numEntries *= 2;
704
705        if ((beginGlyphIndex < 0) || ((numEntries + beginGlyphIndex) > len)) {
706            // awt.44=beginGlyphIndex is out of vector's range
707            throw new IndexOutOfBoundsException(Messages.getString("awt.44")); //$NON-NLS-1$
708        }
709
710        if (numEntries < 0) {
711            // awt.45=numEntries is out of vector's range
712            throw new IllegalArgumentException(Messages.getString("awt.45")); //$NON-NLS-1$
713        }
714
715        System.arraycopy(setPositions, 0, visualPositions, beginGlyphIndex, numEntries);
716        layoutFlags = layoutFlags & FLAG_HAS_POSITION_ADJUSTMENTS;
717
718    }
719
720    /**
721     * Set elements of the visualPositions array.
722     *
723     * @param setPositions the array of positions to set
724     */
725    public void setGlyphPositions(float[] setPositions) {
726
727        int len = (this.getNumGlyphs()+1) << 1;
728        if (len != setPositions.length){
729            // awt.46=length of setPositions array differs from the length of positions array
730            throw new IllegalArgumentException(Messages.getString("awt.46")); //$NON-NLS-1$
731        }
732
733        System.arraycopy(setPositions, 0, visualPositions, 0, len);
734        layoutFlags = layoutFlags & FLAG_HAS_POSITION_ADJUSTMENTS;
735
736    }
737
738
739    /**
740     * Returns glyph code of the specified glyph.
741     *
742     * @param glyphIndex specified index of the glyph
743     */
744    @Override
745    public int getGlyphCode(int glyphIndex) {
746        if (glyphIndex >= this.vector.length || glyphIndex < 0) {
747            // awt.43=glyphIndex is out of vector's limits
748            throw new IndexOutOfBoundsException(Messages.getString("awt.43")); //$NON-NLS-1$
749        }
750        return this.vector[glyphIndex].getGlyphCode();
751    }
752
753    /**
754     * Returns character index of the specified glyph.
755     *
756     * @param glyphIndex specified index of the glyph
757     */
758    @Override
759    public int getGlyphCharIndex(int glyphIndex) {
760
761        if ((glyphIndex < 0) || (glyphIndex >= this.getNumGlyphs())) {
762            // awt.43=glyphIndex is out of vector's limits
763            throw new IllegalArgumentException(Messages.getString("awt.43")); //$NON-NLS-1$
764        }
765
766        if ((this.layoutFlags & Font.LAYOUT_RIGHT_TO_LEFT) != 0) {
767            return this.charVector.length - glyphIndex - 1;
768        }
769
770        return glyphIndex;
771    }
772
773    /**
774     * Returns a character value of the specified glyph.
775     *
776     * @param glyphIndex specified index of the glyph
777     */
778    public char getGlyphChar(int glyphIndex) {
779
780        if ((glyphIndex < 0) || (glyphIndex >= this.getNumGlyphs())) {
781            // awt.43=glyphIndex is out of vector's limits
782            throw new IllegalArgumentException(Messages.getString("awt.43")); //$NON-NLS-1$
783        }
784        return this.charVector[glyphIndex];
785    }
786
787    /**
788     * Assigns default positions to each glyph in this GlyphVector.
789     */
790    @Override
791    public void performDefaultLayout() {
792
793        System.arraycopy(logicalPositions, 0, visualPositions, 0, logicalPositions.length);
794
795        // Set position changes flag to zero
796        clearLayoutFlags(GlyphVector.FLAG_HAS_POSITION_ADJUSTMENTS);
797    }
798
799    /**
800     * Returns the number of glyphs in this Glyph Vector
801     */
802    @Override
803    public int getNumGlyphs() {
804        return vector.length;
805    }
806
807    /**
808     * Returns the logical bounds of this GlyphVector
809     */
810    @Override
811    public Rectangle2D getLogicalBounds(){
812        // XXX: for transforms where an angle between basis vectors is not 90 degrees
813        // Rectanlge2D class doesn't fit as Logical bounds. For this reason we use
814        // only non-transformed bounds!!
815
816        float x = visualPositions[0];
817        float width = visualPositions[visualPositions.length-2];
818
819        double scaleY =  transform.getScaleY();
820
821        Rectangle2D bounds = new Rectangle2D.Float(x, (float)((-this.ascent-this.leading)*scaleY), width, (float)(this.height*scaleY));
822        return bounds;
823    }
824
825
826    /**
827     * Checks whether given GlyphVector equals to this GlyphVector.
828     * @param glyphVector GlyphVector object to compare
829     */
830    @Override
831    public boolean equals(GlyphVector glyphVector){
832        if (glyphVector == this){
833            return true;
834        }
835
836        if (glyphVector != null) {
837
838            if (!(glyphVector.getFontRenderContext().equals(this.vectorFRC) &&
839                      glyphVector.getFont().equals(this.font))){
840                return false;
841            }
842
843            try {
844                boolean eq = true;
845                for (int i = 0; i < getNumGlyphs(); i++) {
846
847                    int idx = i*2;
848                    eq = (((CommonGlyphVector)glyphVector).visualPositions[idx] == this.visualPositions[idx]) &&
849                        (((CommonGlyphVector)glyphVector).visualPositions[idx+1] == this.visualPositions[idx+1]) &&
850                        (glyphVector.getGlyphCharIndex(i) == this.getGlyphCharIndex(i));
851
852                    if (eq){
853                        AffineTransform trans = glyphVector.getGlyphTransform(i);
854                        if (trans == null){
855                            eq = (this.glsTransforms[i] == null);
856                        }else{
857                            eq = this.glsTransforms[i].equals(trans);
858                        }
859                    }
860
861                    if (!eq){
862                        return false;
863                    }
864                }
865
866                return  eq;
867            } catch (ClassCastException e) {
868            }
869        }
870
871        return false;
872    }
873
874
875    /**
876     * Returns flags describing the state of the GlyphVector.
877     */
878    @Override
879    public int getLayoutFlags() {
880        return layoutFlags;
881    }
882
883    /**
884     * Returns char with the specified index.
885     *
886     * @param index specified index of the char
887     *
888     */
889    public char getChar(int index) {
890        return this.charVector[index];
891
892    }
893
894    /**
895     * Clear desired flags in layout flags describing the state.
896     *
897     * @param clearFlags flags mask to clear
898     */
899
900    private void clearLayoutFlags(int clearFlags){
901        layoutFlags &= ~clearFlags;
902    }
903
904    /**
905     * Returns the logical bounds of the specified glyph within this CommonGlyphVector.
906     *
907     * @param glyphIndex index of the glyph to get it's logical bounds
908     * @return logical bounds of the specified glyph
909     */
910    @Override
911    public Shape getGlyphLogicalBounds(int glyphIndex){
912        if ((glyphIndex < 0) || (glyphIndex >= this.getNumGlyphs())){
913            // awt.43=glyphIndex is out of vector's limits
914            throw new IndexOutOfBoundsException(Messages.getString("awt.43")); //$NON-NLS-1$
915        }
916        Glyph glyph = this.vector[glyphIndex];
917
918        float x0 = visualPositions[glyphIndex*2];
919        float y0 = visualPositions[glyphIndex*2+1];
920        float advanceX = glyph.getGlyphPointMetrics().getAdvanceX();
921
922        GeneralPath gp = new GeneralPath();
923        gp.moveTo(0, -ascent - leading);
924        gp.lineTo(advanceX ,-ascent - leading);
925        gp.lineTo(advanceX, descent);
926        gp.lineTo(0, descent);
927        gp.lineTo(0, -ascent - leading);
928        gp.closePath();
929
930        /* Applying GlyphVector font transform */
931        AffineTransform at = (AffineTransform)this.transform.clone();
932
933        /* Applying Glyph transform */
934        AffineTransform glyphTransform = getGlyphTransform(glyphIndex);
935        if (glyphTransform != null){
936            at.concatenate(glyphTransform);
937        }
938
939        /* Applying translation to actual visual bounds */
940        at.preConcatenate(AffineTransform.getTranslateInstance(x0, y0));
941        gp.transform(at);
942        return gp;
943    }
944
945    /**
946     * Returns the Font parameter of this GlyphVector
947     */
948    @Override
949    public Font getFont(){
950        return this.font;
951    }
952
953
954}