1 /* FreetypeGlyphVector.java
2 Copyright (C) 2006 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
38 package gnu.java.awt.peer.gtk;
41 import java.awt.Shape;
42 import java.awt.font.FontRenderContext;
43 import java.awt.font.GlyphJustificationInfo;
44 import java.awt.font.GlyphMetrics;
45 import java.awt.font.GlyphVector;
46 import java.awt.geom.AffineTransform;
47 import java.awt.geom.GeneralPath;
48 import java.awt.geom.Point2D;
49 import java.awt.geom.Rectangle2D;
50 import java.util.Arrays;
52 public class FreetypeGlyphVector extends GlyphVector
55 * The associated font and its peer.
58 private GdkFontPeer peer; // ATTN: Accessed from native code.
60 private Rectangle2D logicalBounds;
62 private float[] glyphPositions;
64 * The string represented by this GlyphVector.
69 * The font render context
71 private FontRenderContext frc;
74 * The total # of glyphs.
81 private int[] glyphCodes;
84 * Glyph transforms. (de facto only the translation is used)
86 private AffineTransform[] glyphTransforms;
88 private GlyphMetrics[] metricsCache;
91 * Create a glyphvector from a given (Freetype) font and a String.
93 public FreetypeGlyphVector(Font f, String s, FontRenderContext frc)
95 this(f, s.toCharArray(), 0, s.length(), frc, Font.LAYOUT_LEFT_TO_RIGHT);
99 * Create a glyphvector from a given (Freetype) font and a String.
101 public FreetypeGlyphVector(Font f, char[] chars, int start, int len,
102 FontRenderContext frc, int flags)
104 this.s = new String(chars, start, len);
108 if( !(font.getPeer() instanceof GdkFontPeer ) )
109 throw new IllegalArgumentException("Not a valid font.");
110 peer = (GdkFontPeer)font.getPeer();
113 if( flags == Font.LAYOUT_RIGHT_TO_LEFT )
115 // reverse the glyph ordering.
116 int[] temp = new int[ nGlyphs ];
117 for(int i = 0; i < nGlyphs; i++)
118 temp[ i ] = glyphCodes[ nGlyphs - i - 1];
121 performDefaultLayout();
125 * Create a glyphvector from a given set of glyph codes.
127 public FreetypeGlyphVector(Font f, int[] codes, FontRenderContext frc)
131 if( !(font.getPeer() instanceof GdkFontPeer ) )
132 throw new IllegalArgumentException("Not a valid font.");
133 peer = (GdkFontPeer)font.getPeer();
135 glyphCodes = new int[ codes.length ];
136 System.arraycopy(codes, 0, glyphCodes, 0, codes.length);
137 nGlyphs = glyphCodes.length;
138 performDefaultLayout();
142 * Cloning constructor
144 private FreetypeGlyphVector( FreetypeGlyphVector gv )
150 nGlyphs = gv.nGlyphs;
151 logicalBounds = gv.logicalBounds.getBounds2D();
153 if( gv.metricsCache != null )
155 metricsCache = new GlyphMetrics[ nGlyphs ];
156 System.arraycopy(gv.metricsCache, 0, metricsCache, 0, nGlyphs);
159 glyphCodes = new int[ nGlyphs ];
160 glyphPositions = new float[(nGlyphs + 1) * 2];
161 glyphTransforms = new AffineTransform[ nGlyphs ];
162 for(int i = 0; i < nGlyphs; i++ )
164 glyphTransforms[ i ] = new AffineTransform( gv.glyphTransforms[ i ] );
165 glyphCodes[i] = gv.glyphCodes[ i ];
167 System.arraycopy(gv.glyphPositions, 0, glyphPositions, 0,
168 glyphPositions.length);
172 * Create the array of glyph codes.
174 private void getGlyphs()
176 nGlyphs = s.codePointCount( 0, s.length() );
177 glyphCodes = new int[ nGlyphs ];
178 int[] codePoints = new int[ nGlyphs ];
181 for(int i = 0; i < nGlyphs; i++)
183 codePoints[i] = s.codePointAt( stringIndex );
184 // UTF32 surrogate handling
185 if( codePoints[i] != (int)s.charAt( stringIndex ) )
189 if (Character.isISOControl(codePoints[i]))
191 // Replace with 'hair space'. Should better be 'zero-width space'
192 // but that doesn't seem to be supported by default font.
193 codePoints[i] = 8202;
197 glyphCodes = getGlyphs( codePoints );
201 * Returns the glyph code within the font for a given character
203 public native int[] getGlyphs(int[] codepoints);
206 * Returns the kerning of a glyph pair
208 private native Point2D getKerning(int leftGlyph, int rightGlyph);
210 private native double[] getMetricsNative( int glyphCode );
212 private native GeneralPath getGlyphOutlineNative(int glyphIndex);
215 public Object clone()
217 return new FreetypeGlyphVector( this );
221 * Duh, compares two instances.
223 public boolean equals(GlyphVector gv)
225 if( ! (gv instanceof FreetypeGlyphVector) )
228 return (((FreetypeGlyphVector)gv).font.equals(font) &&
229 ((FreetypeGlyphVector)gv).frc.equals(frc)
230 && ((FreetypeGlyphVector)gv).s.equals(s));
234 * Returns the associated Font
236 public Font getFont()
242 * Returns the associated FontRenderContext
244 public FontRenderContext getFontRenderContext()
252 public void performDefaultLayout()
254 logicalBounds = null; // invalidate caches.
255 glyphTransforms = new AffineTransform[nGlyphs];
256 Arrays.fill(glyphTransforms, null);
257 glyphPositions = new float[(nGlyphs + 1) * 2];
259 GlyphMetrics gm = null;
262 for(int i = 0; i < nGlyphs; i++)
264 gm = getGlyphMetrics( i );
265 glyphPositions[i*2] = x;
266 glyphPositions[i*2 + 1] = y;
268 x += gm.getAdvanceX();
269 y += gm.getAdvanceY();
273 Point2D p = getKerning(glyphCodes[i], glyphCodes[i + 1]);
278 glyphPositions[nGlyphs * 2] = x;
279 glyphPositions[nGlyphs * 2 + 1] = y;
283 * Returns the code of the glyph at glyphIndex;
285 public int getGlyphCode(int glyphIndex)
287 return glyphCodes[ glyphIndex ];
291 * Returns multiple glyphcodes.
293 public int[] getGlyphCodes(int beginGlyphIndex, int numEntries,
298 if( codeReturn == null || codeReturn.length < numEntries)
299 rval = new int[ numEntries ];
303 System.arraycopy(glyphCodes, beginGlyphIndex, rval, 0, numEntries);
308 public Shape getGlyphLogicalBounds(int glyphIndex)
310 GlyphMetrics gm = getGlyphMetrics( glyphIndex );
313 Rectangle2D r = gm.getBounds2D();
314 Point2D p = getGlyphPosition( glyphIndex );
316 double[] bounds = new double[] {p.getX() + r.getX() - gm.getLSB(),
318 p.getX() + r.getX() - gm.getLSB() + gm.getAdvanceX(),
319 p.getY() + r.getY() + r.getHeight()};
321 if (glyphTransforms[glyphIndex] != null)
322 glyphTransforms[glyphIndex].transform(bounds, 0, bounds, 0, 4);
324 return new Rectangle2D.Double(bounds[0], bounds[1], bounds[2] - bounds[0],
325 bounds[3] - bounds[1]);
329 * FIXME: Not all glyph types are supported.
330 * (The JDK doesn't really seem to do so either)
332 public void setupGlyphMetrics()
334 metricsCache = new GlyphMetrics[ nGlyphs ];
336 for(int i = 0; i < nGlyphs; i++)
338 GlyphMetrics gm = (GlyphMetrics)
339 peer.getGlyphMetrics( glyphCodes[ i ] );
342 double[] val = getMetricsNative( glyphCodes[ i ] );
347 gm = new GlyphMetrics( true,
350 new Rectangle2D.Double
353 GlyphMetrics.STANDARD );
354 peer.putGlyphMetrics( glyphCodes[ i ], gm );
357 metricsCache[ i ] = gm;
362 * Returns the metrics of a single glyph.
364 public GlyphMetrics getGlyphMetrics(int glyphIndex)
366 if( metricsCache == null )
369 return metricsCache[ glyphIndex ];
373 * Returns the outline of a single glyph.
375 public Shape getGlyphOutline(int glyphIndex)
377 GeneralPath gp = getGlyphOutlineNative( glyphCodes[ glyphIndex ] );
378 if (glyphTransforms[glyphIndex] != null)
379 gp.transform( glyphTransforms[glyphIndex]);
385 * Returns the position of a single glyph.
387 public Point2D getGlyphPosition(int glyphIndex)
389 return new Point2D.Float(glyphPositions[glyphIndex*2],
390 glyphPositions[glyphIndex*2 + 1]);
394 * Returns the positions of multiple glyphs.
396 public float[] getGlyphPositions(int beginGlyphIndex, int numEntries,
397 float[] positionReturn)
399 if (positionReturn == null || positionReturn.length < (numEntries * 2))
400 positionReturn = new float[numEntries*2];
402 System.arraycopy(glyphPositions, beginGlyphIndex*2, positionReturn, 0,
404 return positionReturn;
408 * Returns the transform of a glyph.
410 public AffineTransform getGlyphTransform(int glyphIndex)
412 return glyphTransforms[glyphIndex];
416 * Returns the visual bounds of a glyph
417 * May be off by a pixel or two due to hinting/rasterization.
419 public Shape getGlyphVisualBounds(int glyphIndex)
421 return getGlyphOutline( glyphIndex ).getBounds2D();
425 * Return the logical bounds of the whole thing.
427 public Rectangle2D getLogicalBounds()
430 return new Rectangle2D.Double(0, 0, 0, 0);
431 if( logicalBounds != null )
432 return logicalBounds;
434 Rectangle2D rect = (Rectangle2D)getGlyphLogicalBounds( 0 );
435 AffineTransform tx = new AffineTransform();
436 for( int i = 1; i < nGlyphs; i++ )
438 Rectangle2D r2 = (Rectangle2D)getGlyphLogicalBounds( i );
440 rect = rect.createUnion( r2 );
443 logicalBounds = rect;
448 * Returns the number of glyphs.
450 public int getNumGlyphs()
452 return glyphCodes.length;
456 * Returns the outline of the entire GlyphVector.
458 public Shape getOutline()
460 GeneralPath path = new GeneralPath();
461 AffineTransform tx = new AffineTransform();
462 for( int i = 0; i < getNumGlyphs(); i++ )
464 Shape outline = getGlyphOutline(i);
465 tx.setToTranslation(glyphPositions[i*2], glyphPositions[i*2 +1]);
466 outline = tx.createTransformedShape(outline);
467 path.append(outline, false);
474 * FreeType does not currently have an API for the JSTF table. We should
475 * probably get the table ourselves from FT and pass it to some parser
476 * which the native font peers will need.
478 public GlyphJustificationInfo getGlyphJustificationInfo(int glyphIndex)
484 * Returns the outline of the entire vector, drawn at (x,y).
486 public Shape getOutline(float x, float y)
488 AffineTransform tx = AffineTransform.getTranslateInstance( x, y );
489 GeneralPath gp = (GeneralPath)getOutline();
495 * Returns the visual bounds of the entire GlyphVector.
496 * May be off by a pixel or two due to hinting/rasterization.
498 public Rectangle2D getVisualBounds()
500 return getOutline().getBounds2D();
504 * Sets the position of a glyph.
506 public void setGlyphPosition(int glyphIndex, Point2D newPos)
508 glyphPositions[glyphIndex*2] = (float)(newPos.getX());
509 glyphPositions[glyphIndex*2 + 1] = (float)(newPos.getY());
510 logicalBounds = null;
514 * Sets the transform of a single glyph.
516 public void setGlyphTransform(int glyphIndex, AffineTransform newTX)
518 logicalBounds = null;
519 glyphTransforms[glyphIndex] = newTX;