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 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: }