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: import ;
51: import ;
52: import ;
53:
54: public class FreetypeGlyphVector extends GlyphVector
55: {
56:
59: private Font font;
60: private GdkFontPeer peer;
61:
62: private Rectangle2D logicalBounds;
63:
64: private float[] glyphPositions;
65:
68: private String s;
69:
70:
73: private FontRenderContext frc;
74:
75:
78: private int nGlyphs;
79:
80:
83: private int[] glyphCodes;
84:
85:
88: private long[] fontSet = null;
89:
90:
96: private AffineTransform[] glyphTransforms;
97:
98: private GlyphMetrics[] metricsCache;
99:
100: private native void dispose(long[] fonts);
101:
102:
111: private native long getNativeFontPointer(int n);
112:
113:
116: public FreetypeGlyphVector(Font f, String s, FontRenderContext frc)
117: {
118: this(f, s.toCharArray(), 0, s.length(), frc, Font.LAYOUT_LEFT_TO_RIGHT);
119: }
120:
121:
124: public FreetypeGlyphVector(Font f, char[] chars, int start, int len,
125: FontRenderContext frc, int flags)
126: {
127: this.s = new String(chars, start, len);
128:
129: this.font = f;
130: this.frc = frc;
131: if( !(font.getPeer() instanceof GdkFontPeer ) )
132: throw new IllegalArgumentException("Not a valid font.");
133: peer = (GdkFontPeer)font.getPeer();
134:
135: getGlyphs();
136: if( flags == Font.LAYOUT_RIGHT_TO_LEFT )
137: {
138:
139: int[] temp = new int[ nGlyphs ];
140: for(int i = 0; i < nGlyphs; i++)
141: temp[i] = glyphCodes[nGlyphs - i - 1];
142: glyphCodes = temp;
143: }
144: performDefaultLayout();
145: }
146:
147:
150: public FreetypeGlyphVector(Font f, int[] codes, FontRenderContext frc)
151: {
152: this.font = f;
153: this.frc = frc;
154: if( !(font.getPeer() instanceof GdkFontPeer ) )
155: throw new IllegalArgumentException("Not a valid font.");
156: peer = (GdkFontPeer)font.getPeer();
157:
158: glyphCodes = new int[ codes.length ];
159: System.arraycopy(codes, 0, glyphCodes, 0, codes.length);
160: nGlyphs = glyphCodes.length;
161:
162: if (fontSet == null)
163: {
164: fontSet = new long[nGlyphs];
165: Arrays.fill(fontSet, getNativeFontPointer(nGlyphs));
166: }
167:
168: performDefaultLayout();
169: }
170:
171:
174: private FreetypeGlyphVector( FreetypeGlyphVector gv )
175: {
176: font = gv.font;
177: peer = gv.peer;
178: frc = gv.frc;
179: s = gv.s;
180: nGlyphs = gv.nGlyphs;
181: logicalBounds = gv.logicalBounds.getBounds2D();
182:
183: if( gv.metricsCache != null )
184: {
185: metricsCache = new GlyphMetrics[ nGlyphs ];
186: System.arraycopy(gv.metricsCache, 0, metricsCache, 0, nGlyphs);
187: }
188:
189: glyphCodes = new int[ nGlyphs ];
190: fontSet = new long[nGlyphs];
191: glyphPositions = new float[(nGlyphs + 1) * 2];
192: glyphTransforms = new AffineTransform[ nGlyphs ];
193: Arrays.fill(glyphTransforms, null);
194:
195: for(int i = 0; i < nGlyphs; i++ )
196: {
197: if (gv.glyphTransforms[i] != null)
198: glyphTransforms[ i ] = new AffineTransform(gv.glyphTransforms[i]);
199: glyphCodes[i] = gv.glyphCodes[ i ];
200: }
201: System.arraycopy(gv.glyphPositions, 0, glyphPositions, 0,
202: glyphPositions.length);
203: System.arraycopy(gv.glyphCodes, 0, glyphCodes, 0, nGlyphs);
204: System.arraycopy(gv.fontSet, 0, fontSet, 0, nGlyphs);
205: }
206:
207: public void finalize()
208: {
209: dispose(fontSet);
210: }
211:
212:
215: private void getGlyphs()
216: {
217: nGlyphs = s.codePointCount( 0, s.length() );
218: glyphCodes = new int[ nGlyphs ];
219: fontSet = new long[ nGlyphs ];
220: int[] codePoints = new int[ nGlyphs ];
221: int stringIndex = 0;
222:
223: for(int i = 0; i < nGlyphs; i++)
224: {
225: codePoints[i] = s.codePointAt( stringIndex );
226:
227: if( codePoints[i] != (int)s.charAt( stringIndex ) )
228: stringIndex ++;
229: stringIndex ++;
230:
231: if (Character.isISOControl(codePoints[i]))
232: {
233:
234:
235: codePoints[i] = 8202;
236: }
237: }
238:
239: getGlyphs( codePoints, glyphCodes, fontSet );
240: }
241:
242:
245: public native void getGlyphs(int[] codepoints, int[] glyphs, long[] fonts);
246:
247:
250: private native Point2D getKerning(int leftGlyph, int rightGlyph, long font);
251:
252: private native double[] getMetricsNative(int glyphCode, long font);
253:
254: private native GeneralPath getGlyphOutlineNative(int glyphIndex, long font);
255:
256:
257: public Object clone()
258: {
259: return new FreetypeGlyphVector( this );
260: }
261:
262:
265: public boolean equals(GlyphVector gv)
266: {
267: if( ! (gv instanceof FreetypeGlyphVector) )
268: return false;
269:
270: return (((FreetypeGlyphVector)gv).font.equals(font) &&
271: ((FreetypeGlyphVector)gv).frc.equals(frc)
272: && ((FreetypeGlyphVector)gv).s.equals(s));
273: }
274:
275:
278: public Font getFont()
279: {
280: return font;
281: }
282:
283:
286: public FontRenderContext getFontRenderContext()
287: {
288: return frc;
289: }
290:
291:
294: public void performDefaultLayout()
295: {
296: logicalBounds = null;
297: glyphTransforms = new AffineTransform[nGlyphs];
298: Arrays.fill(glyphTransforms, null);
299: glyphPositions = new float[(nGlyphs + 1) * 2];
300:
301: GlyphMetrics gm = null;
302: float x = 0;
303: float y = 0;
304: for(int i = 0; i < nGlyphs; i++)
305: {
306: gm = getGlyphMetrics( i );
307: glyphPositions[i*2] = x;
308: glyphPositions[i*2 + 1] = y;
309:
310: x += gm.getAdvanceX();
311: y += gm.getAdvanceY();
312:
313:
314:
315: if (i != nGlyphs-1 && fontSet[i] == fontSet[i+1])
316: {
317: Point2D p = getKerning(glyphCodes[i], glyphCodes[i + 1], fontSet[i]);
318: x += p.getX();
319: y += p.getY();
320: }
321: }
322: glyphPositions[nGlyphs * 2] = x;
323: glyphPositions[nGlyphs * 2 + 1] = y;
324:
325:
326: TransformAttribute ta;
327: ta = (TransformAttribute)font.getAttributes().get(TextAttribute.TRANSFORM);
328: if (ta != null)
329: {
330: AffineTransform tx = ta.getTransform();
331:
332:
333: tx.transform(glyphPositions, 0, glyphPositions, 0,
334: glyphPositions.length / 2);
335:
336:
337: double[] matrix = new double[4];
338: tx.getMatrix(matrix);
339: AffineTransform deltaTx = new AffineTransform(matrix);
340: if (!deltaTx.isIdentity())
341: Arrays.fill(glyphTransforms, deltaTx);
342: }
343: }
344:
345:
348: public int getGlyphCode(int glyphIndex)
349: {
350: return glyphCodes[ glyphIndex ];
351: }
352:
353:
356: public int[] getGlyphCodes(int beginGlyphIndex, int numEntries,
357: int[] codeReturn)
358: {
359: int[] rval;
360:
361: if( codeReturn == null || codeReturn.length < numEntries)
362: rval = new int[ numEntries ];
363: else
364: rval = codeReturn;
365:
366: System.arraycopy(glyphCodes, beginGlyphIndex, rval, 0, numEntries);
367:
368: return rval;
369: }
370:
371:
376: protected long[] getGlyphFonts(int beginGlyphIndex, int numEntries,
377: long[] codeReturn)
378: {
379: long[] rval;
380:
381: if( codeReturn == null || codeReturn.length < numEntries)
382: rval = new long[ numEntries ];
383: else
384: rval = codeReturn;
385:
386: System.arraycopy(fontSet, beginGlyphIndex, rval, 0, numEntries);
387:
388: return rval;
389: }
390:
391: public Shape getGlyphLogicalBounds(int glyphIndex)
392: {
393: GlyphMetrics gm = getGlyphMetrics( glyphIndex );
394: if( gm == null )
395: return null;
396: Rectangle2D r = gm.getBounds2D();
397: Point2D p = getGlyphPosition( glyphIndex );
398:
399: double[] bounds = new double[] {p.getX() + r.getX() - gm.getLSB(),
400: p.getY() + r.getY(),
401: p.getX() + r.getX() - gm.getLSB() + gm.getAdvanceX(),
402: p.getY() + r.getY() + r.getHeight()};
403:
404: if (glyphTransforms[glyphIndex] != null)
405: glyphTransforms[glyphIndex].transform(bounds, 0, bounds, 0, 2);
406:
407: return new Rectangle2D.Double(bounds[0], bounds[1], bounds[2] - bounds[0],
408: bounds[3] - bounds[1]);
409: }
410:
411:
415: public void setupGlyphMetrics()
416: {
417: metricsCache = new GlyphMetrics[ nGlyphs ];
418:
419: for(int i = 0; i < nGlyphs; i++)
420: {
421: GlyphMetrics gm = (GlyphMetrics)peer.getGlyphMetrics(glyphCodes[i]);
422: if( gm == null )
423: {
424: double[] val = getMetricsNative(glyphCodes[i], fontSet[i]);
425: if( val == null )
426: gm = null;
427: else
428: {
429: gm = new GlyphMetrics(true,
430: (float)val[1],
431: (float)val[2],
432: new Rectangle2D.Double(val[3], val[4],
433: val[5], val[6] ),
434: GlyphMetrics.STANDARD );
435: peer.putGlyphMetrics( glyphCodes[ i ], gm );
436: }
437: }
438: metricsCache[ i ] = gm;
439: }
440: }
441:
442:
445: public GlyphMetrics getGlyphMetrics(int glyphIndex)
446: {
447: if( metricsCache == null )
448: setupGlyphMetrics();
449:
450: return metricsCache[ glyphIndex ];
451: }
452:
453:
459: public Shape getGlyphOutline(int glyphIndex)
460: {
461: GeneralPath gp = getGlyphOutlineNative(glyphCodes[glyphIndex],
462: fontSet[glyphIndex]);
463:
464: AffineTransform tx = AffineTransform.getTranslateInstance(glyphPositions[glyphIndex*2],
465: glyphPositions[glyphIndex*2+1]);
466: if (glyphTransforms[glyphIndex] != null)
467: tx.concatenate( glyphTransforms[glyphIndex]);
468:
469: gp.transform(tx);
470: return gp;
471: }
472:
473:
476: public Point2D getGlyphPosition(int glyphIndex)
477: {
478: return new Point2D.Float(glyphPositions[glyphIndex*2],
479: glyphPositions[glyphIndex*2 + 1]);
480: }
481:
482:
485: public float[] getGlyphPositions(int beginGlyphIndex, int numEntries,
486: float[] positionReturn)
487: {
488: if (positionReturn == null || positionReturn.length < (numEntries * 2))
489: positionReturn = new float[numEntries*2];
490:
491: System.arraycopy(glyphPositions, beginGlyphIndex*2, positionReturn, 0,
492: numEntries*2);
493: return positionReturn;
494: }
495:
496:
499: public AffineTransform getGlyphTransform(int glyphIndex)
500: {
501: return glyphTransforms[glyphIndex];
502: }
503:
504:
507: protected boolean hasTransforms()
508: {
509: for (int i = 0; i < glyphTransforms.length; i++)
510: if (glyphTransforms[i] != null)
511: return true;
512:
513: return false;
514: }
515:
516:
520: public Shape getGlyphVisualBounds(int glyphIndex)
521: {
522: return getGlyphOutline( glyphIndex ).getBounds2D();
523: }
524:
525:
528: public Rectangle2D getLogicalBounds()
529: {
530: if( nGlyphs == 0 )
531: return new Rectangle2D.Double(0, 0, 0, 0);
532: if( logicalBounds != null )
533: return logicalBounds;
534:
535: Rectangle2D rect = (Rectangle2D)getGlyphLogicalBounds( 0 );
536: for( int i = 1; i < nGlyphs; i++ )
537: {
538: Rectangle2D r2 = (Rectangle2D)getGlyphLogicalBounds( i );
539:
540: rect = rect.createUnion( r2 );
541: }
542:
543: logicalBounds = rect;
544: return rect;
545: }
546:
547:
550: public int getNumGlyphs()
551: {
552: return glyphCodes.length;
553: }
554:
555:
558: public Shape getOutline()
559: {
560: GeneralPath path = new GeneralPath();
561: for( int i = 0; i < getNumGlyphs(); i++ )
562: path.append(getGlyphOutline(i), false);
563: return path;
564: }
565:
566:
572: public GlyphJustificationInfo getGlyphJustificationInfo(int glyphIndex)
573: {
574: return null;
575: }
576:
577:
580: public Shape getOutline(float x, float y)
581: {
582: AffineTransform tx = AffineTransform.getTranslateInstance( x, y );
583: GeneralPath gp = (GeneralPath)getOutline();
584: gp.transform( tx );
585: return gp;
586: }
587:
588:
592: public Rectangle2D getVisualBounds()
593: {
594: return getOutline().getBounds2D();
595: }
596:
597:
600: public void setGlyphPosition(int glyphIndex, Point2D newPos)
601: {
602: glyphPositions[glyphIndex*2] = (float)(newPos.getX());
603: glyphPositions[glyphIndex*2 + 1] = (float)(newPos.getY());
604: logicalBounds = null;
605: }
606:
607:
610: public void setGlyphTransform(int glyphIndex, AffineTransform newTX)
611: {
612:
613:
614: if (newTX != null && newTX.isIdentity())
615: newTX = null;
616:
617:
618: if (glyphTransforms[glyphIndex] == null && newTX == null)
619: return;
620:
621: if (newTX != null && newTX.equals(glyphTransforms[glyphIndex]))
622: return;
623:
624:
625: logicalBounds = null;
626: glyphTransforms[glyphIndex] = newTX;
627: }
628: }