blob: 4040a60d9207a0b210736f3476312b7f4b8ee69f [file] [log] [blame]
/*
* 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;
}
}