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