Source for gnu.java.awt.peer.gtk.FreetypeGlyphVector

   1: /* FreetypeGlyphVector.java
   2:    Copyright (C) 2006  Free Software Foundation, Inc.
   3: 
   4: This file is part of GNU Classpath.
   5: 
   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)
   9: any later version.
  10: 
  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.
  15: 
  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
  19: 02110-1301 USA.
  20: 
  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
  24: combination.
  25: 
  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. */
  37: 
  38: package gnu.java.awt.peer.gtk;
  39: 
  40: import java.awt.Font;
  41: import java.awt.Shape;
  42: import java.awt.geom.AffineTransform;
  43: import java.awt.geom.Point2D;
  44: import java.awt.geom.Rectangle2D;
  45: import java.awt.geom.GeneralPath;
  46: import java.awt.font.GlyphJustificationInfo;
  47: import java.awt.font.GlyphMetrics;
  48: import java.awt.font.GlyphVector;
  49: import java.awt.font.FontRenderContext;
  50: 
  51: public class FreetypeGlyphVector extends GlyphVector
  52: {
  53:   /**
  54:    * The associated font and its peer.
  55:    */
  56:   private Font font;
  57:   private GdkFontPeer peer; // ATTN: Accessed from native code.
  58: 
  59:   private Rectangle2D logicalBounds;
  60: 
  61:   private float[] glyphPositions;
  62:   /**
  63:    * The string represented by this GlyphVector.
  64:    */
  65:   private String s;
  66: 
  67:   /**
  68:    * The font render context
  69:    */
  70:   private FontRenderContext frc;
  71: 
  72:   /**
  73:    * The total # of glyphs.
  74:    */
  75:   private int nGlyphs;
  76: 
  77:   /**
  78:    * The glyph codes
  79:    */
  80:   private int[] glyphCodes;
  81: 
  82:   /**
  83:    * Glyph transforms. (de facto only the translation is used)
  84:    */
  85:   private AffineTransform[] glyphTransforms;
  86: 
  87:   private GlyphMetrics[] metricsCache;
  88: 
  89:   /**
  90:    * Create a glyphvector from a given (Freetype) font and a String.
  91:    */
  92:   public FreetypeGlyphVector(Font f, String s, FontRenderContext frc)
  93:   {
  94:     this(f, s, frc, Font.LAYOUT_LEFT_TO_RIGHT);
  95:   }
  96: 
  97:   /**
  98:    * Create a glyphvector from a given (Freetype) font and a String.
  99:    */
 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:     // reverse the glyph ordering.
 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:   /**
 123:    * Create a glyphvector from a given set of glyph codes.
 124:    */
 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:   /**
 140:    * Cloning constructor
 141:    */  
 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:   /**
 169:    * Create the array of glyph codes.
 170:    */
 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:     // UTF32 surrogate handling
 182:     if( codePoints[i] != (int)s.charAt( stringIndex ) )
 183:       stringIndex ++;
 184:     stringIndex ++;
 185:       }
 186: 
 187:    glyphCodes = getGlyphs( codePoints );
 188:   }
 189: 
 190:   /**
 191:    * Returns the glyph code within the font for a given character
 192:    */
 193:   public native int[] getGlyphs(int[] codepoints);
 194: 
 195:   /**
 196:    * Returns the kerning of a glyph pair
 197:    */
 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:   /**
 211:    * Duh, compares two instances.
 212:    */
 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:   /**
 224:    * Returns the associated Font
 225:    */
 226:   public Font getFont()
 227:   {
 228:     return font;
 229:   }
 230: 
 231:   /**
 232:    * Returns the associated FontRenderContext
 233:    */
 234:   public FontRenderContext getFontRenderContext()
 235:   {
 236:     return frc;
 237:   }
 238: 
 239:   /**
 240:    * Layout the glyphs.
 241:    */
 242:   public void performDefaultLayout()
 243:   {
 244:     logicalBounds = null; // invalidate caches.
 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:   /**
 264:    * Returns the code of the glyph at glyphIndex;
 265:    */
 266:   public int getGlyphCode(int glyphIndex)
 267:   {
 268:     return glyphCodes[ glyphIndex ];
 269:   }
 270: 
 271:   /**
 272:    * Returns multiple glyphcodes.
 273:    */
 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:   /**
 290:    * FIXME: Implement me.
 291:    */
 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:   /*
 306:    * FIXME: Not all glyph types are supported.
 307:    * (The JDK doesn't really seem to do so either)
 308:    */
 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:   /**
 339:    * Returns the metrics of a single glyph.
 340:    */
 341:   public GlyphMetrics getGlyphMetrics(int glyphIndex)
 342:   {
 343:     if( metricsCache == null )
 344:       setupGlyphMetrics();
 345: 
 346:     return metricsCache[ glyphIndex ];
 347:   }
 348: 
 349:   /**
 350:    * Returns the outline of a single glyph.
 351:    */
 352:   public Shape getGlyphOutline(int glyphIndex)
 353:   {
 354:     GeneralPath gp = getGlyphOutlineNative( glyphCodes[ glyphIndex ] );
 355:     gp.transform( glyphTransforms[ glyphIndex ] );
 356:     return gp;
 357:   }
 358: 
 359:   /**
 360:    * Returns the position of a single glyph.
 361:    */
 362:   public Point2D getGlyphPosition(int glyphIndex)
 363:   {
 364:     return glyphTransforms[ glyphIndex ].transform( new Point2D.Double(0, 0),
 365:                            null );
 366:   }
 367: 
 368:   /**
 369:    * Returns the positions of multiple glyphs.
 370:    */
 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:   /**
 396:    * Returns the transform of a glyph.
 397:    */
 398:   public AffineTransform getGlyphTransform(int glyphIndex)
 399:   {
 400:     return new AffineTransform( glyphTransforms[ glyphIndex ] );
 401:   }
 402: 
 403:   /**
 404:    * Returns the visual bounds of a glyph
 405:    * May be off by a pixel or two due to hinting/rasterization.
 406:    */
 407:   public Shape getGlyphVisualBounds(int glyphIndex)
 408:   {
 409:     return getGlyphOutline( glyphIndex ).getBounds2D();
 410:   }
 411: 
 412:   /**
 413:    * Return the logical bounds of the whole thing.
 414:    */
 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:   /**
 434:    * Returns the number of glyphs.
 435:    */
 436:   public int getNumGlyphs()
 437:   {
 438:     return glyphCodes.length;
 439:   }
 440: 
 441:   /**
 442:    * Returns the outline of the entire GlyphVector.
 443:    */
 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:   /**
 453:    * TODO: 
 454:    * FreeType does not currently have an API for the JSTF table. We should 
 455:    * probably get the table ourselves from FT and pass it to some parser 
 456:    * which the native font peers will need.
 457:    */
 458:   public GlyphJustificationInfo getGlyphJustificationInfo(int glyphIndex)
 459:   {
 460:     return null;
 461:   }
 462: 
 463:   /**
 464:    * Returns the outline of the entire vector, drawn at (x,y).
 465:    */
 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:   /**
 475:    * Returns the visual bounds of the entire GlyphVector.
 476:    * May be off by a pixel or two due to hinting/rasterization.
 477:    */
 478:   public Rectangle2D getVisualBounds()
 479:   {
 480:     return getOutline().getBounds2D();
 481:   }
 482: 
 483:   /**
 484:    * Sets the position of a glyph.
 485:    */
 486:   public void setGlyphPosition(int glyphIndex, Point2D newPos)
 487:   {
 488:     // FIXME: Scaling, etc.?
 489:     glyphTransforms[ glyphIndex ].setToTranslation( newPos.getX(), 
 490:                             newPos.getY() );
 491:     logicalBounds = null;
 492:     glyphPositions = null;
 493:   }
 494: 
 495:   /**
 496:    * Sets the transform of a single glyph.
 497:    */
 498:   public void setGlyphTransform(int glyphIndex, AffineTransform newTX)
 499:   {
 500:     glyphTransforms[ glyphIndex ].setTransform( newTX );
 501:     logicalBounds = null;
 502:     glyphPositions = null;
 503:   }
 504: }