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 825 Decoration decorator = styledParagraph.getDecorationAt(pos); 826 827 Object graphicOrFont = styledParagraph.getFontOrGraphicAt(pos); 828 829 if (graphicOrFont instanceof GraphicAttribute ) { 830 831 GraphicAttribute graphicAttribute = (GraphicAttribute ) graphicOrFont; 832 do { 833 int chunkLimit = firstVisualChunk(charsLtoV, levels, 834 pos, runLimit); 835 836 GraphicComponent nextGraphic = 837 new GraphicComponent(graphicAttribute, decorator, charsLtoV, levels, pos, chunkLimit); 838 pos = chunkLimit; 839 840 ++numComponents; 841 if (numComponents >= tempComponents.length) { 842 tempComponents = expandArray(tempComponents); 843 } 844 845 tempComponents[numComponents-1] = nextGraphic; 846 847 } while(pos < runLimit); 848 } 849 else { 850 Font font = (Font ) graphicOrFont; 851 852 tempComponents = createComponentsOnRun(pos, runLimit, 853 chars, 854 charsLtoV, levels, 855 factory, font, null, 856 frc, 857 decorator, 858 tempComponents, 859 numComponents); 860 pos = runLimit; 861 numComponents = tempComponents.length; 862 while (tempComponents[numComponents-1] == null) { 863 numComponents -= 1; 864 } 865 } 866 867 } while (pos < textLimit); 868 869 TextLineComponent[] components; 870 if (tempComponents.length == numComponents) { 871 components = tempComponents; 872 } 873 else { 874 components = new TextLineComponent[numComponents]; 875 System.arraycopy(tempComponents, 0, components, 0, numComponents); 876 } 877 878 return components; 879 } 880 881 886 public static TextLine createLineFromText(char[] chars, 887 StyledParagraph styledParagraph, 888 TextLabelFactory factory, 889 boolean isDirectionLTR, 890 float[] baselineOffsets) { 891 892 factory.setLineContext(0, chars.length); 893 894 Bidi lineBidi = factory.getLineBidi(); 895 int[] charsLtoV = null; 896 byte[] levels = null; 897 898 if (lineBidi != null) { 899 levels = BidiUtils.getLevels(lineBidi); 900 int[] charsVtoL = BidiUtils.createVisualToLogicalMap(levels); 901 charsLtoV = BidiUtils.createInverseMap(charsVtoL); 902 } 903 904 TextLineComponent[] components = 905 getComponents(styledParagraph, chars, 0, chars.length, charsLtoV, levels, factory); 906 907 return new TextLine (components, baselineOffsets, 908 chars, 0, chars.length, charsLtoV, levels, isDirectionLTR); 909 } 910 911 915 private static int[] computeComponentOrder(TextLineComponent[] components, 916 int[] charsLtoV) { 917 918 927 int[] componentOrder = null; 928 if (charsLtoV != null && components.length > 1) { 929 componentOrder = new int[components.length]; 930 int gStart = 0; 931 for (int i = 0; i < components.length; i++) { 932 componentOrder[i] = charsLtoV[gStart]; 933 gStart += components[i].getNumCharacters(); 934 } 935 936 componentOrder = BidiUtils.createContiguousOrder(componentOrder); 937 componentOrder = BidiUtils.createInverseMap(componentOrder); 938 } 939 return componentOrder; 940 } 941 942 943 946 public static TextLine standardCreateTextLine(FontRenderContext frc, 947 AttributedCharacterIterator text, 948 char[] chars, 949 float[] baselineOffsets) { 950 951 StyledParagraph styledParagraph = new StyledParagraph (text, chars); 952 Bidi bidi = new Bidi (text); 953 if (bidi.isLeftToRight()) { 954 bidi = null; 955 } 956 int layoutFlags = 0; TextLabelFactory factory = new TextLabelFactory(frc, chars, bidi, layoutFlags); 958 959 boolean isDirectionLTR = true; 960 if (bidi != null) { 961 isDirectionLTR = bidi.baseIsLeftToRight(); 962 } 963 return createLineFromText(chars, styledParagraph, factory, isDirectionLTR, baselineOffsets); 964 } 965 966 967 968 1000 1001 1006 static boolean advanceToFirstFont(AttributedCharacterIterator aci) { 1007 1008 for (char ch = aci.first(); ch != aci.DONE; ch = aci.setIndex(aci.getRunLimit())) { 1009 1010 if (aci.getAttribute(TextAttribute.CHAR_REPLACEMENT) == null) { 1011 return true; 1012 } 1013 } 1014 1015 return false; 1016 } 1017 1018 static float[] getNormalizedOffsets(float[] baselineOffsets, byte baseline) { 1019 1020 if (baselineOffsets[baseline] != 0) { 1021 float base = baselineOffsets[baseline]; 1022 float[] temp = new float[baselineOffsets.length]; 1023 for (int i = 0; i < temp.length; i++) 1024 temp[i] = baselineOffsets[i] - base; 1025 baselineOffsets = temp; 1026 } 1027 return baselineOffsets; 1028 } 1029 1030 static Font getFontAtCurrentPos(AttributedCharacterIterator aci) { 1031 1032 Object value = aci.getAttribute(TextAttribute.FONT); 1033 if (value != null) { 1034 return (Font ) value; 1035 } 1036 if (aci.getAttribute(TextAttribute.FAMILY) != null) { 1037 return Font.getFont(aci.getAttributes()); 1038 } 1039 1040 int ch = CodePointIterator.create(aci).next(); 1041 if (ch != CodePointIterator.DONE) { 1042 FontResolver resolver = FontResolver.getInstance(); 1043 return resolver.getFont(resolver.getFontIndex(ch), aci.getAttributes()); 1044 } 1045 return null; 1046 } 1047 1048 1051 static float getJustifyRatio(Map attributes) { 1052 1053 Object value = attributes.get(TextAttribute.JUSTIFICATION); 1054 1055 if (value == null) { 1056 return 1; 1057 } 1058 1059 float justifyRatio = ((Float )value).floatValue(); 1060 if (justifyRatio < 0) { 1061 justifyRatio = 0; 1062 } 1063 else if (justifyRatio > 1) { 1064 justifyRatio = 1; 1065 } 1066 1067 return justifyRatio; 1068 } 1069 1070 1073 private static int firstVisualChunk(int order[], byte direction[], 1074 int start, int limit) 1075 { 1076 if (order != null && direction != null) { 1077 byte dir = direction[start]; 1078 while (++start < limit && direction[start] == dir) {} 1079 return start; 1080 } 1081 return limit; 1082 } 1083 1084 1088 public TextLine getJustifiedLine(float justificationWidth, float justifyRatio, int justStart, int justLimit) { 1089 1090 TextLineComponent[] newComponents = new TextLineComponent[fComponents.length]; 1091 System.arraycopy(fComponents, 0, newComponents, 0, fComponents.length); 1092 1093 float leftHang = 0; 1094 float adv = 0; 1095 float justifyDelta = 0; 1096 boolean rejustify = false; 1097 do { 1098 adv = getAdvanceBetween(newComponents, 0, characterCount()); 1099 1100 1103 float justifyAdvance = getAdvanceBetween(newComponents, justStart, justLimit); 1104 1105 justifyDelta = (justificationWidth - justifyAdvance) * justifyRatio; 1107 1108 1111 int[] infoPositions = new int[newComponents.length]; 1113 int infoCount = 0; 1114 for (int visIndex = 0; visIndex < newComponents.length; visIndex++) { 1115 int logIndex = fComponentVisualOrder == null ? visIndex : fComponentVisualOrder[visIndex]; 1116 infoPositions[logIndex] = infoCount; 1117 infoCount += newComponents[logIndex].getNumJustificationInfos(); 1118 } 1119 GlyphJustificationInfo [] infos = new GlyphJustificationInfo [infoCount]; 1120 1121 int compStart = 0; 1123 for (int i = 0; i < newComponents.length; i++) { 1124 TextLineComponent comp = newComponents[i]; 1125 int compLength = comp.getNumCharacters(); 1126 int compLimit = compStart + compLength; 1127 if (compLimit > justStart) { 1128 int rangeMin = Math.max(0, justStart - compStart); 1129 int rangeMax = Math.min(compLength, justLimit - compStart); 1130 comp.getJustificationInfos(infos, infoPositions[i], rangeMin, rangeMax); 1131 1132 if (compLimit >= justLimit) { 1133 break; 1134 } 1135 } 1136 } 1137 1138 int infoStart = 0; 1141 int infoLimit = infoCount; 1142 while (infoStart < infoLimit && infos[infoStart] == null) { 1143 ++infoStart; 1144 } 1145 1146 while (infoLimit > infoStart && infos[infoLimit - 1] == null) { 1147 --infoLimit; 1148 } 1149 1150 TextJustifier justifier = new TextJustifier (infos, infoStart, infoLimit); 1152 1153 float[] deltas = justifier.justify(justifyDelta); 1154 1155 boolean canRejustify = rejustify == false; 1156 boolean wantRejustify = false; 1157 boolean[] flags = new boolean[1]; 1158 1159 compStart = 0; 1161 for (int i = 0; i < newComponents.length; i++) { 1162 TextLineComponent comp = newComponents[i]; 1163 int compLength = comp.getNumCharacters(); 1164 int compLimit = compStart + compLength; 1165 if (compLimit > justStart) { 1166 int rangeMin = Math.max(0, justStart - compStart); 1167 int rangeMax = Math.min(compLength, justLimit - compStart); 1168 newComponents[i] = comp.applyJustificationDeltas(deltas, infoPositions[i] * 2, flags); 1169 1170 wantRejustify |= flags[0]; 1171 1172 if (compLimit >= justLimit) { 1173 break; 1174 } 1175 } 1176 } 1177 1178 rejustify = wantRejustify && !rejustify; } while (rejustify); 1180 1181 return new TextLine (newComponents, fBaselineOffsets, fChars, fCharsStart, 1182 fCharsLimit, fCharLogicalOrder, fCharLevels, 1183 fIsDirectionLTR); 1184 } 1185 1186 public static float getAdvanceBetween(TextLineComponent[] components, int start, int limit) { 1188 float advance = 0; 1189 1190 int tlcStart = 0; 1191 for(int i = 0; i < components.length; i++) { 1192 TextLineComponent comp = components[i]; 1193 1194 int tlcLength = comp.getNumCharacters(); 1195 int tlcLimit = tlcStart + tlcLength; 1196 if (tlcLimit > start) { 1197 int measureStart = Math.max(0, start - tlcStart); 1198 int measureLimit = Math.min(tlcLength, limit - tlcStart); 1199 advance += comp.getAdvanceBetween(measureStart, measureLimit); 1200 if (tlcLimit >= limit) { 1201 break; 1202 } 1203 } 1204 1205 tlcStart = tlcLimit; 1206 } 1207 1208 return advance; 1209 } 1210} 1211 | Popular Tags |