1/* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17/** 18 * @author Ilya S. Okomin 19 * @version $Revision$ 20 */ 21package org.apache.harmony.awt.gl.font; 22 23import java.awt.Font; 24import java.awt.Rectangle; 25import java.awt.Shape; 26import java.awt.font.FontRenderContext; 27import java.awt.font.GlyphJustificationInfo; 28import java.awt.font.GlyphMetrics; 29import java.awt.font.GlyphVector; 30import java.awt.geom.AffineTransform; 31import java.awt.geom.GeneralPath; 32import java.awt.geom.Point2D; 33import java.awt.geom.Rectangle2D; 34 35import org.apache.harmony.awt.internal.nls.Messages; 36 37/** 38 * GlyphVector implementation 39 */ 40public class CommonGlyphVector extends GlyphVector { 41 42 // array of transforms of glyphs in GlyphVector 43 protected AffineTransform[] glsTransforms; 44 45 // array of chars defined in constructor 46 public char[] charVector; 47 48 // array of Glyph objects, that describe information about glyphs 49 public Glyph[] vector; 50 51 // array of default positions of glyphs in GlyphVector 52 // without applying GlyphVector's transform 53 float[] defaultPositions; 54 55 // array of logical positions of glyphs in GlyphVector 56 57 float[] logicalPositions; 58 59 // array of visual (real) positions of glyphs in GlyphVector 60 public float[] visualPositions; 61 62 // FontRenderContext for this vector. 63 protected FontRenderContext vectorFRC; 64 65 // layout flags mask 66 protected int layoutFlags = 0; 67 68 // array of cached glyph outlines 69 protected Shape[] gvShapes; 70 71 FontPeerImpl peer; 72 73 // font corresponding to the GlyphVector 74 Font font; 75 76 // ascent of the font 77 float ascent; 78 79 // height of the font 80 float height; 81 82 // leading of the font 83 float leading; 84 85 // descent of the font 86 float descent; 87 88 // transform of the GlyphVector 89 AffineTransform transform; 90 91 /** 92 * Creates new CommonGlyphVector object from the specified parameters. 93 * 94 * @param chars an array of chars 95 * @param frc FontRenderContext object 96 * @param fnt Font object 97 * @param flags layout flags 98 */ 99 @SuppressWarnings("deprecation") 100 public CommonGlyphVector(char[] chars, FontRenderContext frc, Font fnt, 101 int flags) { 102 int len = chars.length; 103 104 this.font = fnt; 105 this.transform = fnt.getTransform(); 106 this.peer = (FontPeerImpl) fnt.getPeer(); 107 108 gvShapes = new Shape[len]; 109 110 // !! As pointed in API documentation for the 111 // getGlyphPosisitions(int index,int numEntries, float[] positionReturn) 112 // and getGlyphPosition(int index) methods, if the index is equals to 113 // the number of glyphs the position after the last glyph must be 114 // returned, thus there are n+1 positions and last (n+1) position 115 // points to the end of GlyphVector. 116 117 logicalPositions = new float[(len+1)<<1]; 118 visualPositions = new float[(len+1)<<1]; 119 defaultPositions = new float[(len+1)<<1]; 120 121 glsTransforms = new AffineTransform[len]; 122 123 this.charVector = chars; 124 this.vectorFRC = frc; 125 //LineMetricsImpl lmImpl = (LineMetricsImpl)peer.getLineMetrics(); 126 127 LineMetricsImpl lmImpl = (LineMetricsImpl)fnt.getLineMetrics(String.valueOf(chars), frc); 128 129 this.ascent = lmImpl.getAscent(); 130 this.height = lmImpl.getHeight(); 131 this.leading = lmImpl.getLeading(); 132 this.descent = lmImpl.getDescent(); 133 this.layoutFlags = flags; 134 135 if ((flags & Font.LAYOUT_RIGHT_TO_LEFT) != 0){ 136 char vector[] = new char[len]; 137 for(int i=0; i < len; i++){ 138 vector[i] = chars[len-i-1]; 139 } 140 this.vector = peer.getGlyphs(vector); 141 142 } else { 143 this.vector = peer.getGlyphs(chars); 144 } 145 146 this.glsTransforms = new AffineTransform[len]; 147 148 setDefaultPositions(); 149 performDefaultLayout(); 150 } 151 152 /** 153 * Creates new CommonGlyphVector object from the specified parameters. 154 * Layout flags set to default. 155 * 156 * @param chars an array of chars 157 * @param frc FontRenderContext object 158 * @param fnt Font object 159 */ 160 public CommonGlyphVector(char[] chars, FontRenderContext frc, Font fnt) { 161 this(chars, frc, fnt, 0); 162 } 163 164 /** 165 * Creates new CommonGlyphVector object from the specified parameters. 166 * Layout flags set to default. 167 * 168 * @param str specified string 169 * @param frc FontRenderContext object 170 * @param fnt Font object 171 */ 172 public CommonGlyphVector(String str, FontRenderContext frc, Font fnt) { 173 this(str.toCharArray(), frc, fnt, 0); 174 } 175 176 /** 177 * Creates new CommonGlyphVector object from the specified parameters. 178 * 179 * @param str specified string 180 * @param frc FontRenderContext object 181 * @param fnt Font object 182 * @param flags layout flags 183 */ 184 public CommonGlyphVector(String str, FontRenderContext frc, Font fnt, int flags) { 185 this(str.toCharArray(), frc, fnt, flags); 186 } 187 188 /** 189 * Set array of logical positions of the glyphs to 190 * default with their default advances and height. 191 */ 192 void setDefaultPositions(){ 193 int len = getNumGlyphs(); 194 195 // First [x,y] is set into [0,0] position 196 // for this reason start index is 1 197 for (int i=1; i <= len; i++ ){ 198 int idx = i << 1; 199 float advanceX = vector[i-1].getGlyphPointMetrics().getAdvanceX(); 200 float advanceY = vector[i-1].getGlyphPointMetrics().getAdvanceY(); 201 202 defaultPositions[idx] = defaultPositions[idx-2] + advanceX; 203 defaultPositions[idx+1] = defaultPositions[idx-1] + advanceY; 204 205 } 206 transform.transform(defaultPositions, 0, logicalPositions, 0, getNumGlyphs()+1); 207 208 } 209 210 /** 211 * Returnes the pixel bounds of this GlyphVector rendered at the 212 * specified x,y location with the given FontRenderContext. 213 * 214 * @param frc a FontRenderContext that is used 215 * @param x specified x coordinate value 216 * @param y specified y coordinate value 217 * @return a Rectangle that bounds pixels of this GlyphVector 218 */ 219 @Override 220 public Rectangle getPixelBounds(FontRenderContext frc, float x, float y) { 221 222 double xM, yM, xm, ym; 223 224 double minX = 0; 225 double minY = 0; 226 double maxX = 0; 227 double maxY = 0; 228 229 for (int i = 0; i < this.getNumGlyphs(); i++) { 230 Rectangle glyphBounds = this.getGlyphPixelBounds(i, frc, 0, 0); 231 xm = glyphBounds.getMinX(); 232 ym = glyphBounds.getMinY(); 233 xM = glyphBounds.getMaxX(); 234 yM = glyphBounds.getMaxY(); 235 236 if (i == 0) { 237 minX = xm; 238 minY = ym; 239 maxX = xM; 240 maxY = yM; 241 } 242 243 if (minX > xm) { 244 minX = xm; 245 } 246 if (minY > ym) { 247 minY = ym; 248 } 249 if (maxX < xM) { 250 maxX = xM; 251 } 252 if (maxY < yM) { 253 maxY = yM; 254 } 255 } 256 return new Rectangle((int)(minX + x), (int)(minY + y), (int)(maxX - minX), (int)(maxY - minY)); 257 258 } 259 260 /** 261 * Returns the visual bounds of this GlyphVector. 262 * The visual bounds is the bounds of the total outline of 263 * this GlyphVector. 264 * @return a Rectangle2D that id the visual bounds of this GlyphVector 265 */ 266 @Override 267 public Rectangle2D getVisualBounds() { 268 float xM, yM, xm, ym; 269 float minX = 0; 270 float minY = 0; 271 float maxX = 0; 272 float maxY = 0; 273 boolean firstIteration = true; 274 275 for (int i = 0; i < this.getNumGlyphs(); i++) { 276 Rectangle2D bounds = this.getGlyphVisualBounds(i).getBounds2D(); 277 if (bounds.getWidth() == 0){ 278 continue; 279 } 280 xm = (float)bounds.getX(); 281 ym = (float)bounds.getY(); 282 283 xM = (float)(xm + bounds.getWidth()); 284 285 yM = ym + (float) bounds.getHeight(); 286 287 if (firstIteration) { 288 minX = xm; 289 minY = ym; 290 maxX = xM; 291 maxY = yM; 292 firstIteration = false; 293 } else { 294 if (minX > xm) { 295 minX = xm; 296 } 297 if (minY > ym) { 298 minY = ym; 299 } 300 if (maxX < xM) { 301 maxX = xM; 302 } 303 if (maxY < yM) { 304 maxY = yM; 305 } 306 307 } 308 } 309 310 return (this.getNumGlyphs() != 0) ? new Rectangle2D.Float(minX, minY, 311 (maxX - minX), (maxY - minY)) : null; 312 } 313 314 /** 315 * Sets new position to the specified glyph. 316 */ 317 @Override 318 public void setGlyphPosition(int glyphIndex, Point2D newPos) { 319 if ((glyphIndex > vector.length) || (glyphIndex < 0)) { 320 // awt.43=glyphIndex is out of vector's limits 321 throw new IndexOutOfBoundsException(Messages.getString("awt.43")); //$NON-NLS-1$ 322 } 323 float x = (float)newPos.getX(); 324 float y = (float)newPos.getY(); 325 int index = glyphIndex << 1; 326 327 if ((x != visualPositions[index]) || (y != visualPositions[index + 1])){ 328 visualPositions[index] = x; 329 visualPositions[index+1] = y; 330 layoutFlags = layoutFlags | FLAG_HAS_POSITION_ADJUSTMENTS; 331 } 332 333 } 334 335 /** 336 * Returns the position of the specified glyph relative to the origin of 337 * this GlyphVector 338 * @return a Point2D that the origin of the glyph with specified index 339 */ 340 @Override 341 public Point2D getGlyphPosition(int glyphIndex) { 342 if ((glyphIndex > vector.length) || (glyphIndex < 0)) { 343 // awt.43=glyphIndex is out of vector's limits 344 throw new IndexOutOfBoundsException(Messages.getString("awt.43")); //$NON-NLS-1$ 345 } 346 int index = glyphIndex << 1; 347 Point2D pos = new Point2D.Float(visualPositions[index], visualPositions[index+1]); 348 349 // For last position we don't have to transform !! 350 if(glyphIndex==vector.length){ 351 return pos; 352 } 353 354 AffineTransform at = getGlyphTransform(glyphIndex); 355 if ((at == null) || (at.isIdentity())){ 356 return pos; 357 } 358 359 pos.setLocation(pos.getX() + at.getTranslateX(), pos.getY() + at.getTranslateY()); 360 361 return pos; 362 } 363 364 /** 365 * Sets new transform to the specified glyph. 366 * 367 * @param glyphIndex specified index of the glyph 368 * @param trans AffineTransform of the glyph with specified index 369 */ 370 @Override 371 public void setGlyphTransform(int glyphIndex, AffineTransform trans) { 372 if ((glyphIndex >= vector.length) || (glyphIndex < 0)) { 373 // awt.43=glyphIndex is out of vector's limits 374 throw new IndexOutOfBoundsException(Messages.getString("awt.43")); //$NON-NLS-1$ 375 } 376 377 if ((trans == null) || (trans.isIdentity())) { 378 glsTransforms[glyphIndex] = null; 379 } else { 380 glsTransforms[glyphIndex] = new AffineTransform(trans); 381 layoutFlags = layoutFlags | FLAG_HAS_TRANSFORMS; 382 } 383 } 384 385 /** 386 * Returns the affine transform of the specified glyph. 387 * 388 * @param glyphIndex specified index of the glyph 389 * @return an AffineTransform of the glyph with specified index 390 */ 391 @Override 392 public AffineTransform getGlyphTransform(int glyphIndex) { 393 if ((glyphIndex >= this.vector.length) || (glyphIndex < 0)) { 394 // awt.43=glyphIndex is out of vector's limits 395 throw new IndexOutOfBoundsException(Messages.getString("awt.43")); //$NON-NLS-1$ 396 } 397 return this.glsTransforms[glyphIndex]; 398 } 399 400 /** 401 * Returns the metrics of the specified glyph. 402 * 403 * @param glyphIndex specified index of the glyph 404 */ 405 @Override 406 public GlyphMetrics getGlyphMetrics(int glyphIndex) { 407 408 if ((glyphIndex < 0) || ((glyphIndex) >= this.getNumGlyphs())) { 409 // awt.43=glyphIndex is out of vector's limits 410 throw new IndexOutOfBoundsException(Messages.getString("awt.43")); //$NON-NLS-1$ 411 } 412 // TODO: is there a sence in GlyphMetrics 413 // if certain glyph or Font has a transform?? 414 return this.vector[glyphIndex].getGlyphMetrics(); 415 } 416 417 /** 418 * Returns a justification information for the glyph with specified glyph 419 * index. 420 * @param glyphIndex index of a glyph which GlyphJustificationInfo is to be 421 * received 422 * @return a GlyphJustificationInfo object that contains glyph justification 423 * properties of the specified glyph 424 */ 425 @Override 426 public GlyphJustificationInfo getGlyphJustificationInfo(int glyphIndex) { 427 // TODO : Find out the source of Justification info 428 if (true) { 429 throw new RuntimeException("Method is not implemented"); //$NON-NLS-1$ 430 } 431 return null; 432 } 433 434 /** 435 * Returns the FontRenderContext parameter of this GlyphVector. 436 */ 437 @Override 438 public FontRenderContext getFontRenderContext() { 439 return this.vectorFRC; 440 } 441 442 /** 443 * Returns the visual bounds of the specified glyph. 444 * 445 * @param glyphIndex specified index of the glyph 446 */ 447 @Override 448 public Shape getGlyphVisualBounds(int glyphIndex) { 449 if ((glyphIndex < 0) || (glyphIndex >= this.getNumGlyphs())) { 450 // awt.43=glyphIndex is out of vector's limits 451 throw new IndexOutOfBoundsException(Messages.getString("awt.43")); //$NON-NLS-1$ 452 } 453 454 int idx = glyphIndex << 1; 455 456 AffineTransform fontTransform = this.transform; 457 double xOffs = fontTransform.getTranslateX(); 458 double yOffs = fontTransform.getTranslateY(); 459 460 if (vector[glyphIndex].getWidth() == 0){ 461 return new Rectangle2D.Float((float)xOffs, (float)yOffs, 0, 0); 462 } 463 464 AffineTransform at = AffineTransform.getTranslateInstance(xOffs, yOffs); 465 AffineTransform glyphTransform = getGlyphTransform(glyphIndex); 466 467 if (transform.isIdentity() && ((glyphTransform == null) || glyphTransform.isIdentity())){ 468 Rectangle2D blackBox = vector[glyphIndex].getGlyphMetrics().getBounds2D(); 469 at.translate(visualPositions[idx], visualPositions[idx+1]); 470 return(at.createTransformedShape(blackBox)); 471 } 472 473 GeneralPath shape = (GeneralPath)this.getGlyphOutline(glyphIndex); 474 shape.transform(at); 475 return shape.getBounds2D(); 476 } 477 478 /** 479 * Returnes the pixel bounds of the specified glyph within GlyphVector 480 * rendered at the specified x,y location. 481 * 482 * @param glyphIndex index of the glyph 483 * @param frc a FontRenderContext that is used 484 * @param x specified x coordinate value 485 * @param y specified y coordinate value 486 * @return a Rectangle that bounds pixels of the specified glyph 487 */ 488 @Override 489 public Rectangle getGlyphPixelBounds(int glyphIndex, FontRenderContext frc, 490 float x, float y) { 491 // TODO : need to be implemented with FontRenderContext 492 if ((glyphIndex < 0) || (glyphIndex >= this.getNumGlyphs())) { 493 // awt.43=glyphIndex is out of vector's limits 494 throw new IndexOutOfBoundsException(Messages.getString("awt.43")); //$NON-NLS-1$ 495 } 496 497 int idx = glyphIndex << 1; 498 499 if (vector[glyphIndex].getWidth() == 0){ 500 AffineTransform fontTransform = this.transform; 501 double xOffs = x + visualPositions[idx] + fontTransform.getTranslateX(); 502 double yOffs = y + visualPositions[idx+1] + fontTransform.getTranslateY(); 503 return new Rectangle((int)xOffs, (int)yOffs, 0, 0); 504 } 505 506 GeneralPath shape = (GeneralPath)this.getGlyphOutline(glyphIndex); 507 508 AffineTransform at = AffineTransform.getTranslateInstance(x, y); 509 510 if (frc != null){ 511 at.concatenate(frc.getTransform()); 512 } 513 514 shape.transform(at); 515 516 Rectangle bounds = shape.getBounds(); 517 return new Rectangle((int)bounds.getX(), (int)bounds.getY(), 518 (int)bounds.getWidth()-1, (int)bounds.getHeight()-1); 519 } 520 521 /** 522 * Returns a Shape that encloses specified glyph. 523 * 524 * @param glyphIndex specified index of the glyph 525 */ 526 @Override 527 public Shape getGlyphOutline(int glyphIndex) { 528 if ((glyphIndex < 0) || (glyphIndex >= this.getNumGlyphs())) { 529 // awt.43=glyphIndex is out of vector's limits 530 throw new IndexOutOfBoundsException(Messages.getString("awt.43")); //$NON-NLS-1$ 531 } 532 533 if (gvShapes[glyphIndex] == null) { 534 gvShapes[glyphIndex] = vector[glyphIndex].getShape(); 535 } 536 537 GeneralPath gp = (GeneralPath)((GeneralPath)gvShapes[glyphIndex]).clone(); 538 539 /* Applying GlyphVector font transform */ 540 AffineTransform at = (AffineTransform)this.transform.clone(); 541 542 /* Applying Glyph transform */ 543 AffineTransform glyphAT = getGlyphTransform(glyphIndex); 544 if (glyphAT != null){ 545 at.preConcatenate(glyphAT); 546 } 547 548 int idx = glyphIndex << 1; 549 550 gp.transform(at); 551 gp.transform(AffineTransform.getTranslateInstance(visualPositions[idx], visualPositions[idx+1])); 552 return gp; 553 } 554 555 556 /** 557 * Returns a Shape that is the outline representation of this GlyphVector 558 * rendered at the specified x,y coordinates. 559 * 560 * @param x specified x coordinate value 561 * @param y specified y coordinate value 562 * @return a Shape object that is the outline of this GlyphVector 563 * at the specified coordinates. 564 */ 565 @Override 566 public Shape getOutline(float x, float y) { 567 GeneralPath gp = new GeneralPath(GeneralPath.WIND_EVEN_ODD); 568 for (int i = 0; i < this.vector.length; i++) { 569 GeneralPath outline = (GeneralPath)getGlyphOutline(i); 570 571 /* Applying translation to actual visual bounds */ 572 outline.transform(AffineTransform.getTranslateInstance(x, y)); 573 gp.append(outline, false); 574 } 575 576 return gp; 577 } 578 579 /** 580 * Returns a Shape that is the outline representation of this GlyphVector. 581 * 582 * @return a Shape object that is the outline of this GlyphVector 583 */ 584 @Override 585 public Shape getOutline() { 586 return this.getOutline(0, 0); 587 } 588 589 /** 590 * Returns an array of glyphcodes for the specified glyphs. 591 * 592 * @param beginGlyphIndex the start index 593 * @param numEntries the number of glyph codes to get 594 * @param codeReturn the array that receives glyph codes' values 595 * @return an array that receives glyph codes' values 596 */ 597 @Override 598 public int[] getGlyphCodes(int beginGlyphIndex, int numEntries, 599 int[] codeReturn) { 600 601 if ((beginGlyphIndex < 0) || ((numEntries + beginGlyphIndex) > this.getNumGlyphs())) { 602 // awt.44=beginGlyphIndex is out of vector's range 603 throw new IndexOutOfBoundsException(Messages.getString("awt.44")); //$NON-NLS-1$ 604 } 605 606 if (numEntries < 0) { 607 // awt.45=numEntries is out of vector's range 608 throw new IllegalArgumentException(Messages.getString("awt.45")); //$NON-NLS-1$ 609 } 610 611 if (codeReturn == null) { 612 codeReturn = new int[numEntries]; 613 } 614 615 for (int i = beginGlyphIndex; i < beginGlyphIndex + numEntries; i++) { 616 codeReturn[i-beginGlyphIndex] = this.vector[i].getGlyphCode(); 617 } 618 619 return codeReturn; 620 } 621 622 /** 623 * Returns an array of numEntries character indices for the specified glyphs. 624 * 625 * @param beginGlyphIndex the start index 626 * @param numEntries the number of glyph codes to get 627 * @param codeReturn the array that receives glyph codes' values 628 * @return an array that receives glyph char indices 629 */ 630 @Override 631 public int[] getGlyphCharIndices(int beginGlyphIndex, int numEntries, 632 int[] codeReturn) { 633 if ((beginGlyphIndex < 0) || (beginGlyphIndex >= this.getNumGlyphs())) { 634 // awt.44=beginGlyphIndex is out of vector's range 635 throw new IllegalArgumentException(Messages.getString("awt.44")); //$NON-NLS-1$ 636 } 637 638 if ((numEntries < 0) 639 || ((numEntries + beginGlyphIndex) > this.getNumGlyphs())) { 640 // awt.45=numEntries is out of vector's range 641 throw new IllegalArgumentException(Messages.getString("awt.45")); //$NON-NLS-1$ 642 } 643 644 if (codeReturn == null) { 645 codeReturn = new int[numEntries]; 646 } 647 648 for (int i = 0; i < numEntries; i++) { 649 codeReturn[i] = this.getGlyphCharIndex(i + beginGlyphIndex); 650 } 651 return codeReturn; 652 } 653 654 /** 655 * Returns an array of numEntries glyphs positions from beginGlyphIndex 656 * glyph in Glyph Vector. 657 * 658 * @param beginGlyphIndex the start index 659 * @param numEntries the number of glyph codes to get 660 * @param positionReturn the array that receives glyphs' positions 661 * @return an array of floats that receives glyph char indices 662 */ 663 @Override 664 public float[] getGlyphPositions(int beginGlyphIndex, int numEntries, 665 float[] positionReturn) { 666 667 int len = (this.getNumGlyphs()+1) << 1; 668 beginGlyphIndex *= 2; 669 numEntries *= 2; 670 671 if ((beginGlyphIndex < 0) || ((numEntries + beginGlyphIndex) > len)) { 672 // awt.44=beginGlyphIndex is out of vector's range 673 throw new IndexOutOfBoundsException(Messages.getString("awt.44")); //$NON-NLS-1$ 674 } 675 676 if (numEntries < 0) { 677 // awt.45=numEntries is out of vector's range 678 throw new IllegalArgumentException(Messages.getString("awt.45")); //$NON-NLS-1$ 679 } 680 681 if (positionReturn == null) { 682 positionReturn = new float[numEntries]; 683 } 684 685 System.arraycopy(visualPositions, beginGlyphIndex, positionReturn, 0, numEntries); 686 687 return positionReturn; 688 } 689 690 /** 691 * Set numEntries elements of the visualPositions array from beginGlyphIndex 692 * of numEntries glyphs positions from beginGlyphIndex glyph in Glyph Vector. 693 * 694 * @param beginGlyphIndex the start index 695 * @param numEntries the number of glyph codes to get 696 * @param setPositions the array of positions to set 697 */ 698 public void setGlyphPositions(int beginGlyphIndex, int numEntries, 699 float[] setPositions) { 700 701 int len = (this.getNumGlyphs()+1) << 1; 702 beginGlyphIndex *= 2; 703 numEntries *= 2; 704 705 if ((beginGlyphIndex < 0) || ((numEntries + beginGlyphIndex) > len)) { 706 // awt.44=beginGlyphIndex is out of vector's range 707 throw new IndexOutOfBoundsException(Messages.getString("awt.44")); //$NON-NLS-1$ 708 } 709 710 if (numEntries < 0) { 711 // awt.45=numEntries is out of vector's range 712 throw new IllegalArgumentException(Messages.getString("awt.45")); //$NON-NLS-1$ 713 } 714 715 System.arraycopy(setPositions, 0, visualPositions, beginGlyphIndex, numEntries); 716 layoutFlags = layoutFlags & FLAG_HAS_POSITION_ADJUSTMENTS; 717 718 } 719 720 /** 721 * Set elements of the visualPositions array. 722 * 723 * @param setPositions the array of positions to set 724 */ 725 public void setGlyphPositions(float[] setPositions) { 726 727 int len = (this.getNumGlyphs()+1) << 1; 728 if (len != setPositions.length){ 729 // awt.46=length of setPositions array differs from the length of positions array 730 throw new IllegalArgumentException(Messages.getString("awt.46")); //$NON-NLS-1$ 731 } 732 733 System.arraycopy(setPositions, 0, visualPositions, 0, len); 734 layoutFlags = layoutFlags & FLAG_HAS_POSITION_ADJUSTMENTS; 735 736 } 737 738 739 /** 740 * Returns glyph code of the specified glyph. 741 * 742 * @param glyphIndex specified index of the glyph 743 */ 744 @Override 745 public int getGlyphCode(int glyphIndex) { 746 if (glyphIndex >= this.vector.length || glyphIndex < 0) { 747 // awt.43=glyphIndex is out of vector's limits 748 throw new IndexOutOfBoundsException(Messages.getString("awt.43")); //$NON-NLS-1$ 749 } 750 return this.vector[glyphIndex].getGlyphCode(); 751 } 752 753 /** 754 * Returns character index of the specified glyph. 755 * 756 * @param glyphIndex specified index of the glyph 757 */ 758 @Override 759 public int getGlyphCharIndex(int glyphIndex) { 760 761 if ((glyphIndex < 0) || (glyphIndex >= this.getNumGlyphs())) { 762 // awt.43=glyphIndex is out of vector's limits 763 throw new IllegalArgumentException(Messages.getString("awt.43")); //$NON-NLS-1$ 764 } 765 766 if ((this.layoutFlags & Font.LAYOUT_RIGHT_TO_LEFT) != 0) { 767 return this.charVector.length - glyphIndex - 1; 768 } 769 770 return glyphIndex; 771 } 772 773 /** 774 * Returns a character value of the specified glyph. 775 * 776 * @param glyphIndex specified index of the glyph 777 */ 778 public char getGlyphChar(int glyphIndex) { 779 780 if ((glyphIndex < 0) || (glyphIndex >= this.getNumGlyphs())) { 781 // awt.43=glyphIndex is out of vector's limits 782 throw new IllegalArgumentException(Messages.getString("awt.43")); //$NON-NLS-1$ 783 } 784 return this.charVector[glyphIndex]; 785 } 786 787 /** 788 * Assigns default positions to each glyph in this GlyphVector. 789 */ 790 @Override 791 public void performDefaultLayout() { 792 793 System.arraycopy(logicalPositions, 0, visualPositions, 0, logicalPositions.length); 794 795 // Set position changes flag to zero 796 clearLayoutFlags(GlyphVector.FLAG_HAS_POSITION_ADJUSTMENTS); 797 } 798 799 /** 800 * Returns the number of glyphs in this Glyph Vector 801 */ 802 @Override 803 public int getNumGlyphs() { 804 return vector.length; 805 } 806 807 /** 808 * Returns the logical bounds of this GlyphVector 809 */ 810 @Override 811 public Rectangle2D getLogicalBounds(){ 812 // XXX: for transforms where an angle between basis vectors is not 90 degrees 813 // Rectanlge2D class doesn't fit as Logical bounds. For this reason we use 814 // only non-transformed bounds!! 815 816 float x = visualPositions[0]; 817 float width = visualPositions[visualPositions.length-2]; 818 819 double scaleY = transform.getScaleY(); 820 821 Rectangle2D bounds = new Rectangle2D.Float(x, (float)((-this.ascent-this.leading)*scaleY), width, (float)(this.height*scaleY)); 822 return bounds; 823 } 824 825 826 /** 827 * Checks whether given GlyphVector equals to this GlyphVector. 828 * @param glyphVector GlyphVector object to compare 829 */ 830 @Override 831 public boolean equals(GlyphVector glyphVector){ 832 if (glyphVector == this){ 833 return true; 834 } 835 836 if (glyphVector != null) { 837 838 if (!(glyphVector.getFontRenderContext().equals(this.vectorFRC) && 839 glyphVector.getFont().equals(this.font))){ 840 return false; 841 } 842 843 try { 844 boolean eq = true; 845 for (int i = 0; i < getNumGlyphs(); i++) { 846 847 int idx = i*2; 848 eq = (((CommonGlyphVector)glyphVector).visualPositions[idx] == this.visualPositions[idx]) && 849 (((CommonGlyphVector)glyphVector).visualPositions[idx+1] == this.visualPositions[idx+1]) && 850 (glyphVector.getGlyphCharIndex(i) == this.getGlyphCharIndex(i)); 851 852 if (eq){ 853 AffineTransform trans = glyphVector.getGlyphTransform(i); 854 if (trans == null){ 855 eq = (this.glsTransforms[i] == null); 856 }else{ 857 eq = this.glsTransforms[i].equals(trans); 858 } 859 } 860 861 if (!eq){ 862 return false; 863 } 864 } 865 866 return eq; 867 } catch (ClassCastException e) { 868 } 869 } 870 871 return false; 872 } 873 874 875 /** 876 * Returns flags describing the state of the GlyphVector. 877 */ 878 @Override 879 public int getLayoutFlags() { 880 return layoutFlags; 881 } 882 883 /** 884 * Returns char with the specified index. 885 * 886 * @param index specified index of the char 887 * 888 */ 889 public char getChar(int index) { 890 return this.charVector[index]; 891 892 } 893 894 /** 895 * Clear desired flags in layout flags describing the state. 896 * 897 * @param clearFlags flags mask to clear 898 */ 899 900 private void clearLayoutFlags(int clearFlags){ 901 layoutFlags &= ~clearFlags; 902 } 903 904 /** 905 * Returns the logical bounds of the specified glyph within this CommonGlyphVector. 906 * 907 * @param glyphIndex index of the glyph to get it's logical bounds 908 * @return logical bounds of the specified glyph 909 */ 910 @Override 911 public Shape getGlyphLogicalBounds(int glyphIndex){ 912 if ((glyphIndex < 0) || (glyphIndex >= this.getNumGlyphs())){ 913 // awt.43=glyphIndex is out of vector's limits 914 throw new IndexOutOfBoundsException(Messages.getString("awt.43")); //$NON-NLS-1$ 915 } 916 Glyph glyph = this.vector[glyphIndex]; 917 918 float x0 = visualPositions[glyphIndex*2]; 919 float y0 = visualPositions[glyphIndex*2+1]; 920 float advanceX = glyph.getGlyphPointMetrics().getAdvanceX(); 921 922 GeneralPath gp = new GeneralPath(); 923 gp.moveTo(0, -ascent - leading); 924 gp.lineTo(advanceX ,-ascent - leading); 925 gp.lineTo(advanceX, descent); 926 gp.lineTo(0, descent); 927 gp.lineTo(0, -ascent - leading); 928 gp.closePath(); 929 930 /* Applying GlyphVector font transform */ 931 AffineTransform at = (AffineTransform)this.transform.clone(); 932 933 /* Applying Glyph transform */ 934 AffineTransform glyphTransform = getGlyphTransform(glyphIndex); 935 if (glyphTransform != null){ 936 at.concatenate(glyphTransform); 937 } 938 939 /* Applying translation to actual visual bounds */ 940 at.preConcatenate(AffineTransform.getTranslateInstance(x0, y0)); 941 gp.transform(at); 942 return gp; 943 } 944 945 /** 946 * Returns the Font parameter of this GlyphVector 947 */ 948 @Override 949 public Font getFont(){ 950 return this.font; 951 } 952 953 954}