| 1 7 8 12 13 package java.awt.font; 14 15 import java.awt.Font ; 16 import java.awt.Graphics2D ; 17 import java.awt.Shape ; 18 19 import java.awt.geom.Rectangle2D ; 20 import java.awt.geom.AffineTransform ; 21 import java.awt.geom.GeneralPath ; 22 23 import java.awt.im.InputMethodHighlight ; 24 25 import java.text.CharacterIterator ; 26 import java.text.AttributedCharacterIterator ; 27 import java.text.Annotation ; 28 import java.text.Bidi ; 29 30 import java.util.Map ; 31 import java.util.Hashtable ; 32 33 import sun.font.BidiUtils; 34 import sun.font.CoreMetrics; 35 import sun.font.Decoration; 36 import sun.font.FontLineMetrics; 37 import sun.font.FontResolver; 38 import sun.font.GraphicComponent; 39 import sun.font.TextLabelFactory; 40 import sun.font.TextLineComponent; 41 import sun.text.CodePointIterator; 42 43 final class TextLine { 44 45 static final class TextLineMetrics { 46 public final float ascent; 47 public final float descent; 48 public final float leading; 49 public final float advance; 50 51 public TextLineMetrics(float ascent, 52 float descent, 53 float leading, 54 float advance) { 55 this.ascent = ascent; 56 this.descent = descent; 57 this.leading = leading; 58 this.advance = advance; 59 } 60 } 61 62 private TextLineComponent[] fComponents; 63 private float[] fBaselineOffsets; 64 private int[] fComponentVisualOrder; private float[] locs; private char[] fChars; 67 private int fCharsStart; 68 private int fCharsLimit; 69 private int[] fCharVisualOrder; private int[] fCharLogicalOrder; private byte[] fCharLevels; private boolean fIsDirectionLTR; 73 74 private TextLineMetrics fMetrics = null; 76 public TextLine(TextLineComponent[] components, 77 float[] baselineOffsets, 78 char[] chars, 79 int charsStart, 80 int charsLimit, 81 int[] charLogicalOrder, 82 byte[] charLevels, 83 boolean isDirectionLTR) { 84 85 int[] componentVisualOrder = computeComponentOrder(components, 86 charLogicalOrder); 87 88 fComponents = components; 89 fBaselineOffsets = baselineOffsets; 90 fComponentVisualOrder = componentVisualOrder; 91 fChars = chars; 92 fCharsStart = charsStart; 93 fCharsLimit = charsLimit; 94 fCharLogicalOrder = charLogicalOrder; 95 fCharLevels = charLevels; 96 fIsDirectionLTR = isDirectionLTR; 97 checkCtorArgs(); 98 99 init(); 100 } 101 102 private void checkCtorArgs() { 103 104 int checkCharCount = 0; 105 for (int i=0; i < fComponents.length; i++) { 106 checkCharCount += fComponents[i].getNumCharacters(); 107 } 108 109 if (checkCharCount != this.characterCount()) { 110 throw new IllegalArgumentException ("Invalid TextLine! " + 111 "char count is different from " + 112 "sum of char counts of components."); 113 } 114 } 115 116 private void init() { 117 118 121 float ascent = 0; 122 float descent = 0; 123 float leading = 0; 124 float advance = 0; 125 126 float maxGraphicHeight = 0; 128 float maxGraphicHeightWithLeading = 0; 129 130 TextLineComponent tlc; 132 boolean fitTopAndBottomGraphics = false; 133 134 for (int i = 0; i < fComponents.length; i++) { 135 tlc = fComponents[i]; 136 137 CoreMetrics cm = tlc.getCoreMetrics(); 138 byte baseline = (byte)cm.baselineIndex; 139 140 if (baseline >= 0) { 141 float baselineOffset = fBaselineOffsets[baseline]; 142 143 ascent = Math.max(ascent, -baselineOffset + cm.ascent); 144 145 float gd = baselineOffset + cm.descent; 146 descent = Math.max(descent, gd); 147 148 leading = Math.max(leading, gd + cm.leading); 149 } 150 else { 151 fitTopAndBottomGraphics = true; 152 float graphicHeight = cm.ascent + cm.descent; 153 float graphicHeightWithLeading = graphicHeight + cm.leading; 154 maxGraphicHeight = Math.max(maxGraphicHeight, graphicHeight); 155 maxGraphicHeightWithLeading = Math.max(maxGraphicHeightWithLeading, 156 graphicHeightWithLeading); 157 } 158 } 159 160 if (fitTopAndBottomGraphics) { 161 if (maxGraphicHeight > ascent + descent) { 162 descent = maxGraphicHeight - ascent; 163 } 164 if (maxGraphicHeightWithLeading > ascent + leading) { 165 leading = maxGraphicHeightWithLeading - ascent; 166 } 167 } 168 169 leading -= descent; 170 171 174 if (fitTopAndBottomGraphics) { 175 fBaselineOffsets = new float[] { 178 fBaselineOffsets[0], 179 fBaselineOffsets[1], 180 fBaselineOffsets[2], 181 descent, 182 -ascent 183 }; 184 } 185 186 float x = 0; 187 float y = 0; 188 CoreMetrics pcm = null; 189 190 locs = new float[fComponents.length * 2 + 2]; 191 192 for (int i = 0, n = 0; i < fComponents.length; ++i, n += 2) { 193 int vi = fComponentVisualOrder == null ? i : fComponentVisualOrder[i]; 194 195 tlc = fComponents[vi]; 196 CoreMetrics cm = tlc.getCoreMetrics(); 197 198 if ((pcm != null) && 199 (pcm.italicAngle != 0 || cm.italicAngle != 0) && (pcm.italicAngle != cm.italicAngle || 201 pcm.baselineIndex != cm.baselineIndex || 202 pcm.ssOffset != cm.ssOffset)) { 203 204 209 float pb = pcm.effectiveBaselineOffset(fBaselineOffsets); 211 float pa = pb - pcm.ascent; 212 float pd = pb + pcm.descent; 213 pb += pcm.ssOffset; 214 215 float cb = cm.effectiveBaselineOffset(fBaselineOffsets); 216 float ca = cb - cm.ascent; 217 float cd = cb + cm.descent; 218 cb += cm.ssOffset; 219 220 float a = Math.max(pa, ca); 221 float d = Math.min(pd, cd); 222 223 float pax = pcm.italicAngle * (pb - a); 225 float pdx = pcm.italicAngle * (pb - d); 226 227 float cax = cm.italicAngle * (cb - a); 228 float cdx = cm.italicAngle * (cb - d); 229 230 float dax = pax - cax; 232 float ddx = pdx - cdx; 233 float dx = Math.max(dax, ddx); 234 235 x += dx; 236 y = cb; 237 } else { 238 y = cm.effectiveBaselineOffset(fBaselineOffsets) + cm.ssOffset; 240 } 241 242 locs[n] = x; 243 locs[n+1] = y; 244 245 x += tlc.getAdvance(); 246 pcm = cm; 247 } 248 249 if (pcm.italicAngle != 0) { 251 float pb = pcm.effectiveBaselineOffset(fBaselineOffsets); 252 float pa = pb - pcm.ascent; 253 float pd = pb + pcm.descent; 254 pb += pcm.ssOffset; 255 256 float d; 257 if (pcm.italicAngle > 0) { 258 d = pb + pcm.ascent; 259 } else { 260 d = pb - pcm.descent; 261 } 262 d *= pcm.italicAngle; 263 264 x += d; 265 } 266 locs[locs.length - 2] = x; 267 269 advance = x; 271 fMetrics = new TextLineMetrics(ascent, descent, leading, advance); 272 } 273 274 private abstract static class Function { 275 276 abstract float computeFunction(TextLine line, 277 int componentIndex, 278 int indexInArray); 279 } 280 281 private static Function fgPosAdvF = new Function() { 282 float computeFunction(TextLine line, 283 int componentIndex, 284 int indexInArray) { 285 286 TextLineComponent tlc = line.fComponents[componentIndex]; 287 int vi = line.fComponentVisualOrder == null 288 ? componentIndex 289 : line.fComponentVisualOrder[componentIndex]; 290 return line.locs[vi * 2] + tlc.getCharX(indexInArray) + tlc.getCharAdvance(indexInArray); 291 } 292 }; 293 294 private static Function fgAdvanceF = new Function() { 295 296 float computeFunction(TextLine line, 297 int componentIndex, 298 int indexInArray) { 299 300 TextLineComponent tlc = line.fComponents[componentIndex]; 301 return tlc.getCharAdvance(indexInArray); 302 } 303 }; 304 305 private static Function fgXPositionF = new Function() { 306 307 float computeFunction(TextLine line, 308 int componentIndex, 309 int indexInArray) { 310 311 int vi = line.fComponentVisualOrder == null 312 ? componentIndex 313 : line.fComponentVisualOrder[componentIndex]; 314 TextLineComponent tlc = line.fComponents[componentIndex]; 315 return line.locs[vi * 2] + tlc.getCharX(indexInArray); 316 } 317 }; 318 319 private static Function fgYPositionF = new Function() { 320 321 float computeFunction(TextLine line, 322 int componentIndex, 323 int indexInArray) { 324 325 TextLineComponent tlc = line.fComponents[componentIndex]; 326 float charPos = tlc.getCharY(indexInArray); 327 328 331 return charPos + line.getComponentShift(componentIndex); 332 } 333 }; 334 335 public int characterCount() { 336 337 return fCharsLimit - fCharsStart; 338 } 339 340 public boolean isDirectionLTR() { 341 342 return fIsDirectionLTR; 343 } 344 345 public TextLineMetrics getMetrics() { 346 return fMetrics; 347 } 348 349 public int visualToLogical(int visualIndex) { 350 351 if (fCharLogicalOrder == null) { 352 return visualIndex; 353 } 354 355 if (fCharVisualOrder == null) { 356 fCharVisualOrder = BidiUtils.createInverseMap(fCharLogicalOrder); 357 } 358 359 return fCharVisualOrder[visualIndex]; 360 } 361 362 public int logicalToVisual(int logicalIndex) { 363 364 return (fCharLogicalOrder == null)? 365 logicalIndex : fCharLogicalOrder[logicalIndex]; 366 } 367 368 public byte getCharLevel(int logicalIndex) { 369 370 return fCharLevels==null? 0 : fCharLevels[logicalIndex]; 371 } 372 373 public boolean isCharLTR(int logicalIndex) { 374 375 return (getCharLevel(logicalIndex) & 0x1) == 0; 376 } 377 378 public int getCharType(int logicalIndex) { 379 380 return Character.getType(fChars[logicalIndex + fCharsStart]); 381 } 382 383 public boolean isCharSpace(int logicalIndex) { 384 385 return Character.isSpaceChar(fChars[logicalIndex + fCharsStart]); 386 } 387 388 public boolean isCharWhitespace(int logicalIndex) { 389 390 return Character.isWhitespace(fChars[logicalIndex + fCharsStart]); 391 } 392 393 public float getCharAngle(int logicalIndex) { 394 395 return getCoreMetricsAt(logicalIndex).italicAngle; 396 } 397 398 public CoreMetrics getCoreMetricsAt(int logicalIndex) { 399 400 if (logicalIndex < 0) { 401 throw new IllegalArgumentException ("Negative logicalIndex."); 402 } 403 404 if (logicalIndex > fCharsLimit - fCharsStart) { 405 throw new IllegalArgumentException ("logicalIndex too large."); 406 } 407 408 int currentTlc = 0; 409 int tlcStart = 0; 410 int tlcLimit = 0; 411 412 do { 413 tlcLimit += fComponents[currentTlc].getNumCharacters(); 414 if (tlcLimit > logicalIndex) { 415 break; 416 } 417 ++currentTlc; 418 tlcStart = tlcLimit; 419 } while(currentTlc < fComponents.length); 420 421 return fComponents[currentTlc].getCoreMetrics(); 422 } 423 424 public float getCharAscent(int logicalIndex) { 425 426 return getCoreMetricsAt(logicalIndex).ascent; 427 } 428 429 public float getCharDescent(int logicalIndex) { 430 431 return getCoreMetricsAt(logicalIndex).descent; 432 } 433 434 public float getCharShift(int logicalIndex) { 435 436 return getCoreMetricsAt(logicalIndex).ssOffset; 437 } 438 439 private float applyFunctionAtIndex(int logicalIndex, Function f) { 440 441 if (logicalIndex < 0) { 442 throw new IllegalArgumentException ("Negative logicalIndex."); 443 } 444 445 int tlcStart = 0; 446 447 for(int i=0; i < fComponents.length; i++) { 448 449 int tlcLimit = tlcStart + fComponents[i].getNumCharacters(); 450 if (tlcLimit > logicalIndex) { 451 return f.computeFunction(this, i, logicalIndex - tlcStart); 452 } 453 else { 454 tlcStart = tlcLimit; 455 } 456 } 457 458 throw new IllegalArgumentException ("logicalIndex too large."); 459 } 460 461 public float getCharAdvance(int logicalIndex) { 462 463 return applyFunctionAtIndex(logicalIndex, fgAdvanceF); 464 } 465 466 public float getCharXPosition(int logicalIndex) { 467 468 return applyFunctionAtIndex(logicalIndex, fgXPositionF); 469 } 470 471 public float getCharYPosition(int logicalIndex) { 472 473 return applyFunctionAtIndex(logicalIndex, fgYPositionF); 474 } 475 476 public float getCharLinePosition(int logicalIndex) { 477 478 return getCharXPosition(logicalIndex); 479 } 480 481 public float getCharLinePosition(int logicalIndex, boolean leading) { 482 Function f = isCharLTR(logicalIndex) == leading ? fgXPositionF : fgPosAdvF; 483 return applyFunctionAtIndex(logicalIndex, f); 484 } 485 486 public boolean caretAtOffsetIsValid(int offset) { 487 488 if (offset < 0) { 489 throw new IllegalArgumentException ("Negative offset."); 490 } 491 492 int tlcStart = 0; 493 494 for(int i=0; i < fComponents.length; i++) { 495 496 int tlcLimit = tlcStart + fComponents[i].getNumCharacters(); 497 if (tlcLimit > offset) { 498 return fComponents[i].caretAtOffsetIsValid(offset-tlcStart); 499 } 500 else { 501 tlcStart = tlcLimit; 502 } 503 } 504 505 throw new IllegalArgumentException ("logicalIndex too large."); 506 } 507 508 public Rectangle2D getCharBounds(int logicalIndex) { 509 510 if (logicalIndex < 0) { 511 throw new IllegalArgumentException ("Negative logicalIndex."); 512 } 513 514 int tlcStart = 0; 515 516 for (int i=0; i < fComponents.length; i++) { 517 518 int tlcLimit = tlcStart + fComponents[i].getNumCharacters(); 519 if (tlcLimit > logicalIndex) { 520 521 TextLineComponent tlc = fComponents[i]; 522 int indexInTlc = logicalIndex - tlcStart; 523 Rectangle2D chBounds = tlc.getCharVisualBounds(indexInTlc); 524 525 int vi = fComponentVisualOrder == null ? i : fComponentVisualOrder[i]; 526 chBounds.setRect(chBounds.getX() + locs[vi * 2], 527 chBounds.getY() + locs[vi * 2 + 1], 528 chBounds.getWidth(), 529 chBounds.getHeight()); 530 return chBounds; 531 } 532 else { 533 tlcStart = tlcLimit; 534 } 535 } 536 537 throw new IllegalArgumentException ("logicalIndex too large."); 538 } 539 540 private float getComponentShift(int index) { 541 CoreMetrics cm = fComponents[index].getCoreMetrics(); 542 return cm.effectiveBaselineOffset(fBaselineOffsets); 543 } 544 545 public void draw(Graphics2D g2, float x, float y) { 546 547 for (int i = 0, n = 0; i < fComponents.length; i++, n += 2) { 548 int vi = fComponentVisualOrder==null? i : fComponentVisualOrder[i]; 549 TextLineComponent tlc = fComponents[vi]; 550 tlc.draw(g2, locs[n] + x, locs[n+1] + y); 551 } 552 } 553 554 555 556 public Rectangle2D getBounds() { 557 558 float left = Float.MAX_VALUE, right = -Float.MAX_VALUE; 559 float top = Float.MAX_VALUE, bottom = -Float.MAX_VALUE; 560 561 for (int i=0, n = 0; i < fComponents.length; i++, n += 2) { 562 int vi = fComponentVisualOrder==null? i : fComponentVisualOrder[i]; 563 TextLineComponent tlc = fComponents[vi]; 564 565 Rectangle2D tlcBounds = tlc.getVisualBounds(); 566 float x = locs[n]; 567 float y = locs[n+1]; 568 569 left = Math.min(left, x + (float)tlcBounds.getX()); 570 right = Math.max(right, x + (float)tlcBounds.getMaxX()); 571 572 top = Math.min(top, y + (float)tlcBounds.getY()); 573 bottom = Math.max(bottom, y + (float)tlcBounds.getMaxY()); 574 } 575 576 return new Rectangle2D.Float (left, top, right-left, bottom-top); 577 } 578 579 public Rectangle2D getItalicBounds() { 580 581 float left = Float.MAX_VALUE, right = -Float.MAX_VALUE; 582 float top = Float.MAX_VALUE, bottom = -Float.MAX_VALUE; 583 584 for (int i=0, n = 0; i < fComponents.length; i++, n += 2) { 585 int vi = fComponentVisualOrder==null? i : fComponentVisualOrder[i]; 586 TextLineComponent tlc = fComponents[vi]; 587 588 Rectangle2D tlcBounds = tlc.getItalicBounds(); 589 float x = locs[n]; 590 float y = locs[n+1]; 591 592 left = Math.min(left, x + (float)tlcBounds.getX()); 593 right = Math.max(right, x + (float)tlcBounds.getMaxX()); 594 595 top = Math.min(top, y + (float)tlcBounds.getY()); 596 bottom = Math.max(bottom, y + (float)tlcBounds.getMaxY()); 597 } 598 599 return new Rectangle2D.Float (left, top, right-left, bottom-top); 600 } 601 602 public Shape getOutline(AffineTransform tx) { 603 604 GeneralPath dstShape = new GeneralPath (GeneralPath.WIND_NON_ZERO); 605 606 for (int i=0, n = 0; i < fComponents.length; i++, n += 2) { 607 int vi = fComponentVisualOrder==null? i : fComponentVisualOrder[i]; 608 TextLineComponent tlc = fComponents[vi]; 609 610 dstShape.append(tlc.getOutline(locs[n], locs[n+1]), false); 611 } 612 613 if (tx != null) { 614 dstShape.transform(tx); 615 } 616 return dstShape; 617 } 618 619 public int hashCode() { 620 return (fComponents.length << 16) ^ 621 (fComponents[0].hashCode() << 3) ^ (fCharsLimit-fCharsStart); 622 } 623 624 public String toString() { 625 StringBuffer buf = new StringBuffer (); 626 627 for (int i = 0; i < fComponents.length; i++) { 628 buf.append(fComponents[i]); 629 } 630 631 return buf.toString(); 632 } 633 634 640 public static TextLine fastCreateTextLine(FontRenderContext frc, 641 char[] chars, 642 Font font, 643 CoreMetrics lm, 644 Map attributes) { 645 646 boolean isDirectionLTR = true; 647 byte[] levels = null; 648 int[] charsLtoV = null; 649 Bidi bidi = null; 650 int characterCount = chars.length; 651 652 boolean requiresBidi = false; 653 boolean directionKnown = false; 654 byte[] embs = null; 655 if (attributes != null) { 656 try { 657 Boolean runDirection = (Boolean )attributes.get(TextAttribute.RUN_DIRECTION); 658 if (runDirection != null) { 659 directionKnown = true; 660 isDirectionLTR = TextAttribute.RUN_DIRECTION_LTR.equals(runDirection); 661 requiresBidi = !isDirectionLTR; 662 } 663 } 664 catch (ClassCastException e) { 665 } 666 667 try { 668 Integer embeddingLevel = (Integer )attributes.get(TextAttribute.BIDI_EMBEDDING); 669 if (embeddingLevel != null) { 670 int intLevel = embeddingLevel.intValue(); 671 if (intLevel >= -61 && intLevel < 62) { 672 byte level = (byte)intLevel; 673 requiresBidi = true; 674 embs = new byte[characterCount]; 675 for (int i = 0; i < embs.length; ++i) { 676 embs[i] = level; 677 } 678 } 679 } 680 } 681 catch (ClassCastException e) { 682 } 683 } 684 685 if (!requiresBidi) { 686 requiresBidi = Bidi.requiresBidi(chars, 0, chars.length); 687 } 688 689 if (requiresBidi) { 690 int bidiflags = Bidi.DIRECTION_DEFAULT_LEFT_TO_RIGHT; 691 if (directionKnown) { 692 if (isDirectionLTR) { 693 bidiflags = Bidi.DIRECTION_LEFT_TO_RIGHT; 694 } else { 695 bidiflags = Bidi.DIRECTION_RIGHT_TO_LEFT; 696 } 697 } 698 699 bidi = new Bidi (chars, 0, embs, 0, chars.length, bidiflags); 700 if (!bidi.isLeftToRight()) { 701 levels = BidiUtils.getLevels(bidi); 702 int[] charsVtoL = BidiUtils.createVisualToLogicalMap(levels); 703 charsLtoV = BidiUtils.createInverseMap(charsVtoL); 704 isDirectionLTR = bidi.baseIsLeftToRight(); 705 } 706 } 707 708 Decoration decorator; 709 if (attributes != null) { 710 decorator = Decoration.getDecoration(StyledParagraph.addInputMethodAttrs(attributes)); 711 } 712 else { 713 decorator = Decoration.getPlainDecoration(); 714 } 715 int layoutFlags = 0; TextLabelFactory factory = new TextLabelFactory(frc, chars, bidi, layoutFlags); 717 718 TextLineComponent[] components = new TextLineComponent[1]; 719 720 components = createComponentsOnRun(0, chars.length, 721 chars, 722 charsLtoV, levels, 723 factory, font, lm, 724 frc, 725 decorator, 726 components, 727 0); 728 729 int numComponents = components.length; 730 while (components[numComponents-1] == null) { 731 numComponents -= 1; 732 } 733 734 if (numComponents != components.length) { 735 TextLineComponent[] temp = new TextLineComponent[numComponents]; 736 System.arraycopy(components, 0, temp, 0, numComponents); 737 components = temp; 738 } 739 740 return new TextLine (components, lm.baselineOffsets, 741 chars, 0, chars.length, charsLtoV, levels, isDirectionLTR); 742 } 743 744 private static TextLineComponent[] expandArray(TextLineComponent[] orig) { 745 746 TextLineComponent[] newComponents = new TextLineComponent[orig.length + 8]; 747 System.arraycopy(orig, 0, newComponents, 0, orig.length); 748 749 return newComponents; 750 } 751 752 756 public static TextLineComponent[] createComponentsOnRun(int runStart, 757 int runLimit, 758 char[] chars, 759 int[] charsLtoV, 760 byte[] levels, 761 TextLabelFactory factory, 762 Font font, 763 CoreMetrics cm, 764 FontRenderContext frc, 765 Decoration decorator, 766 TextLineComponent[] components, 767 int numComponents) { 768 769 int pos = runStart; 770 do { 771 int chunkLimit = firstVisualChunk(charsLtoV, levels, pos, runLimit); 773 do { 774 int startPos = pos; 775 int lmCount; 776 777 if (cm == null) { 778 LineMetrics lineMetrics = font.getLineMetrics(chars, startPos, chunkLimit, frc); 779 cm = CoreMetrics.get(lineMetrics); 780 lmCount = lineMetrics.getNumChars(); 781 } 782 else { 783 lmCount = (chunkLimit-startPos); 784 } 785 786 TextLineComponent nextComponent = 787 factory.createExtended(font, cm, decorator, startPos, startPos + lmCount); 788 789 ++numComponents; 790 if (numComponents >= components.length) { 791 components = expandArray(components); 792 } 793 794 components[numComponents-1] = nextComponent; 795 796 pos += lmCount; 797 } while (pos < chunkLimit); 798 799 } while (pos < runLimit); 800 801 return components; 802 } 803 804 808 public static TextLineComponent[] getComponents(StyledParagraph styledParagraph, 809 char[] chars, 810 int textStart, 811 int textLimit, 812 int[] charsLtoV, 813 byte[] levels, 814 TextLabelFactory factory) { 815 816 FontRenderContext frc = factory.getFontRenderContext(); 817 818 int numComponents = 0; 819 TextLineComponent[] tempComponents = new TextLineComponent[1]; 820 821 int pos = textStart; 822 do { 823 int runLimit = Math.min(styledParagraph.getRunLimit(pos), textLimit); 824
|