| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| /** |
| * @author Ilya S. Okomin |
| * @version $Revision$ |
| */ |
| package org.apache.harmony.awt.gl.font; |
| |
| import java.awt.Font; |
| import java.awt.Rectangle; |
| import java.awt.Shape; |
| import java.awt.font.FontRenderContext; |
| import java.awt.font.GlyphJustificationInfo; |
| import java.awt.font.GlyphMetrics; |
| import java.awt.font.GlyphVector; |
| import java.awt.geom.AffineTransform; |
| import java.awt.geom.GeneralPath; |
| import java.awt.geom.Point2D; |
| import java.awt.geom.Rectangle2D; |
| |
| import org.apache.harmony.awt.internal.nls.Messages; |
| |
| /** |
| * GlyphVector implementation |
| */ |
| public class CommonGlyphVector extends GlyphVector { |
| |
| // array of transforms of glyphs in GlyphVector |
| protected AffineTransform[] glsTransforms; |
| |
| // array of chars defined in constructor |
| public char[] charVector; |
| |
| // array of Glyph objects, that describe information about glyphs |
| public Glyph[] vector; |
| |
| // array of default positions of glyphs in GlyphVector |
| // without applying GlyphVector's transform |
| float[] defaultPositions; |
| |
| // array of logical positions of glyphs in GlyphVector |
| |
| float[] logicalPositions; |
| |
| // array of visual (real) positions of glyphs in GlyphVector |
| public float[] visualPositions; |
| |
| // FontRenderContext for this vector. |
| protected FontRenderContext vectorFRC; |
| |
| // layout flags mask |
| protected int layoutFlags = 0; |
| |
| // array of cached glyph outlines |
| protected Shape[] gvShapes; |
| |
| FontPeerImpl peer; |
| |
| // font corresponding to the GlyphVector |
| Font font; |
| |
| // ascent of the font |
| float ascent; |
| |
| // height of the font |
| float height; |
| |
| // leading of the font |
| float leading; |
| |
| // descent of the font |
| float descent; |
| |
| // transform of the GlyphVector |
| AffineTransform transform; |
| |
| /** |
| * Creates new CommonGlyphVector object from the specified parameters. |
| * |
| * @param chars an array of chars |
| * @param frc FontRenderContext object |
| * @param fnt Font object |
| * @param flags layout flags |
| */ |
| @SuppressWarnings("deprecation") |
| public CommonGlyphVector(char[] chars, FontRenderContext frc, Font fnt, |
| int flags) { |
| int len = chars.length; |
| |
| this.font = fnt; |
| this.transform = fnt.getTransform(); |
| this.peer = (FontPeerImpl) fnt.getPeer(); |
| |
| gvShapes = new Shape[len]; |
| |
| // !! As pointed in API documentation for the |
| // getGlyphPosisitions(int index,int numEntries, float[] positionReturn) |
| // and getGlyphPosition(int index) methods, if the index is equals to |
| // the number of glyphs the position after the last glyph must be |
| // returned, thus there are n+1 positions and last (n+1) position |
| // points to the end of GlyphVector. |
| |
| logicalPositions = new float[(len+1)<<1]; |
| visualPositions = new float[(len+1)<<1]; |
| defaultPositions = new float[(len+1)<<1]; |
| |
| glsTransforms = new AffineTransform[len]; |
| |
| this.charVector = chars; |
| this.vectorFRC = frc; |
| //LineMetricsImpl lmImpl = (LineMetricsImpl)peer.getLineMetrics(); |
| |
| LineMetricsImpl lmImpl = (LineMetricsImpl)fnt.getLineMetrics(String.valueOf(chars), frc); |
| |
| this.ascent = lmImpl.getAscent(); |
| this.height = lmImpl.getHeight(); |
| this.leading = lmImpl.getLeading(); |
| this.descent = lmImpl.getDescent(); |
| this.layoutFlags = flags; |
| |
| if ((flags & Font.LAYOUT_RIGHT_TO_LEFT) != 0){ |
| char vector[] = new char[len]; |
| for(int i=0; i < len; i++){ |
| vector[i] = chars[len-i-1]; |
| } |
| this.vector = peer.getGlyphs(vector); |
| |
| } else { |
| this.vector = peer.getGlyphs(chars); |
| } |
| |
| this.glsTransforms = new AffineTransform[len]; |
| |
| setDefaultPositions(); |
| performDefaultLayout(); |
| } |
| |
| /** |
| * Creates new CommonGlyphVector object from the specified parameters. |
| * Layout flags set to default. |
| * |
| * @param chars an array of chars |
| * @param frc FontRenderContext object |
| * @param fnt Font object |
| */ |
| public CommonGlyphVector(char[] chars, FontRenderContext frc, Font fnt) { |
| this(chars, frc, fnt, 0); |
| } |
| |
| /** |
| * Creates new CommonGlyphVector object from the specified parameters. |
| * Layout flags set to default. |
| * |
| * @param str specified string |
| * @param frc FontRenderContext object |
| * @param fnt Font object |
| */ |
| public CommonGlyphVector(String str, FontRenderContext frc, Font fnt) { |
| this(str.toCharArray(), frc, fnt, 0); |
| } |
| |
| /** |
| * Creates new CommonGlyphVector object from the specified parameters. |
| * |
| * @param str specified string |
| * @param frc FontRenderContext object |
| * @param fnt Font object |
| * @param flags layout flags |
| */ |
| public CommonGlyphVector(String str, FontRenderContext frc, Font fnt, int flags) { |
| this(str.toCharArray(), frc, fnt, flags); |
| } |
| |
| /** |
| * Set array of logical positions of the glyphs to |
| * default with their default advances and height. |
| */ |
| void setDefaultPositions(){ |
| int len = getNumGlyphs(); |
| |
| // First [x,y] is set into [0,0] position |
| // for this reason start index is 1 |
| for (int i=1; i <= len; i++ ){ |
| int idx = i << 1; |
| float advanceX = vector[i-1].getGlyphPointMetrics().getAdvanceX(); |
| float advanceY = vector[i-1].getGlyphPointMetrics().getAdvanceY(); |
| |
| defaultPositions[idx] = defaultPositions[idx-2] + advanceX; |
| defaultPositions[idx+1] = defaultPositions[idx-1] + advanceY; |
| |
| } |
| transform.transform(defaultPositions, 0, logicalPositions, 0, getNumGlyphs()+1); |
| |
| } |
| |
| /** |
| * Returnes the pixel bounds of this GlyphVector rendered at the |
| * specified x,y location with the given FontRenderContext. |
| * |
| * @param frc a FontRenderContext that is used |
| * @param x specified x coordinate value |
| * @param y specified y coordinate value |
| * @return a Rectangle that bounds pixels of this GlyphVector |
| */ |
| @Override |
| public Rectangle getPixelBounds(FontRenderContext frc, float x, float y) { |
| |
| double xM, yM, xm, ym; |
| |
| double minX = 0; |
| double minY = 0; |
| double maxX = 0; |
| double maxY = 0; |
| |
| for (int i = 0; i < this.getNumGlyphs(); i++) { |
| Rectangle glyphBounds = this.getGlyphPixelBounds(i, frc, 0, 0); |
| xm = glyphBounds.getMinX(); |
| ym = glyphBounds.getMinY(); |
| xM = glyphBounds.getMaxX(); |
| yM = glyphBounds.getMaxY(); |
| |
| if (i == 0) { |
| minX = xm; |
| minY = ym; |
| maxX = xM; |
| maxY = yM; |
| } |
| |
| if (minX > xm) { |
| minX = xm; |
| } |
| if (minY > ym) { |
| minY = ym; |
| } |
| if (maxX < xM) { |
| maxX = xM; |
| } |
| if (maxY < yM) { |
| maxY = yM; |
| } |
| } |
| return new Rectangle((int)(minX + x), (int)(minY + y), (int)(maxX - minX), (int)(maxY - minY)); |
| |
| } |
| |
| /** |
| * Returns the visual bounds of this GlyphVector. |
| * The visual bounds is the bounds of the total outline of |
| * this GlyphVector. |
| * @return a Rectangle2D that id the visual bounds of this GlyphVector |
| */ |
| @Override |
| public Rectangle2D getVisualBounds() { |
| float xM, yM, xm, ym; |
| float minX = 0; |
| float minY = 0; |
| float maxX = 0; |
| float maxY = 0; |
| boolean firstIteration = true; |
| |
| for (int i = 0; i < this.getNumGlyphs(); i++) { |
| Rectangle2D bounds = this.getGlyphVisualBounds(i).getBounds2D(); |
| if (bounds.getWidth() == 0){ |
| continue; |
| } |
| xm = (float)bounds.getX(); |
| ym = (float)bounds.getY(); |
| |
| xM = (float)(xm + bounds.getWidth()); |
| |
| yM = ym + (float) bounds.getHeight(); |
| |
| if (firstIteration) { |
| minX = xm; |
| minY = ym; |
| maxX = xM; |
| maxY = yM; |
| firstIteration = false; |
| } else { |
| if (minX > xm) { |
| minX = xm; |
| } |
| if (minY > ym) { |
| minY = ym; |
| } |
| if (maxX < xM) { |
| maxX = xM; |
| } |
| if (maxY < yM) { |
| maxY = yM; |
| } |
| |
| } |
| } |
| |
| return (this.getNumGlyphs() != 0) ? new Rectangle2D.Float(minX, minY, |
| (maxX - minX), (maxY - minY)) : null; |
| } |
| |
| /** |
| * Sets new position to the specified glyph. |
| */ |
| @Override |
| public void setGlyphPosition(int glyphIndex, Point2D newPos) { |
| if ((glyphIndex > vector.length) || (glyphIndex < 0)) { |
| // awt.43=glyphIndex is out of vector's limits |
| throw new IndexOutOfBoundsException(Messages.getString("awt.43")); //$NON-NLS-1$ |
| } |
| float x = (float)newPos.getX(); |
| float y = (float)newPos.getY(); |
| int index = glyphIndex << 1; |
| |
| if ((x != visualPositions[index]) || (y != visualPositions[index + 1])){ |
| visualPositions[index] = x; |
| visualPositions[index+1] = y; |
| layoutFlags = layoutFlags | FLAG_HAS_POSITION_ADJUSTMENTS; |
| } |
| |
| } |
| |
| /** |
| * Returns the position of the specified glyph relative to the origin of |
| * this GlyphVector |
| * @return a Point2D that the origin of the glyph with specified index |
| */ |
| @Override |
| public Point2D getGlyphPosition(int glyphIndex) { |
| if ((glyphIndex > vector.length) || (glyphIndex < 0)) { |
| // awt.43=glyphIndex is out of vector's limits |
| throw new IndexOutOfBoundsException(Messages.getString("awt.43")); //$NON-NLS-1$ |
| } |
| int index = glyphIndex << 1; |
| Point2D pos = new Point2D.Float(visualPositions[index], visualPositions[index+1]); |
| |
| // For last position we don't have to transform !! |
| if(glyphIndex==vector.length){ |
| return pos; |
| } |
| |
| AffineTransform at = getGlyphTransform(glyphIndex); |
| if ((at == null) || (at.isIdentity())){ |
| return pos; |
| } |
| |
| pos.setLocation(pos.getX() + at.getTranslateX(), pos.getY() + at.getTranslateY()); |
| |
| return pos; |
| } |
| |
| /** |
| * Sets new transform to the specified glyph. |
| * |
| * @param glyphIndex specified index of the glyph |
| * @param trans AffineTransform of the glyph with specified index |
| */ |
| @Override |
| public void setGlyphTransform(int glyphIndex, AffineTransform trans) { |
| if ((glyphIndex >= vector.length) || (glyphIndex < 0)) { |
| // awt.43=glyphIndex is out of vector's limits |
| throw new IndexOutOfBoundsException(Messages.getString("awt.43")); //$NON-NLS-1$ |
| } |
| |
| if ((trans == null) || (trans.isIdentity())) { |
| glsTransforms[glyphIndex] = null; |
| } else { |
| glsTransforms[glyphIndex] = new AffineTransform(trans); |
| layoutFlags = layoutFlags | FLAG_HAS_TRANSFORMS; |
| } |
| } |
| |
| /** |
| * Returns the affine transform of the specified glyph. |
| * |
| * @param glyphIndex specified index of the glyph |
| * @return an AffineTransform of the glyph with specified index |
| */ |
| @Override |
| public AffineTransform getGlyphTransform(int glyphIndex) { |
| if ((glyphIndex >= this.vector.length) || (glyphIndex < 0)) { |
| // awt.43=glyphIndex is out of vector's limits |
| throw new IndexOutOfBoundsException(Messages.getString("awt.43")); //$NON-NLS-1$ |
| } |
| return this.glsTransforms[glyphIndex]; |
| } |
| |
| /** |
| * Returns the metrics of the specified glyph. |
| * |
| * @param glyphIndex specified index of the glyph |
| */ |
| @Override |
| public GlyphMetrics getGlyphMetrics(int glyphIndex) { |
| |
| if ((glyphIndex < 0) || ((glyphIndex) >= this.getNumGlyphs())) { |
| // awt.43=glyphIndex is out of vector's limits |
| throw new IndexOutOfBoundsException(Messages.getString("awt.43")); //$NON-NLS-1$ |
| } |
| // TODO: is there a sence in GlyphMetrics |
| // if certain glyph or Font has a transform?? |
| return this.vector[glyphIndex].getGlyphMetrics(); |
| } |
| |
| /** |
| * Returns a justification information for the glyph with specified glyph |
| * index. |
| * @param glyphIndex index of a glyph which GlyphJustificationInfo is to be |
| * received |
| * @return a GlyphJustificationInfo object that contains glyph justification |
| * properties of the specified glyph |
| */ |
| @Override |
| public GlyphJustificationInfo getGlyphJustificationInfo(int glyphIndex) { |
| // TODO : Find out the source of Justification info |
| if (true) { |
| throw new RuntimeException("Method is not implemented"); //$NON-NLS-1$ |
| } |
| return null; |
| } |
| |
| /** |
| * Returns the FontRenderContext parameter of this GlyphVector. |
| */ |
| @Override |
| public FontRenderContext getFontRenderContext() { |
| return this.vectorFRC; |
| } |
| |
| /** |
| * Returns the visual bounds of the specified glyph. |
| * |
| * @param glyphIndex specified index of the glyph |
| */ |
| @Override |
| public Shape getGlyphVisualBounds(int glyphIndex) { |
| if ((glyphIndex < 0) || (glyphIndex >= this.getNumGlyphs())) { |
| // awt.43=glyphIndex is out of vector's limits |
| throw new IndexOutOfBoundsException(Messages.getString("awt.43")); //$NON-NLS-1$ |
| } |
| |
| int idx = glyphIndex << 1; |
| |
| AffineTransform fontTransform = this.transform; |
| double xOffs = fontTransform.getTranslateX(); |
| double yOffs = fontTransform.getTranslateY(); |
| |
| if (vector[glyphIndex].getWidth() == 0){ |
| return new Rectangle2D.Float((float)xOffs, (float)yOffs, 0, 0); |
| } |
| |
| AffineTransform at = AffineTransform.getTranslateInstance(xOffs, yOffs); |
| AffineTransform glyphTransform = getGlyphTransform(glyphIndex); |
| |
| if (transform.isIdentity() && ((glyphTransform == null) || glyphTransform.isIdentity())){ |
| Rectangle2D blackBox = vector[glyphIndex].getGlyphMetrics().getBounds2D(); |
| at.translate(visualPositions[idx], visualPositions[idx+1]); |
| return(at.createTransformedShape(blackBox)); |
| } |
| |
| GeneralPath shape = (GeneralPath)this.getGlyphOutline(glyphIndex); |
| shape.transform(at); |
| return shape.getBounds2D(); |
| } |
| |
| /** |
| * Returnes the pixel bounds of the specified glyph within GlyphVector |
| * rendered at the specified x,y location. |
| * |
| * @param glyphIndex index of the glyph |
| * @param frc a FontRenderContext that is used |
| * @param x specified x coordinate value |
| * @param y specified y coordinate value |
| * @return a Rectangle that bounds pixels of the specified glyph |
| */ |
| @Override |
| public Rectangle getGlyphPixelBounds(int glyphIndex, FontRenderContext frc, |
| float x, float y) { |
| // TODO : need to be implemented with FontRenderContext |
| if ((glyphIndex < 0) || (glyphIndex >= this.getNumGlyphs())) { |
| // awt.43=glyphIndex is out of vector's limits |
| throw new IndexOutOfBoundsException(Messages.getString("awt.43")); //$NON-NLS-1$ |
| } |
| |
| int idx = glyphIndex << 1; |
| |
| if (vector[glyphIndex].getWidth() == 0){ |
| AffineTransform fontTransform = this.transform; |
| double xOffs = x + visualPositions[idx] + fontTransform.getTranslateX(); |
| double yOffs = y + visualPositions[idx+1] + fontTransform.getTranslateY(); |
| return new Rectangle((int)xOffs, (int)yOffs, 0, 0); |
| } |
| |
| GeneralPath shape = (GeneralPath)this.getGlyphOutline(glyphIndex); |
| |
| AffineTransform at = AffineTransform.getTranslateInstance(x, y); |
| |
| if (frc != null){ |
| at.concatenate(frc.getTransform()); |
| } |
| |
| shape.transform(at); |
| |
| Rectangle bounds = shape.getBounds(); |
| return new Rectangle((int)bounds.getX(), (int)bounds.getY(), |
| (int)bounds.getWidth()-1, (int)bounds.getHeight()-1); |
| } |
| |
| /** |
| * Returns a Shape that encloses specified glyph. |
| * |
| * @param glyphIndex specified index of the glyph |
| */ |
| @Override |
| public Shape getGlyphOutline(int glyphIndex) { |
| if ((glyphIndex < 0) || (glyphIndex >= this.getNumGlyphs())) { |
| // awt.43=glyphIndex is out of vector's limits |
| throw new IndexOutOfBoundsException(Messages.getString("awt.43")); //$NON-NLS-1$ |
| } |
| |
| if (gvShapes[glyphIndex] == null) { |
| gvShapes[glyphIndex] = vector[glyphIndex].getShape(); |
| } |
| |
| GeneralPath gp = (GeneralPath)((GeneralPath)gvShapes[glyphIndex]).clone(); |
| |
| /* Applying GlyphVector font transform */ |
| AffineTransform at = (AffineTransform)this.transform.clone(); |
| |
| /* Applying Glyph transform */ |
| AffineTransform glyphAT = getGlyphTransform(glyphIndex); |
| if (glyphAT != null){ |
| at.preConcatenate(glyphAT); |
| } |
| |
| int idx = glyphIndex << 1; |
| |
| gp.transform(at); |
| gp.transform(AffineTransform.getTranslateInstance(visualPositions[idx], visualPositions[idx+1])); |
| return gp; |
| } |
| |
| |
| /** |
| * Returns a Shape that is the outline representation of this GlyphVector |
| * rendered at the specified x,y coordinates. |
| * |
| * @param x specified x coordinate value |
| * @param y specified y coordinate value |
| * @return a Shape object that is the outline of this GlyphVector |
| * at the specified coordinates. |
| */ |
| @Override |
| public Shape getOutline(float x, float y) { |
| GeneralPath gp = new GeneralPath(GeneralPath.WIND_EVEN_ODD); |
| for (int i = 0; i < this.vector.length; i++) { |
| GeneralPath outline = (GeneralPath)getGlyphOutline(i); |
| |
| /* Applying translation to actual visual bounds */ |
| outline.transform(AffineTransform.getTranslateInstance(x, y)); |
| gp.append(outline, false); |
| } |
| |
| return gp; |
| } |
| |
| /** |
| * Returns a Shape that is the outline representation of this GlyphVector. |
| * |
| * @return a Shape object that is the outline of this GlyphVector |
| */ |
| @Override |
| public Shape getOutline() { |
| return this.getOutline(0, 0); |
| } |
| |
| /** |
| * Returns an array of glyphcodes for the specified glyphs. |
| * |
| * @param beginGlyphIndex the start index |
| * @param numEntries the number of glyph codes to get |
| * @param codeReturn the array that receives glyph codes' values |
| * @return an array that receives glyph codes' values |
| */ |
| @Override |
| public int[] getGlyphCodes(int beginGlyphIndex, int numEntries, |
| int[] codeReturn) { |
| |
| if ((beginGlyphIndex < 0) || ((numEntries + beginGlyphIndex) > this.getNumGlyphs())) { |
| // awt.44=beginGlyphIndex is out of vector's range |
| throw new IndexOutOfBoundsException(Messages.getString("awt.44")); //$NON-NLS-1$ |
| } |
| |
| if (numEntries < 0) { |
| // awt.45=numEntries is out of vector's range |
| throw new IllegalArgumentException(Messages.getString("awt.45")); //$NON-NLS-1$ |
| } |
| |
| if (codeReturn == null) { |
| codeReturn = new int[numEntries]; |
| } |
| |
| for (int i = beginGlyphIndex; i < beginGlyphIndex + numEntries; i++) { |
| codeReturn[i-beginGlyphIndex] = this.vector[i].getGlyphCode(); |
| } |
| |
| return codeReturn; |
| } |
| |
| /** |
| * Returns an array of numEntries character indices for the specified glyphs. |
| * |
| * @param beginGlyphIndex the start index |
| * @param numEntries the number of glyph codes to get |
| * @param codeReturn the array that receives glyph codes' values |
| * @return an array that receives glyph char indices |
| */ |
| @Override |
| public int[] getGlyphCharIndices(int beginGlyphIndex, int numEntries, |
| int[] codeReturn) { |
| if ((beginGlyphIndex < 0) || (beginGlyphIndex >= this.getNumGlyphs())) { |
| // awt.44=beginGlyphIndex is out of vector's range |
| throw new IllegalArgumentException(Messages.getString("awt.44")); //$NON-NLS-1$ |
| } |
| |
| if ((numEntries < 0) |
| || ((numEntries + beginGlyphIndex) > this.getNumGlyphs())) { |
| // awt.45=numEntries is out of vector's range |
| throw new IllegalArgumentException(Messages.getString("awt.45")); //$NON-NLS-1$ |
| } |
| |
| if (codeReturn == null) { |
| codeReturn = new int[numEntries]; |
| } |
| |
| for (int i = 0; i < numEntries; i++) { |
| codeReturn[i] = this.getGlyphCharIndex(i + beginGlyphIndex); |
| } |
| return codeReturn; |
| } |
| |
| /** |
| * Returns an array of numEntries glyphs positions from beginGlyphIndex |
| * glyph in Glyph Vector. |
| * |
| * @param beginGlyphIndex the start index |
| * @param numEntries the number of glyph codes to get |
| * @param positionReturn the array that receives glyphs' positions |
| * @return an array of floats that receives glyph char indices |
| */ |
| @Override |
| public float[] getGlyphPositions(int beginGlyphIndex, int numEntries, |
| float[] positionReturn) { |
| |
| int len = (this.getNumGlyphs()+1) << 1; |
| beginGlyphIndex *= 2; |
| numEntries *= 2; |
| |
| if ((beginGlyphIndex < 0) || ((numEntries + beginGlyphIndex) > len)) { |
| // awt.44=beginGlyphIndex is out of vector's range |
| throw new IndexOutOfBoundsException(Messages.getString("awt.44")); //$NON-NLS-1$ |
| } |
| |
| if (numEntries < 0) { |
| // awt.45=numEntries is out of vector's range |
| throw new IllegalArgumentException(Messages.getString("awt.45")); //$NON-NLS-1$ |
| } |
| |
| if (positionReturn == null) { |
| positionReturn = new float[numEntries]; |
| } |
| |
| System.arraycopy(visualPositions, beginGlyphIndex, positionReturn, 0, numEntries); |
| |
| return positionReturn; |
| } |
| |
| /** |
| * Set numEntries elements of the visualPositions array from beginGlyphIndex |
| * of numEntries glyphs positions from beginGlyphIndex glyph in Glyph Vector. |
| * |
| * @param beginGlyphIndex the start index |
| * @param numEntries the number of glyph codes to get |
| * @param setPositions the array of positions to set |
| */ |
| public void setGlyphPositions(int beginGlyphIndex, int numEntries, |
| float[] setPositions) { |
| |
| int len = (this.getNumGlyphs()+1) << 1; |
| beginGlyphIndex *= 2; |
| numEntries *= 2; |
| |
| if ((beginGlyphIndex < 0) || ((numEntries + beginGlyphIndex) > len)) { |
| // awt.44=beginGlyphIndex is out of vector's range |
| throw new IndexOutOfBoundsException(Messages.getString("awt.44")); //$NON-NLS-1$ |
| } |
| |
| if (numEntries < 0) { |
| // awt.45=numEntries is out of vector's range |
| throw new IllegalArgumentException(Messages.getString("awt.45")); //$NON-NLS-1$ |
| } |
| |
| System.arraycopy(setPositions, 0, visualPositions, beginGlyphIndex, numEntries); |
| layoutFlags = layoutFlags & FLAG_HAS_POSITION_ADJUSTMENTS; |
| |
| } |
| |
| /** |
| * Set elements of the visualPositions array. |
| * |
| * @param setPositions the array of positions to set |
| */ |
| public void setGlyphPositions(float[] setPositions) { |
| |
| int len = (this.getNumGlyphs()+1) << 1; |
| if (len != setPositions.length){ |
| // awt.46=length of setPositions array differs from the length of positions array |
| throw new IllegalArgumentException(Messages.getString("awt.46")); //$NON-NLS-1$ |
| } |
| |
| System.arraycopy(setPositions, 0, visualPositions, 0, len); |
| layoutFlags = layoutFlags & FLAG_HAS_POSITION_ADJUSTMENTS; |
| |
| } |
| |
| |
| /** |
| * Returns glyph code of the specified glyph. |
| * |
| * @param glyphIndex specified index of the glyph |
| */ |
| @Override |
| public int getGlyphCode(int glyphIndex) { |
| if (glyphIndex >= this.vector.length || glyphIndex < 0) { |
| // awt.43=glyphIndex is out of vector's limits |
| throw new IndexOutOfBoundsException(Messages.getString("awt.43")); //$NON-NLS-1$ |
| } |
| return this.vector[glyphIndex].getGlyphCode(); |
| } |
| |
| /** |
| * Returns character index of the specified glyph. |
| * |
| * @param glyphIndex specified index of the glyph |
| */ |
| @Override |
| public int getGlyphCharIndex(int glyphIndex) { |
| |
| if ((glyphIndex < 0) || (glyphIndex >= this.getNumGlyphs())) { |
| // awt.43=glyphIndex is out of vector's limits |
| throw new IllegalArgumentException(Messages.getString("awt.43")); //$NON-NLS-1$ |
| } |
| |
| if ((this.layoutFlags & Font.LAYOUT_RIGHT_TO_LEFT) != 0) { |
| return this.charVector.length - glyphIndex - 1; |
| } |
| |
| return glyphIndex; |
| } |
| |
| /** |
| * Returns a character value of the specified glyph. |
| * |
| * @param glyphIndex specified index of the glyph |
| */ |
| public char getGlyphChar(int glyphIndex) { |
| |
| if ((glyphIndex < 0) || (glyphIndex >= this.getNumGlyphs())) { |
| // awt.43=glyphIndex is out of vector's limits |
| throw new IllegalArgumentException(Messages.getString("awt.43")); //$NON-NLS-1$ |
| } |
| return this.charVector[glyphIndex]; |
| } |
| |
| /** |
| * Assigns default positions to each glyph in this GlyphVector. |
| */ |
| @Override |
| public void performDefaultLayout() { |
| |
| System.arraycopy(logicalPositions, 0, visualPositions, 0, logicalPositions.length); |
| |
| // Set position changes flag to zero |
| clearLayoutFlags(GlyphVector.FLAG_HAS_POSITION_ADJUSTMENTS); |
| } |
| |
| /** |
| * Returns the number of glyphs in this Glyph Vector |
| */ |
| @Override |
| public int getNumGlyphs() { |
| return vector.length; |
| } |
| |
| /** |
| * Returns the logical bounds of this GlyphVector |
| */ |
| @Override |
| public Rectangle2D getLogicalBounds(){ |
| // XXX: for transforms where an angle between basis vectors is not 90 degrees |
| // Rectanlge2D class doesn't fit as Logical bounds. For this reason we use |
| // only non-transformed bounds!! |
| |
| float x = visualPositions[0]; |
| float width = visualPositions[visualPositions.length-2]; |
| |
| double scaleY = transform.getScaleY(); |
| |
| Rectangle2D bounds = new Rectangle2D.Float(x, (float)((-this.ascent-this.leading)*scaleY), width, (float)(this.height*scaleY)); |
| return bounds; |
| } |
| |
| |
| /** |
| * Checks whether given GlyphVector equals to this GlyphVector. |
| * @param glyphVector GlyphVector object to compare |
| */ |
| @Override |
| public boolean equals(GlyphVector glyphVector){ |
| if (glyphVector == this){ |
| return true; |
| } |
| |
| if (glyphVector != null) { |
| |
| if (!(glyphVector.getFontRenderContext().equals(this.vectorFRC) && |
| glyphVector.getFont().equals(this.font))){ |
| return false; |
| } |
| |
| try { |
| boolean eq = true; |
| for (int i = 0; i < getNumGlyphs(); i++) { |
| |
| int idx = i*2; |
| eq = (((CommonGlyphVector)glyphVector).visualPositions[idx] == this.visualPositions[idx]) && |
| (((CommonGlyphVector)glyphVector).visualPositions[idx+1] == this.visualPositions[idx+1]) && |
| (glyphVector.getGlyphCharIndex(i) == this.getGlyphCharIndex(i)); |
| |
| if (eq){ |
| AffineTransform trans = glyphVector.getGlyphTransform(i); |
| if (trans == null){ |
| eq = (this.glsTransforms[i] == null); |
| }else{ |
| eq = this.glsTransforms[i].equals(trans); |
| } |
| } |
| |
| if (!eq){ |
| return false; |
| } |
| } |
| |
| return eq; |
| } catch (ClassCastException e) { |
| } |
| } |
| |
| return false; |
| } |
| |
| |
| /** |
| * Returns flags describing the state of the GlyphVector. |
| */ |
| @Override |
| public int getLayoutFlags() { |
| return layoutFlags; |
| } |
| |
| /** |
| * Returns char with the specified index. |
| * |
| * @param index specified index of the char |
| * |
| */ |
| public char getChar(int index) { |
| return this.charVector[index]; |
| |
| } |
| |
| /** |
| * Clear desired flags in layout flags describing the state. |
| * |
| * @param clearFlags flags mask to clear |
| */ |
| |
| private void clearLayoutFlags(int clearFlags){ |
| layoutFlags &= ~clearFlags; |
| } |
| |
| /** |
| * Returns the logical bounds of the specified glyph within this CommonGlyphVector. |
| * |
| * @param glyphIndex index of the glyph to get it's logical bounds |
| * @return logical bounds of the specified glyph |
| */ |
| @Override |
| public Shape getGlyphLogicalBounds(int glyphIndex){ |
| if ((glyphIndex < 0) || (glyphIndex >= this.getNumGlyphs())){ |
| // awt.43=glyphIndex is out of vector's limits |
| throw new IndexOutOfBoundsException(Messages.getString("awt.43")); //$NON-NLS-1$ |
| } |
| Glyph glyph = this.vector[glyphIndex]; |
| |
| float x0 = visualPositions[glyphIndex*2]; |
| float y0 = visualPositions[glyphIndex*2+1]; |
| float advanceX = glyph.getGlyphPointMetrics().getAdvanceX(); |
| |
| GeneralPath gp = new GeneralPath(); |
| gp.moveTo(0, -ascent - leading); |
| gp.lineTo(advanceX ,-ascent - leading); |
| gp.lineTo(advanceX, descent); |
| gp.lineTo(0, descent); |
| gp.lineTo(0, -ascent - leading); |
| gp.closePath(); |
| |
| /* Applying GlyphVector font transform */ |
| AffineTransform at = (AffineTransform)this.transform.clone(); |
| |
| /* Applying Glyph transform */ |
| AffineTransform glyphTransform = getGlyphTransform(glyphIndex); |
| if (glyphTransform != null){ |
| at.concatenate(glyphTransform); |
| } |
| |
| /* Applying translation to actual visual bounds */ |
| at.preConcatenate(AffineTransform.getTranslateInstance(x0, y0)); |
| gp.transform(at); |
| return gp; |
| } |
| |
| /** |
| * Returns the Font parameter of this GlyphVector |
| */ |
| @Override |
| public Font getFont(){ |
| return this.font; |
| } |
| |
| |
| } |