1:
37:
38: package ;
39:
40: import ;
41: import ;
42: import ;
43: import ;
44: import ;
45: import ;
46: import ;
47: import ;
48: import ;
49: import ;
50:
51: public class FreetypeGlyphVector extends GlyphVector
52: {
53:
56: private Font font;
57: private GdkFontPeer peer;
58:
59: private Rectangle2D logicalBounds;
60:
61: private float[] glyphPositions;
62:
65: private String s;
66:
67:
70: private FontRenderContext frc;
71:
72:
75: private int nGlyphs;
76:
77:
80: private int[] glyphCodes;
81:
82:
85: private AffineTransform[] glyphTransforms;
86:
87: private GlyphMetrics[] metricsCache;
88:
89:
92: public FreetypeGlyphVector(Font f, String s, FontRenderContext frc)
93: {
94: this(f, s, frc, Font.LAYOUT_LEFT_TO_RIGHT);
95: }
96:
97:
100: public FreetypeGlyphVector(Font f, String s, FontRenderContext frc,
101: int flags)
102: {
103: this.s = s;
104: this.font = f;
105: this.frc = frc;
106: if( !(font.getPeer() instanceof GdkFontPeer ) )
107: throw new IllegalArgumentException("Not a valid font.");
108: peer = (GdkFontPeer)font.getPeer();
109:
110: getGlyphs();
111: if( flags == Font.LAYOUT_RIGHT_TO_LEFT )
112: {
113:
114: int[] temp = new int[ nGlyphs ];
115: for(int i = 0; i < nGlyphs; i++)
116: temp[ i ] = glyphCodes[ nGlyphs - i - 1];
117: glyphCodes = temp;
118: }
119: performDefaultLayout();
120: }
121:
122:
125: public FreetypeGlyphVector(Font f, int[] codes, FontRenderContext frc)
126: {
127: this.font = f;
128: this.frc = frc;
129: if( !(font.getPeer() instanceof GdkFontPeer ) )
130: throw new IllegalArgumentException("Not a valid font.");
131: peer = (GdkFontPeer)font.getPeer();
132:
133: glyphCodes = new int[ codes.length ];
134: System.arraycopy(codes, 0, glyphCodes, 0, codes.length);
135: nGlyphs = glyphCodes.length;
136: performDefaultLayout();
137: }
138:
139:
142: private FreetypeGlyphVector( FreetypeGlyphVector gv )
143: {
144: font = gv.font;
145: peer = gv.peer;
146: frc = gv.frc;
147: s = gv.s;
148: nGlyphs = gv.nGlyphs;
149: logicalBounds = gv.logicalBounds.getBounds2D();
150:
151: if( gv.metricsCache != null )
152: {
153: metricsCache = new GlyphMetrics[ nGlyphs ];
154: System.arraycopy(gv.metricsCache, 0, metricsCache, 0, nGlyphs);
155: }
156:
157: glyphCodes = new int[ nGlyphs ];
158: glyphPositions = new float[ nGlyphs ];
159: glyphTransforms = new AffineTransform[ nGlyphs ];
160: for(int i = 0; i < nGlyphs; i++ )
161: {
162: glyphTransforms[ i ] = new AffineTransform( gv.glyphTransforms[ i ] );
163: glyphCodes[i] = gv.glyphCodes[ i ];
164: glyphPositions[i] = gv.glyphPositions[ i ];
165: }
166: }
167:
168:
171: private void getGlyphs()
172: {
173: nGlyphs = s.codePointCount( 0, s.length() );
174: glyphCodes = new int[ nGlyphs ];
175: int[] codePoints = new int[ nGlyphs ];
176: int stringIndex = 0;
177:
178: for(int i = 0; i < nGlyphs; i++)
179: {
180: codePoints[i] = s.codePointAt( stringIndex );
181:
182: if( codePoints[i] != (int)s.charAt( stringIndex ) )
183: stringIndex ++;
184: stringIndex ++;
185: }
186:
187: glyphCodes = getGlyphs( codePoints );
188: }
189:
190:
193: public native int[] getGlyphs(int[] codepoints);
194:
195:
198: private native Point2D getKerning(int leftGlyph, int rightGlyph);
199:
200: private native double[] getMetricsNative( int glyphCode );
201:
202: private native GeneralPath getGlyphOutlineNative(int glyphIndex);
203:
204:
205: public Object clone()
206: {
207: return new FreetypeGlyphVector( this );
208: }
209:
210:
213: public boolean equals(GlyphVector gv)
214: {
215: if( ! (gv instanceof FreetypeGlyphVector) )
216: return false;
217:
218: return (((FreetypeGlyphVector)gv).font.equals(font) &&
219: ((FreetypeGlyphVector)gv).frc.equals(frc)
220: && ((FreetypeGlyphVector)gv).s.equals(s));
221: }
222:
223:
226: public Font getFont()
227: {
228: return font;
229: }
230:
231:
234: public FontRenderContext getFontRenderContext()
235: {
236: return frc;
237: }
238:
239:
242: public void performDefaultLayout()
243: {
244: logicalBounds = null;
245: glyphPositions = null;
246:
247: glyphTransforms = new AffineTransform[ nGlyphs ];
248: double x = 0;
249:
250: for(int i = 0; i < nGlyphs; i++)
251: {
252: GlyphMetrics gm = getGlyphMetrics( i );
253: glyphTransforms[ i ] = AffineTransform.getTranslateInstance(x, 0);
254: x += gm.getAdvanceX();
255: if( i > 0 )
256: {
257: Point2D p = getKerning( glyphCodes[ i - 1 ], glyphCodes[ i ] );
258: x += p.getX();
259: }
260: }
261: }
262:
263:
266: public int getGlyphCode(int glyphIndex)
267: {
268: return glyphCodes[ glyphIndex ];
269: }
270:
271:
274: public int[] getGlyphCodes(int beginGlyphIndex, int numEntries,
275: int[] codeReturn)
276: {
277: int[] rval;
278:
279: if( codeReturn == null )
280: rval = new int[ numEntries ];
281: else
282: rval = codeReturn;
283:
284: System.arraycopy(glyphCodes, beginGlyphIndex, rval, 0, numEntries);
285:
286: return rval;
287: }
288:
289:
292: public Shape getGlyphLogicalBounds(int glyphIndex)
293: {
294: GlyphMetrics gm = getGlyphMetrics( glyphIndex );
295: if( gm == null )
296: return null;
297: Rectangle2D r = gm.getBounds2D();
298: Point2D p = getGlyphPosition( glyphIndex );
299: return new Rectangle2D.Double( p.getX() + r.getX() - gm.getLSB(),
300: p.getY() + r.getY(),
301: gm.getAdvanceX(),
302: r.getHeight() );
303: }
304:
305:
309: public void setupGlyphMetrics()
310: {
311: metricsCache = new GlyphMetrics[ nGlyphs ];
312:
313: for(int i = 0; i < nGlyphs; i++)
314: {
315: GlyphMetrics gm = (GlyphMetrics)
316: peer.getGlyphMetrics( glyphCodes[ i ] );
317: if( gm == null )
318: {
319: double[] val = getMetricsNative( glyphCodes[ i ] );
320: if( val == null )
321: gm = null;
322: else
323: {
324: gm = new GlyphMetrics( true,
325: (float)val[1],
326: (float)val[2],
327: new Rectangle2D.Double
328: ( val[3], val[4],
329: val[5], val[6] ),
330: GlyphMetrics.STANDARD );
331: peer.putGlyphMetrics( glyphCodes[ i ], gm );
332: }
333: }
334: metricsCache[ i ] = gm;
335: }
336: }
337:
338:
341: public GlyphMetrics getGlyphMetrics(int glyphIndex)
342: {
343: if( metricsCache == null )
344: setupGlyphMetrics();
345:
346: return metricsCache[ glyphIndex ];
347: }
348:
349:
352: public Shape getGlyphOutline(int glyphIndex)
353: {
354: GeneralPath gp = getGlyphOutlineNative( glyphCodes[ glyphIndex ] );
355: gp.transform( glyphTransforms[ glyphIndex ] );
356: return gp;
357: }
358:
359:
362: public Point2D getGlyphPosition(int glyphIndex)
363: {
364: return glyphTransforms[ glyphIndex ].transform( new Point2D.Double(0, 0),
365: null );
366: }
367:
368:
371: public float[] getGlyphPositions(int beginGlyphIndex, int numEntries,
372: float[] positionReturn)
373: {
374: if( glyphPositions != null )
375: return glyphPositions;
376:
377: float[] rval;
378:
379: if( positionReturn == null )
380: rval = new float[2 * numEntries];
381: else
382: rval = positionReturn;
383:
384: for( int i = beginGlyphIndex; i < numEntries; i++ )
385: {
386: Point2D p = getGlyphPosition( i );
387: rval[i * 2] = (float)p.getX();
388: rval[i * 2 + 1] = (float)p.getY();
389: }
390:
391: glyphPositions = rval;
392: return rval;
393: }
394:
395:
398: public AffineTransform getGlyphTransform(int glyphIndex)
399: {
400: return new AffineTransform( glyphTransforms[ glyphIndex ] );
401: }
402:
403:
407: public Shape getGlyphVisualBounds(int glyphIndex)
408: {
409: return getGlyphOutline( glyphIndex ).getBounds2D();
410: }
411:
412:
415: public Rectangle2D getLogicalBounds()
416: {
417: if( nGlyphs == 0 )
418: return new Rectangle2D.Double(0, 0, 0, 0);
419: if( logicalBounds != null )
420: return logicalBounds;
421:
422: Rectangle2D rect = (Rectangle2D)getGlyphLogicalBounds( 0 );
423: for( int i = 1; i < nGlyphs; i++ )
424: {
425: Rectangle2D r2 = (Rectangle2D)getGlyphLogicalBounds( i );
426: rect = rect.createUnion( r2 );
427: }
428:
429: logicalBounds = rect;
430: return rect;
431: }
432:
433:
436: public int getNumGlyphs()
437: {
438: return glyphCodes.length;
439: }
440:
441:
444: public Shape getOutline()
445: {
446: GeneralPath path = new GeneralPath();
447: for( int i = 0; i < getNumGlyphs(); i++ )
448: path.append( getGlyphOutline( i ), false );
449: return path;
450: }
451:
452:
458: public GlyphJustificationInfo getGlyphJustificationInfo(int glyphIndex)
459: {
460: return null;
461: }
462:
463:
466: public Shape getOutline(float x, float y)
467: {
468: AffineTransform tx = AffineTransform.getTranslateInstance( x, y );
469: GeneralPath gp = (GeneralPath)getOutline();
470: gp.transform( tx );
471: return gp;
472: }
473:
474:
478: public Rectangle2D getVisualBounds()
479: {
480: return getOutline().getBounds2D();
481: }
482:
483:
486: public void setGlyphPosition(int glyphIndex, Point2D newPos)
487: {
488:
489: glyphTransforms[ glyphIndex ].setToTranslation( newPos.getX(),
490: newPos.getY() );
491: logicalBounds = null;
492: glyphPositions = null;
493: }
494:
495:
498: public void setGlyphTransform(int glyphIndex, AffineTransform newTX)
499: {
500: glyphTransforms[ glyphIndex ].setTransform( newTX );
501: logicalBounds = null;
502: glyphPositions = null;
503: }
504: }