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 void getKerning(int leftGlyph, int rightGlyph, long font,
251: float[] p);
252:
253: private native double[] getMetricsNative(int glyphCode, long font);
254:
255: private native GeneralPath getGlyphOutlineNative(int glyphIndex, long font);
256:
257:
258: public Object clone()
259: {
260: return new FreetypeGlyphVector( this );
261: }
262:
263:
266: public boolean equals(GlyphVector gv)
267: {
268: if( ! (gv instanceof FreetypeGlyphVector) )
269: return false;
270:
271: return (((FreetypeGlyphVector)gv).font.equals(font) &&
272: ((FreetypeGlyphVector)gv).frc.equals(frc)
273: && ((FreetypeGlyphVector)gv).s.equals(s));
274: }
275:
276:
279: public Font getFont()
280: {
281: return font;
282: }
283:
284:
287: public FontRenderContext getFontRenderContext()
288: {
289: return frc;
290: }
291:
292:
295: public void performDefaultLayout()
296: {
297: logicalBounds = null;
298: glyphTransforms = new AffineTransform[nGlyphs];
299: Arrays.fill(glyphTransforms, null);
300: glyphPositions = new float[(nGlyphs + 1) * 2];
301:
302: GlyphMetrics gm = null;
303: float x = 0;
304: float y = 0;
305: float[] p = {0.0f, 0.0f};
306: for(int i = 0; i < nGlyphs; i++)
307: {
308: gm = getGlyphMetrics( i );
309: glyphPositions[i*2] = x;
310: glyphPositions[i*2 + 1] = y;
311:
312: x += gm.getAdvanceX();
313: y += gm.getAdvanceY();
314:
315:
316:
317: if (i != nGlyphs-1 && fontSet[i] == fontSet[i+1])
318: {
319: getKerning(glyphCodes[i], glyphCodes[i + 1], fontSet[i], p);
320: x += p[0];
321: y += p[1];
322: }
323: }
324: glyphPositions[nGlyphs * 2] = x;
325: glyphPositions[nGlyphs * 2 + 1] = y;
326:
327:
328: TransformAttribute ta;
329: ta = (TransformAttribute)font.getAttributes().get(TextAttribute.TRANSFORM);
330: if (ta != null)
331: {
332: AffineTransform tx = ta.getTransform();
333:
334:
335: tx.transform(glyphPositions, 0, glyphPositions, 0,
336: glyphPositions.length / 2);
337:
338:
339: double[] matrix = new double[4];
340: tx.getMatrix(matrix);
341: AffineTransform deltaTx = new AffineTransform(matrix);
342: if (!deltaTx.isIdentity())
343: Arrays.fill(glyphTransforms, deltaTx);
344: }
345: }
346:
347:
350: public int getGlyphCode(int glyphIndex)
351: {
352: return glyphCodes[ glyphIndex ];
353: }
354:
355:
358: public int[] getGlyphCodes(int beginGlyphIndex, int numEntries,
359: int[] codeReturn)
360: {
361: int[] rval;
362:
363: if( codeReturn == null || codeReturn.length < numEntries)
364: rval = new int[ numEntries ];
365: else
366: rval = codeReturn;
367:
368: System.arraycopy(glyphCodes, beginGlyphIndex, rval, 0, numEntries);
369:
370: return rval;
371: }
372:
373:
378: protected long[] getGlyphFonts(int beginGlyphIndex, int numEntries,
379: long[] codeReturn)
380: {
381: long[] rval;
382:
383: if( codeReturn == null || codeReturn.length < numEntries)
384: rval = new long[ numEntries ];
385: else
386: rval = codeReturn;
387:
388: System.arraycopy(fontSet, beginGlyphIndex, rval, 0, numEntries);
389:
390: return rval;
391: }
392:
393: public Shape getGlyphLogicalBounds(int glyphIndex)
394: {
395: GlyphMetrics gm = getGlyphMetrics( glyphIndex );
396: if( gm == null )
397: return null;
398: Rectangle2D r = gm.getBounds2D();
399: Point2D p = getGlyphPosition( glyphIndex );
400:
401: double[] bounds = new double[] {p.getX() + r.getX() - gm.getLSB(),
402: p.getY() + r.getY(),
403: p.getX() + r.getX() - gm.getLSB() + gm.getAdvanceX(),
404: p.getY() + r.getY() + r.getHeight()};
405:
406: if (glyphTransforms[glyphIndex] != null)
407: glyphTransforms[glyphIndex].transform(bounds, 0, bounds, 0, 2);
408:
409: return new Rectangle2D.Double(bounds[0], bounds[1], bounds[2] - bounds[0],
410: bounds[3] - bounds[1]);
411: }
412:
413:
417: public void setupGlyphMetrics()
418: {
419: metricsCache = new GlyphMetrics[ nGlyphs ];
420:
421: for(int i = 0; i < nGlyphs; i++)
422: {
423: GlyphMetrics gm = (GlyphMetrics)peer.getGlyphMetrics(glyphCodes[i]);
424: if( gm == null )
425: {
426: double[] val = getMetricsNative(glyphCodes[i], fontSet[i]);
427: if( val == null )
428: gm = null;
429: else
430: {
431: gm = new GlyphMetrics(true,
432: (float)val[1],
433: (float)val[2],
434: new Rectangle2D.Double(val[3], val[4],
435: val[5], val[6] ),
436: GlyphMetrics.STANDARD );
437: peer.putGlyphMetrics( glyphCodes[ i ], gm );
438: }
439: }
440: metricsCache[ i ] = gm;
441: }
442: }
443:
444:
447: public GlyphMetrics getGlyphMetrics(int glyphIndex)
448: {
449: if( metricsCache == null )
450: setupGlyphMetrics();
451:
452: return metricsCache[ glyphIndex ];
453: }
454:
455:
461: public Shape getGlyphOutline(int glyphIndex)
462: {
463: GeneralPath gp = getGlyphOutlineNative(glyphCodes[glyphIndex],
464: fontSet[glyphIndex]);
465:
466: AffineTransform tx = AffineTransform.getTranslateInstance(glyphPositions[glyphIndex*2],
467: glyphPositions[glyphIndex*2+1]);
468: if (glyphTransforms[glyphIndex] != null)
469: tx.concatenate( glyphTransforms[glyphIndex]);
470:
471: gp.transform(tx);
472: return gp;
473: }
474:
475:
478: public Point2D getGlyphPosition(int glyphIndex)
479: {
480: return new Point2D.Float(glyphPositions[glyphIndex*2],
481: glyphPositions[glyphIndex*2 + 1]);
482: }
483:
484:
487: public float[] getGlyphPositions(int beginGlyphIndex, int numEntries,
488: float[] positionReturn)
489: {
490: if (positionReturn == null || positionReturn.length < (numEntries * 2))
491: positionReturn = new float[numEntries*2];
492:
493: System.arraycopy(glyphPositions, beginGlyphIndex*2, positionReturn, 0,
494: numEntries*2);
495: return positionReturn;
496: }
497:
498:
501: public AffineTransform getGlyphTransform(int glyphIndex)
502: {
503: return glyphTransforms[glyphIndex];
504: }
505:
506:
509: protected boolean hasTransforms()
510: {
511: for (int i = 0; i < glyphTransforms.length; i++)
512: if (glyphTransforms[i] != null)
513: return true;
514:
515: return false;
516: }
517:
518:
522: public Shape getGlyphVisualBounds(int glyphIndex)
523: {
524: return getGlyphOutline( glyphIndex ).getBounds2D();
525: }
526:
527:
530: public Rectangle2D getLogicalBounds()
531: {
532: if( nGlyphs == 0 )
533: return new Rectangle2D.Double(0, 0, 0, 0);
534: if( logicalBounds != null )
535: return logicalBounds;
536:
537: Rectangle2D rect = (Rectangle2D)getGlyphLogicalBounds( 0 );
538: for( int i = 1; i < nGlyphs; i++ )
539: {
540: Rectangle2D r2 = (Rectangle2D)getGlyphLogicalBounds( i );
541:
542: rect = rect.createUnion( r2 );
543: }
544:
545: logicalBounds = rect;
546: return rect;
547: }
548:
549:
552: public int getNumGlyphs()
553: {
554: return glyphCodes.length;
555: }
556:
557:
560: public Shape getOutline()
561: {
562: GeneralPath path = new GeneralPath();
563: for( int i = 0; i < getNumGlyphs(); i++ )
564: path.append(getGlyphOutline(i), false);
565: return path;
566: }
567:
568:
574: public GlyphJustificationInfo getGlyphJustificationInfo(int glyphIndex)
575: {
576: return null;
577: }
578:
579:
582: public Shape getOutline(float x, float y)
583: {
584: AffineTransform tx = AffineTransform.getTranslateInstance( x, y );
585: GeneralPath gp = (GeneralPath)getOutline();
586: gp.transform( tx );
587: return gp;
588: }
589:
590:
594: public Rectangle2D getVisualBounds()
595: {
596: return getOutline().getBounds2D();
597: }
598:
599:
602: public void setGlyphPosition(int glyphIndex, Point2D newPos)
603: {
604: glyphPositions[glyphIndex*2] = (float)(newPos.getX());
605: glyphPositions[glyphIndex*2 + 1] = (float)(newPos.getY());
606: logicalBounds = null;
607: }
608:
609:
612: public void setGlyphTransform(int glyphIndex, AffineTransform newTX)
613: {
614:
615:
616: if (newTX != null && newTX.isIdentity())
617: newTX = null;
618:
619:
620: if (glyphTransforms[glyphIndex] == null && newTX == null)
621: return;
622:
623: if (newTX != null && newTX.equals(glyphTransforms[glyphIndex]))
624: return;
625:
626:
627: logicalBounds = null;
628: glyphTransforms[glyphIndex] = newTX;
629: }
630: }