import java.awt.Font;
import java.awt.Shape;
-import java.awt.geom.AffineTransform;
-import java.awt.geom.Point2D;
-import java.awt.geom.Rectangle2D;
-import java.awt.geom.GeneralPath;
+import java.awt.font.FontRenderContext;
import java.awt.font.GlyphJustificationInfo;
import java.awt.font.GlyphMetrics;
import java.awt.font.GlyphVector;
-import java.awt.font.FontRenderContext;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.GeneralPath;
+import java.awt.geom.Point2D;
+import java.awt.geom.Rectangle2D;
+import java.util.Arrays;
public class FreetypeGlyphVector extends GlyphVector
{
*/
public FreetypeGlyphVector(Font f, String s, FontRenderContext frc)
{
- this(f, s, frc, Font.LAYOUT_LEFT_TO_RIGHT);
+ this(f, s.toCharArray(), 0, s.length(), frc, Font.LAYOUT_LEFT_TO_RIGHT);
}
/**
* Create a glyphvector from a given (Freetype) font and a String.
*/
- public FreetypeGlyphVector(Font f, String s, FontRenderContext frc,
- int flags)
+ public FreetypeGlyphVector(Font f, char[] chars, int start, int len,
+ FontRenderContext frc, int flags)
{
- this.s = s;
+ this.s = new String(chars, start, len);
+
this.font = f;
this.frc = frc;
if( !(font.getPeer() instanceof GdkFontPeer ) )
}
glyphCodes = new int[ nGlyphs ];
- glyphPositions = new float[ nGlyphs ];
+ glyphPositions = new float[(nGlyphs + 1) * 2];
glyphTransforms = new AffineTransform[ nGlyphs ];
for(int i = 0; i < nGlyphs; i++ )
{
- glyphTransforms[ i ] = new AffineTransform( gv.glyphTransforms[ i ] );
- glyphCodes[i] = gv.glyphCodes[ i ];
- glyphPositions[i] = gv.glyphPositions[ i ];
+ glyphTransforms[ i ] = new AffineTransform( gv.glyphTransforms[ i ] );
+ glyphCodes[i] = gv.glyphCodes[ i ];
}
+ System.arraycopy(gv.glyphPositions, 0, glyphPositions, 0,
+ glyphPositions.length);
}
/**
for(int i = 0; i < nGlyphs; i++)
{
codePoints[i] = s.codePointAt( stringIndex );
- // UTF32 surrogate handling
+ // UTF32 surrogate handling
if( codePoints[i] != (int)s.charAt( stringIndex ) )
stringIndex ++;
stringIndex ++;
+
+ if (Character.isISOControl(codePoints[i]))
+ {
+ // Replace with 'hair space'. Should better be 'zero-width space'
+ // but that doesn't seem to be supported by default font.
+ codePoints[i] = 8202;
+ }
}
glyphCodes = getGlyphs( codePoints );
public void performDefaultLayout()
{
logicalBounds = null; // invalidate caches.
- glyphPositions = null;
-
- glyphTransforms = new AffineTransform[ nGlyphs ];
- double x = 0;
+ glyphTransforms = new AffineTransform[nGlyphs];
+ Arrays.fill(glyphTransforms, null);
+ glyphPositions = new float[(nGlyphs + 1) * 2];
+ GlyphMetrics gm = null;
+ float x = 0;
+ float y = 0;
for(int i = 0; i < nGlyphs; i++)
{
- GlyphMetrics gm = getGlyphMetrics( i );
- glyphTransforms[ i ] = AffineTransform.getTranslateInstance(x, 0);
- x += gm.getAdvanceX();
- if( i > 0 )
- {
- Point2D p = getKerning( glyphCodes[ i - 1 ], glyphCodes[ i ] );
- x += p.getX();
- }
+ gm = getGlyphMetrics( i );
+ glyphPositions[i*2] = x;
+ glyphPositions[i*2 + 1] = y;
+
+ x += gm.getAdvanceX();
+ y += gm.getAdvanceY();
+
+ if (i != nGlyphs-1)
+ {
+ Point2D p = getKerning(glyphCodes[i], glyphCodes[i + 1]);
+ x += p.getX();
+ y += p.getY();
+ }
}
+ glyphPositions[nGlyphs * 2] = x;
+ glyphPositions[nGlyphs * 2 + 1] = y;
}
/**
{
int[] rval;
- if( codeReturn == null )
+ if( codeReturn == null || codeReturn.length < numEntries)
rval = new int[ numEntries ];
else
rval = codeReturn;
return rval;
}
- /**
- * FIXME: Implement me.
- */
public Shape getGlyphLogicalBounds(int glyphIndex)
{
GlyphMetrics gm = getGlyphMetrics( glyphIndex );
return null;
Rectangle2D r = gm.getBounds2D();
Point2D p = getGlyphPosition( glyphIndex );
- return new Rectangle2D.Double( p.getX() + r.getX() - gm.getLSB(),
- p.getY() + r.getY(),
- gm.getAdvanceX(),
- r.getHeight() );
+
+ double[] bounds = new double[] {p.getX() + r.getX() - gm.getLSB(),
+ p.getY() + r.getY(),
+ p.getX() + r.getX() - gm.getLSB() + gm.getAdvanceX(),
+ p.getY() + r.getY() + r.getHeight()};
+
+ if (glyphTransforms[glyphIndex] != null)
+ glyphTransforms[glyphIndex].transform(bounds, 0, bounds, 0, 4);
+
+ return new Rectangle2D.Double(bounds[0], bounds[1], bounds[2] - bounds[0],
+ bounds[3] - bounds[1]);
}
/*
public Shape getGlyphOutline(int glyphIndex)
{
GeneralPath gp = getGlyphOutlineNative( glyphCodes[ glyphIndex ] );
- gp.transform( glyphTransforms[ glyphIndex ] );
+ if (glyphTransforms[glyphIndex] != null)
+ gp.transform( glyphTransforms[glyphIndex]);
+
return gp;
}
*/
public Point2D getGlyphPosition(int glyphIndex)
{
- return glyphTransforms[ glyphIndex ].transform( new Point2D.Double(0, 0),
- null );
+ return new Point2D.Float(glyphPositions[glyphIndex*2],
+ glyphPositions[glyphIndex*2 + 1]);
}
/**
public float[] getGlyphPositions(int beginGlyphIndex, int numEntries,
float[] positionReturn)
{
- if( glyphPositions != null )
- return glyphPositions;
-
- float[] rval;
-
- if( positionReturn == null )
- rval = new float[2 * numEntries];
- else
- rval = positionReturn;
-
- for( int i = beginGlyphIndex; i < numEntries; i++ )
- {
- Point2D p = getGlyphPosition( i );
- rval[i * 2] = (float)p.getX();
- rval[i * 2 + 1] = (float)p.getY();
- }
-
- glyphPositions = rval;
- return rval;
+ if (positionReturn == null || positionReturn.length < (numEntries * 2))
+ positionReturn = new float[numEntries*2];
+
+ System.arraycopy(glyphPositions, beginGlyphIndex*2, positionReturn, 0,
+ numEntries*2);
+ return positionReturn;
}
/**
*/
public AffineTransform getGlyphTransform(int glyphIndex)
{
- return new AffineTransform( glyphTransforms[ glyphIndex ] );
+ return glyphTransforms[glyphIndex];
}
/**
return logicalBounds;
Rectangle2D rect = (Rectangle2D)getGlyphLogicalBounds( 0 );
+ AffineTransform tx = new AffineTransform();
for( int i = 1; i < nGlyphs; i++ )
{
- Rectangle2D r2 = (Rectangle2D)getGlyphLogicalBounds( i );
- rect = rect.createUnion( r2 );
+ Rectangle2D r2 = (Rectangle2D)getGlyphLogicalBounds( i );
+
+ rect = rect.createUnion( r2 );
}
logicalBounds = rect;
public Shape getOutline()
{
GeneralPath path = new GeneralPath();
+ AffineTransform tx = new AffineTransform();
for( int i = 0; i < getNumGlyphs(); i++ )
- path.append( getGlyphOutline( i ), false );
+ {
+ Shape outline = getGlyphOutline(i);
+ tx.setToTranslation(glyphPositions[i*2], glyphPositions[i*2 +1]);
+ outline = tx.createTransformedShape(outline);
+ path.append(outline, false);
+ }
return path;
}
*/
public void setGlyphPosition(int glyphIndex, Point2D newPos)
{
- // FIXME: Scaling, etc.?
- glyphTransforms[ glyphIndex ].setToTranslation( newPos.getX(),
- newPos.getY() );
+ glyphPositions[glyphIndex*2] = (float)(newPos.getX());
+ glyphPositions[glyphIndex*2 + 1] = (float)(newPos.getY());
logicalBounds = null;
- glyphPositions = null;
}
/**
*/
public void setGlyphTransform(int glyphIndex, AffineTransform newTX)
{
- glyphTransforms[ glyphIndex ].setTransform( newTX );
logicalBounds = null;
- glyphPositions = null;
+ glyphTransforms[glyphIndex] = newTX;
}
}