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:       // FIXME: We shouldn't round here, but instead hint the metrics
 168:       // correctly.
 169:       pos[p] = x += Math.round(advance.x);
 170:       pos[p + 1] = y += advance.y;
 171:     }
 172:     valid = true;
 173:   }
 174: 
 175: 
 176:   /**
 177:    * Determines the number of glyphs in this GlyphVector.
 178:    */
 179:   public int getNumGlyphs()
 180:   {
 181:     return glyphs.length;
 182:   }
 183: 
 184: 
 185:   /**
 186:    * Determines the glyph number by index in this vector.
 187:    * Glyph numbers are specific to each font, so two fonts
 188:    * will likely assign different numbers to the same glyph.
 189:    *
 190:    * @param glyphIndex the index of the glyph whose glyph number is to
 191:    * be retrieved.
 192:    *
 193:    * @throws IndexOutOfBoundsException if <code>glyphIndex</code>
 194:    * is not in the range <code[0 .. getNumGlyphs() - 1]</code>.
 195:    */
 196:   public int getGlyphCode(int glyphIndex)
 197:   {
 198:     /* The exception is thrown automatically if the index is out
 199:      * of the valid bounds.
 200:      */
 201:     return glyphs[glyphIndex];
 202:   }
 203: 
 204: 
 205:   /**
 206:    * Returns a slice of this GlyphVector.
 207:    *
 208:    * @param firstGlyphIndex the index of the first glyph in the
 209:    * returned slice.
 210:    *
 211:    * @param numEntries the size of the returned slice.
 212:    *
 213:    * @param outCodes a pre-allocated array for storing the slice,
 214:    * or <code>null</code> to cause allocation of a new array.
 215:    *
 216:    * @return a slice of this GlyphVector. If <code>outCodes</code>
 217:    * is <code>null</code>, the slice will be stored into a freshly
 218:    * allocated array; otherwise, the result will be stored into
 219:    * <code>outCodes</code>.
 220:    */
 221:   public int[] getGlyphCodes(int firstGlyphIndex,
 222:                              int numEntries,
 223:                              int[] outCodes)
 224:   {
 225:     if (numEntries < 0)
 226:       throw new IllegalArgumentException();
 227:     if (outCodes == null)
 228:       outCodes = new int[numEntries];
 229:     System.arraycopy(glyphs, firstGlyphIndex, outCodes, 0, numEntries);
 230:     return outCodes;
 231:   }
 232: 
 233: 
 234:   public Rectangle2D getLogicalBounds()
 235:   {
 236:     float ascent, descent;
 237: 
 238:     validate();
 239: 
 240:     return new Rectangle2D.Float(0, 0,
 241:                                  pos[pos.length - 2],
 242:                                  getAscent() - getDescent());
 243:   }
 244: 
 245: 
 246:   public Rectangle2D getVisualBounds()
 247:   {
 248:     validate();
 249: 
 250:     // FIXME: Not yet implemented.
 251:     return getLogicalBounds();
 252:   }
 253: 
 254: 
 255:   /**
 256:    * Returns the shape of this GlyphVector.
 257:    */
 258:   public Shape getOutline()
 259:   {
 260:     validate();
 261:     return getOutline(0.0f, 0.0f);
 262:   }
 263: 
 264: 
 265:   /**
 266:    * Returns the shape of this GlyphVector, translated to the
 267:    * specified position.
 268:    *
 269:    * @param x the horizontal position for rendering this vector.
 270:    * @param y the vertical position for rendering this vector.
 271:    */
 272:   public Shape getOutline(float x, float y)
 273:   {
 274:     validate();
 275: 
 276:     GeneralPath outline = new GeneralPath();
 277:     int len = glyphs.length;
 278:     for (int i = 0; i < len; i++)
 279:       {
 280:         GeneralPath p = new GeneralPath(getGlyphOutline(i));
 281:         outline.append(p, false);
 282:       }
 283:     AffineTransform t = new AffineTransform();
 284:     t.translate(x, y);
 285:     outline.transform(t);
 286:     return outline;
 287:   }
 288: 
 289:   public Shape getOutline(float x, float y, int type)
 290:   {
 291:     validate();
 292: 
 293:     GeneralPath outline = new GeneralPath();
 294:     int len = glyphs.length;
 295:     for (int i = 0; i < len; i++)
 296:       {
 297:         GeneralPath p = new GeneralPath(getGlyphOutline(i, type));
 298:         outline.append(p, false);
 299:       }
 300:     AffineTransform t = new AffineTransform();
 301:     t.translate(x, y);
 302:     outline.transform(t);
 303:     return outline;
 304:   }
 305: 
 306:   /**
 307:    * Determines the shape of the specified glyph.
 308:    *
 309:    * @throws IndexOutOfBoundsException if <code>glyphIndex</code> is
 310:    * not in the range <code[0 .. getNumGlyphs()]</code>.
 311:    */
 312:   public Shape getGlyphOutline(int glyphIndex)
 313:   {
 314:     AffineTransform tx, glyphTx;
 315:     GeneralPath path;
 316: 
 317:     validate();
 318: 
 319:     if ((transforms != null)
 320:         && ((glyphTx = transforms[glyphIndex]) != null))
 321:     {
 322:       tx =  new AffineTransform(transform);
 323:       tx.concatenate(glyphTx);
 324:     }
 325:     else
 326:       tx = transform;
 327: 
 328:     path = fontDelegate.getGlyphOutline(glyphs[glyphIndex], fontSize, tx,
 329:                                         renderContext.isAntiAliased(),
 330:                                         renderContext.usesFractionalMetrics(),
 331:                                         FontDelegate.FLAG_FITTED);
 332: 
 333:     tx = new AffineTransform();
 334:     tx.translate(pos[glyphIndex * 2], pos[glyphIndex * 2 + 1]);
 335:     path.transform(tx);
 336:     return path;
 337:   }
 338: 
 339:   public Shape getGlyphOutline(int glyphIndex, int type)
 340:   {
 341:     AffineTransform tx, glyphTx;
 342:     GeneralPath path;
 343: 
 344:     validate();
 345: 
 346:     if ((transforms != null)
 347:         && ((glyphTx = transforms[glyphIndex]) != null))
 348:     {
 349:       tx =  new AffineTransform(transform);
 350:       tx.concatenate(glyphTx);
 351:     }
 352:     else
 353:       tx = transform;
 354: 
 355:     path = fontDelegate.getGlyphOutline(glyphs[glyphIndex], fontSize, tx,
 356:                                         renderContext.isAntiAliased(),
 357:                                         renderContext.usesFractionalMetrics(),
 358:                                         type);
 359: 
 360:     tx = new AffineTransform();
 361:     tx.translate(pos[glyphIndex * 2], pos[glyphIndex * 2 + 1]);
 362:     path.transform(tx);
 363:     return path;
 364:   }
 365: 
 366:   /**
 367:    * Determines the position of the specified glyph, or the
 368:    * total advance width and height of the vector.
 369:    *
 370:    * @param glyphIndex the index of the glyph in question.
 371:    * If this value equals <code>getNumGlyphs()</code>, the
 372:    * position <i>after</i> the last glyph will be returned,
 373:    * which is the total advance width and height of the vector.
 374:    *
 375:    * @throws IndexOutOfBoundsException if <code>glyphIndex</code> is
 376:    * not in the range <code[0 .. getNumGlyphs()]</code>.
 377:    */
 378:   public Point2D getGlyphPosition(int glyphIndex)
 379:   {
 380:     validate();
 381:     return new Point2D.Float(pos[glyphIndex * 2],
 382:                              pos[glyphIndex * 2 + 1]);
 383:   }
 384: 
 385: 
 386:   /**
 387:    * Moves the specified glyph to a new position, or changes the
 388:    * advance width and height of the entire glyph vector.
 389:    *
 390:    * <p>Note that the position of an individual glyph may also
 391:    * affected by its affine transformation.
 392:    *
 393:    * @param glyphIndex the index of the moved glyph. If
 394:    * <code>glyphIndex</code> equals the total number of glyphs in this
 395:    * vector, the advance width and height of the vector is changed.
 396:    *
 397:    * @param position the new position of the glyph.
 398:    *
 399:    * @throws IndexOutOfBoundsException if <code>glyphIndex</code> is
 400:    * not in the range <code[0 .. getNumGlyphs()]</code>.
 401:    */
 402:   public void setGlyphPosition(int glyphIndex, Point2D position)
 403:   {
 404:     validate();
 405:     pos[glyphIndex * 2] = (float) position.getX();
 406:     pos[glyphIndex * 2 + 1] = (float) position.getY();
 407:   }
 408: 
 409: 
 410:   /**
 411:    * Returns the affine transformation that is applied to the
 412:    * glyph at the specified index.
 413:    *
 414:    * @param glyphIndex the index of the glyph whose transformation
 415:    * is to be retrieved.
 416:    *
 417:    * @return an affine transformation, or <code>null</code>
 418:    * for the identity transformation.
 419:    *
 420:    * @throws IndexOutOfBoundsException if <code>glyphIndex</code> is
 421:    * not in the range <code[0 .. getNumGlyphs() - 1]</code>.
 422:    */
 423:   public AffineTransform getGlyphTransform(int glyphIndex)
 424:   {
 425:     if (transforms == null)
 426:       return null;
 427:     else
 428:       return transforms[glyphIndex];
 429:   }
 430: 
 431: 
 432:   /**
 433:    * Applies an affine transformation to the glyph at the specified
 434:    * index.
 435:    *
 436:    * @param glyphIndex the index of the glyph to which the
 437:    * transformation is applied.
 438:    *
 439:    * @param transform the affine transformation for the glyph, or
 440:    * <code>null</code> for an identity transformation.
 441:    */
 442:   public void setGlyphTransform(int glyphIndex,
 443:                                 AffineTransform transform)
 444:   {
 445:     if (transforms == null)
 446:       transforms = new AffineTransform[glyphs.length];
 447:     transforms[glyphIndex] = transform;
 448: 
 449:     /* If the GlyphVector has only a transform for a single glyph, and
 450:      * the caller clears its transform, the FLAG_HAS_TRANSFORMS bit
 451:      * should be cleared in layoutFlags.  However, this would require
 452:      * that we keep track of the number of transformed glyphs, or that
 453:      * we count them when a transform is cleared. This would
 454:      * complicate the code quite a bit. Note that the only drawback of
 455:      * wrongly setting FLAG_HAS_TRANSFORMS is that a slower code path
 456:      * might be taken for rendering the vector. Right now, we never
 457:      * really look at the flag, so it does not make any difference.
 458:      */
 459:     if (transform != null)
 460:       layoutFlags |= FLAG_HAS_TRANSFORMS;
 461:     valid = false;
 462:   }
 463: 
 464: 
 465:   /**
 466:    * Returns flags that can be used for optimizing the rendering
 467:    * of this GlyphVector.
 468:    *
 469:    * @return a bit mask with the applicable flags set.
 470:    *
 471:    * @since 1.4
 472:    *
 473:    * @see GlyphVector#FLAG_HAS_POSITION_ADJUSTMENTS
 474:    * @see GlyphVector#FLAG_HAS_TRANSFORMS
 475:    * @see GlyphVector#FLAG_RUN_RTL
 476:    * @see GlyphVector#FLAG_COMPLEX_GLYPHS
 477:    * @see GlyphVector#FLAG_MASK
 478:    */
 479:   public int getLayoutFlags()
 480:   {
 481:     return layoutFlags;
 482:   }
 483:   
 484:   
 485:   /**
 486:    * Returns the positions of a range of glyphs in this vector.
 487:    * 
 488:    * @param firstGlyphIndex the index of the first glyph whose
 489:    * position is retrieved.
 490:    *
 491:    * @param numGlyphs the number of glyphs whose positions
 492:    * are retrieved.
 493:    *
 494:    * @param outPositions an array for storing the results
 495:    * (the length must be at least twice <code>numGlyphs</code>),
 496:    * or <code>null</code> for freshly allocating an array.
 497:    *
 498:    * @return an array with the glyph positions. The horizontal
 499:    * position of the <code>i</code>-th glyph is at index <code>2 *
 500:    * i</code>, the vertical position at index <code>2 * i + 1</code>.
 501:    *
 502:    * @throws IllegalArgumentException if <code>numGlyphs</code>
 503:    * is less than zero.
 504:    *
 505:    * @throws IndexOutOfBoundsException if either
 506:    * <code>firstGlyphIndex</code> or <code>(firstGlyphIndex +
 507:    * numGlyphs)</code> is not in the range <code>[0 .. getNumGlyphs() -
 508:    * 1]</code>.
 509:    */
 510:   public float[] getGlyphPositions(int firstGlyphIndex,
 511:                                    int numGlyphs,
 512:                                    float[] outPositions)
 513:   {
 514:     if (numGlyphs < 0)
 515:       throw new IllegalArgumentException();
 516: 
 517:     validate();
 518:     if (outPositions == null)
 519:       outPositions = new float[numGlyphs * 2];
 520: 
 521:     System.arraycopy(/*src */ pos, /* srcStart */ firstGlyphIndex * 2,
 522:                      /* dest */ outPositions, /* destStart */ 0,
 523:                      /* length */ numGlyphs * 2);
 524:     return outPositions;
 525:   }
 526: 
 527:   
 528:   private float getAscent()
 529:   {
 530:     return fontDelegate.getAscent(fontSize, transform,
 531:                                   renderContext.isAntiAliased(),
 532:                                   renderContext.usesFractionalMetrics(),
 533:                                   /* horizontal */ true);
 534:   }
 535: 
 536: 
 537:   private float getDescent()
 538:   {
 539:     return fontDelegate.getDescent(fontSize, transform,
 540:                                    renderContext.isAntiAliased(),
 541:                                    renderContext.usesFractionalMetrics(),
 542:                                    /* horizontal */ true);    
 543:   }
 544: 
 545: 
 546:   public Shape getGlyphLogicalBounds(int glyphIndex)
 547:   {
 548:     float x, y, ascent;
 549: 
 550:     validate();
 551:     ascent = getAscent();
 552:     x = pos[glyphIndex * 2];
 553:     y = pos[glyphIndex * 2 + 1];
 554: 
 555:     return new Rectangle2D.Float(x, y - ascent,
 556:                                  pos[(glyphIndex + 1) * 2] - x,
 557:                                  ascent - getDescent());
 558:   }
 559: 
 560: 
 561:   public Shape getGlyphVisualBounds(int glyphIndex)
 562:   {
 563:     return getGlyphOutline(glyphIndex).getBounds2D();
 564:   }
 565: 
 566: 
 567:   /**
 568:    * Determines the metrics of the glyph at the specified index.
 569:    *
 570:    * @param glyphIndex the index of the glyph whose metrics is to be
 571:    * retrieved.
 572:    *
 573:    * @throws IndexOutOfBoundsException if <code>glyphIndex</code> is
 574:    * not in the range <code[0 .. getNumGlyphs() - 1]</code>.
 575:    */
 576:   public GlyphMetrics getGlyphMetrics(int glyphIndex)
 577:   {
 578:     // FIXME: Not yet implemented.
 579:     throw new UnsupportedOperationException();
 580:   }
 581: 
 582: 
 583:   /**
 584:    * Determines the justification information for the glyph at the
 585:    * specified index.
 586:    *
 587:    * @param glyphIndex the index of the glyph whose justification
 588:    * information is to be retrieved.
 589:    *
 590:    * @throws IndexOutOfBoundsException if <code>glyphIndex</code> is
 591:    * not in the range <code[0 .. getNumGlyphs() - 1]</code>.
 592:    */
 593:   public GlyphJustificationInfo getGlyphJustificationInfo(int glyphIndex)
 594:   {
 595:     // FIXME: Not yet implemented.
 596:     throw new UnsupportedOperationException();
 597:   }
 598: 
 599: 
 600:   /**
 601:    * Determines whether another GlyphVector is for the same font and
 602:    * rendering context, uses the same glyphs and positions them to the
 603:    * same location.
 604:    *
 605:    * @param other the GlyphVector to compare with.
 606:    *
 607:    * @return <code>true</code> if the two vectors are equal,
 608:    * <code>false</code> otherwise.
 609:    */
 610:   public boolean equals(GlyphVector other)
 611:   {
 612:     GNUGlyphVector o;
 613:     if (!(other instanceof GNUGlyphVector))
 614:       return false;
 615: 
 616:     o = (GNUGlyphVector) other;
 617:     if ((this.font != o.font)
 618:         || (this.fontDelegate != o.fontDelegate)
 619:         || (this.renderContext != o.renderContext)
 620:         || (this.glyphs.length != o.glyphs.length))
 621:       return false;
 622: 
 623:     for (int i = 0; i < glyphs.length; i++)
 624:       if (this.glyphs[i] != o.glyphs[i])
 625:         return false;
 626: 
 627:     validate();
 628:     o.validate();
 629:     for (int i = 0; i < pos.length; i++)
 630:       if (this.pos[i] != o.pos[i])
 631:         return false;
 632: 
 633:     return true;
 634:   }
 635: 
 636:   private void validate()
 637:   {
 638:     if (!valid)
 639:       performDefaultLayout();
 640:   }
 641: }