Source for gnu.java.awt.font.GNUGlyphVector

   1: /* GNUGlyphVector.java -- The GNU implementation of GlyphVector.
   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.font;
  39: 
  40: import java.awt.Font;
  41: import java.awt.font.FontRenderContext;
  42: import java.awt.font.GlyphMetrics;
  43: import java.awt.font.GlyphJustificationInfo;
  44: import java.awt.font.GlyphVector;
  45: 
  46: import java.awt.Shape;
  47: import java.awt.geom.AffineTransform;
  48: import java.awt.geom.GeneralPath;
  49: import java.awt.geom.Point2D;
  50: import java.awt.geom.Rectangle2D;
  51: 
  52: 
  53: /**
  54:  * The GNU implementation of the abstract GlyphVector class, which
  55:  * uses the services provided by a FontDelegate for its functionality.
  56:  *
  57:  * @author Sascha Brawer (brawer@dandelis.ch)
  58:  */
  59: public class GNUGlyphVector
  60:   extends GlyphVector
  61: {
  62:   private FontDelegate fontDelegate;
  63:   private Font font;
  64:   private FontRenderContext renderContext;
  65:   private int[] glyphs;
  66:   private float fontSize;
  67:   private AffineTransform transform;
  68:   private boolean valid;
  69: 
  70: 
  71:   /**
  72:    * The position of each glyph. The horizontal position of the
  73:    * <code>i</code>-th glyph is at <code>pos[i * 2]</code>, its
  74:    * vertical position at <code>pos[i * 2 + 1]</code>. The total
  75:    * advance width of the entire vector is stored at
  76:    * <code>pos[numGlyphs]</code>, the total advance height at
  77:    * <code>pos[numGlyphs + 1]</code>.
  78:    */
  79:   private float[] pos;
  80: 
  81: 
  82:   private AffineTransform[] transforms;
  83:   private int layoutFlags;
  84: 
  85: 
  86:   /**
  87:    * Constructs a new GNUGlyphVector.
  88:    *
  89:    * @param fontDelegate the FontDelegate that creates this vector.
  90:    *
  91:    * @param font the Font that this GlyphVector will return for {@link
  92:    * #getFont()}. That object is also used to determine the point
  93:    * size, which affects the affine transformation used by the font
  94:    * scaler.
  95:    *
  96:    * @param renderContext an object with parameters for font
  97:    * rendering, such as whether anti-aliasing is enabled.
  98:    *
  99:    * @param glyphs the glyphs in this vector.
 100:    */
 101:   public GNUGlyphVector(FontDelegate fontDelegate,
 102:                         Font font,
 103:                         FontRenderContext renderContext,
 104:                         int[] glyphs)
 105:   {
 106:     this.fontDelegate = fontDelegate;
 107:     this.font = font;
 108:     this.renderContext = renderContext;
 109:     this.glyphs = glyphs;
 110:     
 111:     fontSize = font.getSize2D();
 112:     transform = font.getTransform(); // returns a modifiable copy
 113:     //transform.concatenate(renderContext.getTransform());
 114:   }
 115: 
 116: 
 117: 
 118:   /**
 119:    * Returns the font of the glyphs in this GlyphVector.
 120:    */
 121:   public Font getFont()
 122:   {
 123:     return font;
 124:   }
 125: 
 126: 
 127:   /**
 128:    * Returns the FontRenderContext that is used to calculate the
 129:    * extent and position of the glyphs.
 130:    */
 131:   public FontRenderContext getFontRenderContext()
 132:   {
 133:     return renderContext;
 134:   }
 135: 
 136: 
 137:   /**
 138:    * Moves each glyph in the vector to its default position.
 139:    */
 140:   public void performDefaultLayout()
 141:   {
 142:     float x, y, advanceWidth, advanceHeight;
 143:     int i, p;
 144:     AffineTransform tx;
 145:     Point2D.Float advance = new Point2D.Float();
 146: 
 147:     pos = new float[(glyphs.length + 1) * 2];
 148:     x = y = 0.0f;
 149:     p = 0;
 150:     for (i = p = 0; i < glyphs.length; i++)
 151:     {
 152:       p += 2;
 153:       
 154:       if ((transforms == null) || (tx = transforms[i]) == null)
 155:         tx = this.transform;
 156:       else
 157:       {
 158:         tx = new AffineTransform(tx);
 159:         tx.concatenate(this.transform);
 160:       }
 161:       
 162:       fontDelegate.getAdvance(glyphs[i], fontSize, tx,
 163:                               renderContext.isAntiAliased(),
 164:                               renderContext.usesFractionalMetrics(),
 165:                               /* horizontal */ true,
 166:                               advance);
 167:       pos[p] = x += advance.x;
 168:       pos[p + 1] = y += advance.y;
 169:     }
 170:     valid = true;
 171:   }
 172: 
 173: 
 174:   /**
 175:    * Determines the number of glyphs in this GlyphVector.
 176:    */
 177:   public int getNumGlyphs()
 178:   {
 179:     return glyphs.length;
 180:   }
 181: 
 182: 
 183:   /**
 184:    * Determines the glyph number by index in this vector.
 185:    * Glyph numbers are specific to each font, so two fonts
 186:    * will likely assign different numbers to the same glyph.
 187:    *
 188:    * @param glyphIndex the index of the glyph whose glyph number is to
 189:    * be retrieved.
 190:    *
 191:    * @throws IndexOutOfBoundsException if <code>glyphIndex</code>
 192:    * is not in the range <code[0 .. getNumGlyphs() - 1]</code>.
 193:    */
 194:   public int getGlyphCode(int glyphIndex)
 195:   {
 196:     /* The exception is thrown automatically if the index is out
 197:      * of the valid bounds.
 198:      */
 199:     return glyphs[glyphIndex];
 200:   }
 201: 
 202: 
 203:   /**
 204:    * Returns a slice of this GlyphVector.
 205:    *
 206:    * @param firstGlyphIndex the index of the first glyph in the
 207:    * returned slice.
 208:    *
 209:    * @param numEntries the size of the returned slice.
 210:    *
 211:    * @param outCodes a pre-allocated array for storing the slice,
 212:    * or <code>null</code> to cause allocation of a new array.
 213:    *
 214:    * @return a slice of this GlyphVector. If <code>outCodes</code>
 215:    * is <code>null</code>, the slice will be stored into a freshly
 216:    * allocated array; otherwise, the result will be stored into
 217:    * <code>outCodes</code>.
 218:    */
 219:   public int[] getGlyphCodes(int firstGlyphIndex,
 220:                              int numEntries,
 221:                              int[] outCodes)
 222:   {
 223:     if (numEntries < 0)
 224:       throw new IllegalArgumentException();
 225:     if (outCodes == null)
 226:       outCodes = new int[numEntries];
 227:     System.arraycopy(glyphs, firstGlyphIndex, outCodes, 0, numEntries);
 228:     return outCodes;
 229:   }
 230: 
 231: 
 232:   public Rectangle2D getLogicalBounds()
 233:   {
 234:     float ascent, descent;
 235: 
 236:     validate();
 237: 
 238:     return new Rectangle2D.Float(0, 0,
 239:                                  pos[pos.length - 2],
 240:                                  getAscent() - getDescent());
 241:   }
 242: 
 243: 
 244:   public Rectangle2D getVisualBounds()
 245:   {
 246:     validate();
 247: 
 248:     // FIXME: Not yet implemented.
 249:     return getLogicalBounds();
 250:   }
 251: 
 252: 
 253:   /**
 254:    * Returns the shape of this GlyphVector.
 255:    */
 256:   public Shape getOutline()
 257:   {
 258:     validate();
 259:     return getOutline(0.0f, 0.0f);
 260:   }
 261: 
 262: 
 263:   /**
 264:    * Returns the shape of this GlyphVector, translated to the
 265:    * specified position.
 266:    *
 267:    * @param x the horizontal position for rendering this vector.
 268:    * @param y the vertical position for rendering this vector.
 269:    */
 270:   public Shape getOutline(float x, float y)
 271:   {
 272:     validate();
 273: 
 274:     GeneralPath outline = new GeneralPath();
 275:     int len = glyphs.length;
 276:     for (int i = 0; i < len; i++)
 277:       {
 278:         GeneralPath p = new GeneralPath(getGlyphOutline(i));
 279:         outline.append(p, false);
 280:       }
 281:     AffineTransform t = new AffineTransform();
 282:     t.translate(x, y);
 283:     outline.transform(t);
 284:     return outline;
 285:   }
 286: 
 287: 
 288:   /**
 289:    * Determines the shape of the specified glyph.
 290:    *
 291:    * @throws IndexOutOfBoundsException if <code>glyphIndex</code> is
 292:    * not in the range <code[0 .. getNumGlyphs()]</code>.
 293:    */
 294:   public Shape getGlyphOutline(int glyphIndex)
 295:   {
 296:     AffineTransform tx, glyphTx;
 297:     GeneralPath path;
 298: 
 299:     validate();
 300: 
 301:     if ((transforms != null)
 302:         && ((glyphTx = transforms[glyphIndex]) != null))
 303:     {
 304:       tx =  new AffineTransform(transform);
 305:       tx.concatenate(glyphTx);
 306:     }
 307:     else
 308:       tx = transform;
 309: 
 310:     path = fontDelegate.getGlyphOutline(glyphs[glyphIndex], fontSize, tx,
 311:                                         renderContext.isAntiAliased(),
 312:                                         renderContext.usesFractionalMetrics());
 313: 
 314:     tx = new AffineTransform();
 315:     tx.translate(pos[glyphIndex * 2], pos[glyphIndex * 2 + 1]);
 316:     path.transform(tx);
 317:     return path;
 318:   }
 319: 
 320: 
 321:   /**
 322:    * Determines the position of the specified glyph, or the
 323:    * total advance width and height of the vector.
 324:    *
 325:    * @param glyphIndex the index of the glyph in question.
 326:    * If this value equals <code>getNumGlyphs()</code>, the
 327:    * position <i>after</i> the last glyph will be returned,
 328:    * which is the total advance width and height of the vector.
 329:    *
 330:    * @throws IndexOutOfBoundsException if <code>glyphIndex</code> is
 331:    * not in the range <code[0 .. getNumGlyphs()]</code>.
 332:    */
 333:   public Point2D getGlyphPosition(int glyphIndex)
 334:   {
 335:     validate();
 336:     return new Point2D.Float(pos[glyphIndex * 2],
 337:                              pos[glyphIndex * 2 + 1]);
 338:   }
 339: 
 340: 
 341:   /**
 342:    * Moves the specified glyph to a new position, or changes the
 343:    * advance width and height of the entire glyph vector.
 344:    *
 345:    * <p>Note that the position of an individual glyph may also
 346:    * affected by its affine transformation.
 347:    *
 348:    * @param glyphIndex the index of the moved glyph. If
 349:    * <code>glyphIndex</code> equals the total number of glyphs in this
 350:    * vector, the advance width and height of the vector is changed.
 351:    *
 352:    * @param position the new position of the glyph.
 353:    *
 354:    * @throws IndexOutOfBoundsException if <code>glyphIndex</code> is
 355:    * not in the range <code[0 .. getNumGlyphs()]</code>.
 356:    */
 357:   public void setGlyphPosition(int glyphIndex, Point2D position)
 358:   {
 359:     validate();
 360:     pos[glyphIndex * 2] = (float) position.getX();
 361:     pos[glyphIndex * 2 + 1] = (float) position.getY();
 362:   }
 363: 
 364: 
 365:   /**
 366:    * Returns the affine transformation that is applied to the
 367:    * glyph at the specified index.
 368:    *
 369:    * @param glyphIndex the index of the glyph whose transformation
 370:    * is to be retrieved.
 371:    *
 372:    * @return an affine transformation, or <code>null</code>
 373:    * for the identity transformation.
 374:    *
 375:    * @throws IndexOutOfBoundsException if <code>glyphIndex</code> is
 376:    * not in the range <code[0 .. getNumGlyphs() - 1]</code>.
 377:    */
 378:   public AffineTransform getGlyphTransform(int glyphIndex)
 379:   {
 380:     if (transforms == null)
 381:       return null;
 382:     else
 383:       return transforms[glyphIndex];
 384:   }
 385: 
 386: 
 387:   /**
 388:    * Applies an affine transformation to the glyph at the specified
 389:    * index.
 390:    *
 391:    * @param glyphIndex the index of the glyph to which the
 392:    * transformation is applied.
 393:    *
 394:    * @param transform the affine transformation for the glyph, or
 395:    * <code>null</code> for an identity transformation.
 396:    */
 397:   public void setGlyphTransform(int glyphIndex,
 398:                                 AffineTransform transform)
 399:   {
 400:     if (transforms == null)
 401:       transforms = new AffineTransform[glyphs.length];
 402:     transforms[glyphIndex] = transform;
 403: 
 404:     /* If the GlyphVector has only a transform for a single glyph, and
 405:      * the caller clears its transform, the FLAG_HAS_TRANSFORMS bit
 406:      * should be cleared in layoutFlags.  However, this would require
 407:      * that we keep track of the number of transformed glyphs, or that
 408:      * we count them when a transform is cleared. This would
 409:      * complicate the code quite a bit. Note that the only drawback of
 410:      * wrongly setting FLAG_HAS_TRANSFORMS is that a slower code path
 411:      * might be taken for rendering the vector. Right now, we never
 412:      * really look at the flag, so it does not make any difference.
 413:      */
 414:     if (transform != null)
 415:       layoutFlags |= FLAG_HAS_TRANSFORMS;
 416:     valid = false;
 417:   }
 418: 
 419: 
 420:   /**
 421:    * Returns flags that can be used for optimizing the rendering
 422:    * of this GlyphVector.
 423:    *
 424:    * @return a bit mask with the applicable flags set.
 425:    *
 426:    * @since 1.4
 427:    *
 428:    * @see GlyphVector#FLAG_HAS_POSITION_ADJUSTMENTS
 429:    * @see GlyphVector#FLAG_HAS_TRANSFORMS
 430:    * @see GlyphVector#FLAG_RUN_RTL
 431:    * @see GlyphVector#FLAG_COMPLEX_GLYPHS
 432:    * @see GlyphVector#FLAG_MASK
 433:    */
 434:   public int getLayoutFlags()
 435:   {
 436:     return layoutFlags;
 437:   }
 438:   
 439:   
 440:   /**
 441:    * Returns the positions of a range of glyphs in this vector.
 442:    * 
 443:    * @param firstGlyphIndex the index of the first glyph whose
 444:    * position is retrieved.
 445:    *
 446:    * @param numGlyphs the number of glyphs whose positions
 447:    * are retrieved.
 448:    *
 449:    * @param outPositions an array for storing the results
 450:    * (the length must be at least twice <code>numGlyphs</code>),
 451:    * or <code>null</code> for freshly allocating an array.
 452:    *
 453:    * @return an array with the glyph positions. The horizontal
 454:    * position of the <code>i</code>-th glyph is at index <code>2 *
 455:    * i</code>, the vertical position at index <code>2 * i + 1</code>.
 456:    *
 457:    * @throws IllegalArgumentException if <code>numGlyphs</code>
 458:    * is less than zero.
 459:    *
 460:    * @throws IndexOutOfBoundsException if either
 461:    * <code>firstGlyphIndex</code> or <code>(firstGlyphIndex +
 462:    * numGlyphs)</code> is not in the range <code>[0 .. getNumGlyphs() -
 463:    * 1]</code>.
 464:    */
 465:   public float[] getGlyphPositions(int firstGlyphIndex,
 466:                                    int numGlyphs,
 467:                                    float[] outPositions)
 468:   {
 469:     if (numGlyphs < 0)
 470:       throw new IllegalArgumentException();
 471: 
 472:     validate();
 473:     if (outPositions == null)
 474:       outPositions = new float[numGlyphs * 2];
 475: 
 476:     System.arraycopy(/*src */ pos, /* srcStart */ firstGlyphIndex * 2,
 477:                      /* dest */ outPositions, /* destStart */ 0,
 478:                      /* length */ numGlyphs * 2);
 479:     return outPositions;
 480:   }
 481: 
 482:   
 483:   private float getAscent()
 484:   {
 485:     return fontDelegate.getAscent(fontSize, transform,
 486:                                   renderContext.isAntiAliased(),
 487:                                   renderContext.usesFractionalMetrics(),
 488:                                   /* horizontal */ true);
 489:   }
 490: 
 491: 
 492:   private float getDescent()
 493:   {
 494:     return fontDelegate.getDescent(fontSize, transform,
 495:                                    renderContext.isAntiAliased(),
 496:                                    renderContext.usesFractionalMetrics(),
 497:                                    /* horizontal */ true);    
 498:   }
 499: 
 500: 
 501:   public Shape getGlyphLogicalBounds(int glyphIndex)
 502:   {
 503:     float x, y, ascent;
 504: 
 505:     validate();
 506:     ascent = getAscent();
 507:     x = pos[glyphIndex * 2];
 508:     y = pos[glyphIndex * 2 + 1];
 509: 
 510:     return new Rectangle2D.Float(x, y - ascent,
 511:                                  pos[(glyphIndex + 1) * 2] - x,
 512:                                  ascent - getDescent());
 513:   }
 514: 
 515: 
 516:   public Shape getGlyphVisualBounds(int glyphIndex)
 517:   {
 518:     return getGlyphOutline(glyphIndex).getBounds2D();
 519:   }
 520: 
 521: 
 522:   /**
 523:    * Determines the metrics of the glyph at the specified index.
 524:    *
 525:    * @param glyphIndex the index of the glyph whose metrics is to be
 526:    * retrieved.
 527:    *
 528:    * @throws IndexOutOfBoundsException if <code>glyphIndex</code> is
 529:    * not in the range <code[0 .. getNumGlyphs() - 1]</code>.
 530:    */
 531:   public GlyphMetrics getGlyphMetrics(int glyphIndex)
 532:   {
 533:     // FIXME: Not yet implemented.
 534:     throw new UnsupportedOperationException();
 535:   }
 536: 
 537: 
 538:   /**
 539:    * Determines the justification information for the glyph at the
 540:    * specified index.
 541:    *
 542:    * @param glyphIndex the index of the glyph whose justification
 543:    * information is to be retrieved.
 544:    *
 545:    * @throws IndexOutOfBoundsException if <code>glyphIndex</code> is
 546:    * not in the range <code[0 .. getNumGlyphs() - 1]</code>.
 547:    */
 548:   public GlyphJustificationInfo getGlyphJustificationInfo(int glyphIndex)
 549:   {
 550:     // FIXME: Not yet implemented.
 551:     throw new UnsupportedOperationException();
 552:   }
 553: 
 554: 
 555:   /**
 556:    * Determines whether another GlyphVector is for the same font and
 557:    * rendering context, uses the same glyphs and positions them to the
 558:    * same location.
 559:    *
 560:    * @param other the GlyphVector to compare with.
 561:    *
 562:    * @return <code>true</code> if the two vectors are equal,
 563:    * <code>false</code> otherwise.
 564:    */
 565:   public boolean equals(GlyphVector other)
 566:   {
 567:     GNUGlyphVector o;
 568:     if (!(other instanceof GNUGlyphVector))
 569:       return false;
 570: 
 571:     o = (GNUGlyphVector) other;
 572:     if ((this.font != o.font)
 573:         || (this.fontDelegate != o.fontDelegate)
 574:         || (this.renderContext != o.renderContext)
 575:         || (this.glyphs.length != o.glyphs.length))
 576:       return false;
 577: 
 578:     for (int i = 0; i < glyphs.length; i++)
 579:       if (this.glyphs[i] != o.glyphs[i])
 580:         return false;
 581: 
 582:     validate();
 583:     o.validate();
 584:     for (int i = 0; i < pos.length; i++)
 585:       if (this.pos[i] != o.pos[i])
 586:         return false;
 587: 
 588:     return true;
 589:   }
 590: 
 591:   private void validate()
 592:   {
 593:     if (!valid)
 594:       performDefaultLayout();
 595:   }
 596: }