1 7 8 22 23 package java.awt.font; 24 25 import java.awt.Font ; 26 27 import java.text.AttributedCharacterIterator ; 28 import java.text.AttributedString ; 29 import java.text.Bidi ; 30 import java.text.BreakIterator ; 31 import java.text.CharacterIterator ; 32 33 import java.awt.font.FontRenderContext ; 34 35 import java.util.Hashtable ; 36 import java.util.Map ; 37 38 import sun.font.BidiUtils; 39 import sun.font.TextLineComponent; 40 import sun.font.TextLabelFactory; 41 import sun.font.FontResolver; 42 43 78 79 public final class TextMeasurer implements Cloneable { 80 81 private static float EST_LINES = (float) 2.1; 83 84 98 99 private FontRenderContext fFrc; 100 101 private int fStart; 102 103 private char[] fChars; 105 106 private Bidi fBidi; 108 109 private byte[] fLevels; 112 113 private TextLineComponent[] fComponents; 115 116 private int fComponentStart; 118 119 private int fComponentLimit; 121 122 private boolean haveLayoutWindow; 123 124 private BreakIterator fLineBreak = null; 126 private CharArrayIterator charIter = null; 127 int layoutCount = 0; 128 int layoutCharCount = 0; 129 130 private StyledParagraph fParagraph; 132 133 private boolean fIsDirectionLTR; 135 private byte fBaseline; 136 private float[] fBaselineOffsets; 137 private float fJustifyRatio = 1; 138 139 146 public TextMeasurer(AttributedCharacterIterator text, FontRenderContext frc) { 147 148 fFrc = frc; 149 initAll(text); 150 } 151 152 protected Object clone() { 153 TextMeasurer other; 154 try { 155 other = (TextMeasurer ) super.clone(); 156 } 157 catch(CloneNotSupportedException e) { 158 throw new Error (); 159 } 160 if (fComponents != null) { 161 other.fComponents = (TextLineComponent[]) fComponents.clone(); 162 } 163 return other; 164 } 165 166 private void invalidateComponents() { 167 fComponentStart = fComponentLimit = fChars.length; 168 fComponents = null; 169 haveLayoutWindow = false; 170 } 171 172 176 private void initAll(AttributedCharacterIterator text) { 177 178 fStart = text.getBeginIndex(); 179 180 fChars = new char[text.getEndIndex() - fStart]; 182 183 int n = 0; 184 for (char c = text.first(); c != text.DONE; c = text.next()) { 185 fChars[n++] = c; 186 } 187 188 text.first(); 189 190 fBidi = new Bidi (text); 191 if (fBidi.isLeftToRight()) { 192 fBidi = null; 193 } 194 195 text.first(); 196 Map paragraphAttrs = text.getAttributes(); 197 if (paragraphAttrs != null) { 198 try { 199 NumericShaper shaper = (NumericShaper )paragraphAttrs.get(TextAttribute.NUMERIC_SHAPING); 200 if (shaper != null) { 201 shaper.shape(fChars, 0, fChars.length); 202 } 203 } 204 catch (ClassCastException e) { 205 } 206 } 207 208 fParagraph = new StyledParagraph (text, fChars); 209 210 { 212 fJustifyRatio = TextLine.getJustifyRatio(paragraphAttrs); 217 218 boolean haveFont = TextLine.advanceToFirstFont(text); 219 220 if (haveFont) { 221 Font defaultFont = TextLine.getFontAtCurrentPos(text); 222 int charsStart = text.getIndex() - text.getBeginIndex(); 223 LineMetrics lm = defaultFont.getLineMetrics(fChars, charsStart, charsStart+1, fFrc); 224 fBaseline = (byte) lm.getBaselineIndex(); 225 fBaselineOffsets = lm.getBaselineOffsets(); 226 } 227 else { 228 231 GraphicAttribute graphic = (GraphicAttribute ) 232 paragraphAttrs.get(TextAttribute.CHAR_REPLACEMENT); 233 fBaseline = TextLayout.getBaselineFromGraphic(graphic); 234 Font dummyFont = new Font (new Hashtable (5, (float)0.9)); 235 LineMetrics lm = dummyFont.getLineMetrics(" ", 0, 1, fFrc); 236 fBaselineOffsets = lm.getBaselineOffsets(); 237 } 238 fBaselineOffsets = TextLine.getNormalizedOffsets(fBaselineOffsets, fBaseline); 239 } 240 241 invalidateComponents(); 242 } 243 244 248 private void generateComponents(int startingAt, int endingAt) { 249 250 if (collectStats) { 251 formattedChars += (endingAt-startingAt); 252 } 253 int layoutFlags = 0; TextLabelFactory factory = new TextLabelFactory(fFrc, fChars, fBidi, layoutFlags); 255 256 int[] charsLtoV = null; 257 258 if (fBidi != null) { 259 fLevels = BidiUtils.getLevels(fBidi); 260 int[] charsVtoL = BidiUtils.createVisualToLogicalMap(fLevels); 261 charsLtoV = BidiUtils.createInverseMap(charsVtoL); 262 fIsDirectionLTR = fBidi.baseIsLeftToRight(); 263 } 264 else { 265 fLevels = null; 266 fIsDirectionLTR = true; 267 } 268 269 try { 270 fComponents = TextLine.getComponents( 271 fParagraph, fChars, startingAt, endingAt, charsLtoV, fLevels, factory); 272 } 273 catch(IllegalArgumentException e) { 274 System.out.println("startingAt="+startingAt+"; endingAt="+endingAt); 275 System.out.println("fComponentLimit="+fComponentLimit); 276 throw e; 277 } 278 279 fComponentStart = startingAt; 280 fComponentLimit = endingAt; 281 } 283 284 private int calcLineBreak(final int pos, final float maxAdvance) { 285 286 290 int startPos = pos; 291 float width = maxAdvance; 292 293 int tlcIndex; 294 int tlcStart = fComponentStart; 295 296 for (tlcIndex = 0; tlcIndex < fComponents.length; tlcIndex++) { 297 int gaLimit = tlcStart + fComponents[tlcIndex].getNumCharacters(); 298 if (gaLimit > startPos) { 299 break; 300 } 301 else { 302 tlcStart = gaLimit; 303 } 304 } 305 306 308 for (; tlcIndex < fComponents.length; tlcIndex++) { 309 310 TextLineComponent tlc = fComponents[tlcIndex]; 311 int numCharsInGa = tlc.getNumCharacters(); 312 313 int lineBreak = tlc.getLineBreakIndex(startPos - tlcStart, width); 314 if (lineBreak == numCharsInGa && tlcIndex < fComponents.length) { 315 width -= tlc.getAdvanceBetween(startPos - tlcStart, lineBreak); 316 tlcStart += numCharsInGa; 317 startPos = tlcStart; 318 } 319 else { 320 return tlcStart + lineBreak; 321 } 322 } 323 324 if (fComponentLimit < fChars.length) { 325 330 generateComponents(pos, fChars.length); 331 return calcLineBreak(pos, maxAdvance); 332 } 333 334 return fChars.length; 335 } 336 337 346 private int trailingCdWhitespaceStart(int startPos, int limitPos) { 347 348 if (fLevels != null) { 349 final byte baseLevel = (byte) (fIsDirectionLTR? 0 : 1); 351 for (int cdWsStart = limitPos; --cdWsStart >= startPos;) { 352 if ((fLevels[cdWsStart] % 2) == baseLevel || 353 Character.getDirectionality(fChars[cdWsStart]) != Character.DIRECTIONALITY_WHITESPACE) { 354 return ++cdWsStart; 355 } 356 } 357 } 358 359 return startPos; 360 } 361 362 private TextLineComponent[] makeComponentsOnRange(int startPos, 363 int limitPos) { 364 365 final int cdWsStart = trailingCdWhitespaceStart(startPos, limitPos); 370 371 int tlcIndex; 372 int tlcStart = fComponentStart; 373 374 for (tlcIndex = 0; tlcIndex < fComponents.length; tlcIndex++) { 375 int gaLimit = tlcStart + fComponents[tlcIndex].getNumCharacters(); 376 if (gaLimit > startPos) { 377 break; 378 } 379 else { 380 tlcStart = gaLimit; 381 } 382 } 383 384 386 int componentCount; 387 { 388 boolean split = false; 389 int compStart = tlcStart; 390 int lim=tlcIndex; 391 for (boolean cont=true; cont; lim++) { 392 int gaLimit = compStart + fComponents[lim].getNumCharacters(); 393 if (cdWsStart > Math.max(compStart, startPos) 394 && cdWsStart < Math.min(gaLimit, limitPos)) { 395 split = true; 396 } 397 if (gaLimit >= limitPos) { 398 cont=false; 399 } 400 else { 401 compStart = gaLimit; 402 } 403 } 404 componentCount = lim-tlcIndex; 405 if (split) { 406 componentCount++; 407 } 408 } 409 410 TextLineComponent[] components = new TextLineComponent[componentCount]; 411 int newCompIndex = 0; 412 int linePos = startPos; 413 414 int breakPt = cdWsStart; 415 416 int subsetFlag; 417 if (breakPt == startPos) { 418 subsetFlag = fIsDirectionLTR? TextLineComponent.LEFT_TO_RIGHT : 419 TextLineComponent.RIGHT_TO_LEFT; 420 breakPt = limitPos; 421 } 422 else { 423 subsetFlag = TextLineComponent.UNCHANGED; 424 } 425 426 while (linePos < limitPos) { 427 428 int compLength = fComponents[tlcIndex].getNumCharacters(); 429 int tlcLimit = tlcStart + compLength; 430 431 int start = Math.max(linePos, tlcStart); 432 int limit = Math.min(breakPt, tlcLimit); 433 434 components[newCompIndex++] = fComponents[tlcIndex].getSubset( 435 start-tlcStart, 436 limit-tlcStart, 437 subsetFlag); 438 linePos += (limit-start); 439 if (linePos == breakPt) { 440 breakPt = limitPos; 441 subsetFlag = fIsDirectionLTR? TextLineComponent.LEFT_TO_RIGHT : 442 TextLineComponent.RIGHT_TO_LEFT; 443 } 444 if (linePos == tlcLimit) { 445 tlcIndex++; 446 tlcStart = tlcLimit; 447 } 448 } 449 450 return components; 451 } 452 453 private TextLine makeTextLineOnRange(int startPos, int limitPos) { 454 455 int[] charsLtoV = null; 456 byte[] charLevels = null; 457 458 if (fBidi != null) { 459 Bidi lineBidi = fBidi.createLineBidi(startPos, limitPos); 460 charLevels = BidiUtils.getLevels(lineBidi); 461 int[] charsVtoL = BidiUtils.createVisualToLogicalMap(charLevels); 462 charsLtoV = BidiUtils.createInverseMap(charsVtoL); 463 } 464 465 TextLineComponent[] components = makeComponentsOnRange(startPos, limitPos); 466 467 return new TextLine (components, 468 fBaselineOffsets, 469 fChars, 470 startPos, 471 limitPos, 472 charsLtoV, 473 charLevels, 474 fIsDirectionLTR); 475 476 } 477 478 private void ensureComponents(int start, int limit) { 479 480 if (start < fComponentStart || limit > fComponentLimit) { 481 generateComponents(start, limit); 482 } 483 } 484 485 private void makeLayoutWindow(int localStart) { 486 487 int compStart = localStart; 488 int compLimit = fChars.length; 489 490 if (layoutCount > 0 && !haveLayoutWindow) { 492 float avgLineLength = Math.max(layoutCharCount / layoutCount, 1); 493 compLimit = Math.min(localStart + (int)(avgLineLength*EST_LINES), fChars.length); 494 } 495 496 if (localStart > 0 || compLimit < fChars.length) { 497 if (charIter == null) { 498 charIter = new CharArrayIterator (fChars); 499 } 500 else { 501 charIter.reset(fChars); 502 } 503 if (fLineBreak == null) { 504 fLineBreak = BreakIterator.getLineInstance(); 505 } 506 fLineBreak.setText(charIter); 507 if (localStart > 0) { 508 if (!fLineBreak.isBoundary(localStart)) { 509 compStart = fLineBreak.preceding(localStart); 510 } 511 } 512 if (compLimit < fChars.length) { 513 if (!fLineBreak.isBoundary(compLimit)) { 514 compLimit = fLineBreak.following(compLimit); 515 } 516 } 517 } 518 519 ensureComponents(compStart, compLimit); 520 haveLayoutWindow = true; 521 } 522 523 538 public int getLineBreakIndex(int start, float maxAdvance) { 539 540 int localStart = start - fStart; 541 542 if (!haveLayoutWindow || 543 localStart < fComponentStart || 544 localStart >= fComponentLimit) { 545 makeLayoutWindow(localStart); 546 } 547 548 return calcLineBreak(localStart, maxAdvance) + fStart; 549 } 550 551 567 public float getAdvanceBetween(int start, int limit) { 568 569 int localStart = start - fStart; 570 int localLimit = limit - fStart; 571 572 ensureComponents(localStart, localLimit); 573 TextLine line = makeTextLineOnRange(localStart, localLimit); 574 return line.getMetrics().advance; 575 } 577 578 592 public TextLayout getLayout(int start, int limit) { 593 594 int localStart = start - fStart; 595 int localLimit = limit - fStart; 596 597 ensureComponents(localStart, localLimit); 598 TextLine textLine = makeTextLineOnRange(localStart, localLimit); 599 600 if (localLimit < fChars.length) { 601 layoutCharCount += limit-start; 602 layoutCount++; 603 } 604 605 return new TextLayout (textLine, 606 fBaseline, 607 fBaselineOffsets, 608 fJustifyRatio); 609 } 610 611 private int formattedChars = 0; 612 private static boolean wantStats = false; 613 private boolean collectStats = false; 614 615 private void printStats() { 616 System.out.println("formattedChars: " + formattedChars); 617 collectStats = false; 619 } 620 621 643 public void insertChar(AttributedCharacterIterator newParagraph, int insertPos) { 644 645 if (collectStats) { 646 printStats(); 647 } 648 if (wantStats) { 649 collectStats = true; 650 } 651 652 fStart = newParagraph.getBeginIndex(); 653 int end = newParagraph.getEndIndex(); 654 if (end - fStart != fChars.length+1) { 655 initAll(newParagraph); 656 } 657 658 char[] newChars = new char[end-fStart]; 659 int newCharIndex = insertPos - fStart; 660 System.arraycopy(fChars, 0, newChars, 0, newCharIndex); 661 662 char newChar = newParagraph.setIndex(insertPos); 663 newChars[newCharIndex] = newChar; 664 System.arraycopy(fChars, 665 newCharIndex, 666 newChars, 667 newCharIndex+1, 668 end-insertPos-1); 669 fChars = newChars; 670 671 if (fBidi != null || Bidi.requiresBidi(newChars, newCharIndex, newCharIndex + 1) || 672 newParagraph.getAttribute(TextAttribute.BIDI_EMBEDDING) != null) { 673 674 fBidi = new Bidi (newParagraph); 675 if (fBidi.isLeftToRight()) { 676 fBidi = null; 677 } 678 } 679 680 fParagraph = StyledParagraph.insertChar(newParagraph, 681 fChars, 682 insertPos, 683 fParagraph); 684 invalidateComponents(); 685 } 686 687 709 public void deleteChar(AttributedCharacterIterator newParagraph, int deletePos) { 710 711 fStart = newParagraph.getBeginIndex(); 712 int end = newParagraph.getEndIndex(); 713 if (end - fStart != fChars.length-1) { 714 initAll(newParagraph); 715 } 716 717 char[] newChars = new char[end-fStart]; 718 int changedIndex = deletePos-fStart; 719 720 System.arraycopy(fChars, 0, newChars, 0, deletePos-fStart); 721 System.arraycopy(fChars, changedIndex+1, newChars, changedIndex, end-deletePos); 722 fChars = newChars; 723 724 if (fBidi != null) { 725 fBidi = new Bidi (newParagraph); 726 if (fBidi.isLeftToRight()) { 727 fBidi = null; 728 } 729 } 730 731 fParagraph = StyledParagraph.deleteChar(newParagraph, 732 fChars, 733 deletePos, 734 fParagraph); 735 invalidateComponents(); 736 } 737 738 742 char[] getChars() { 743 744 return fChars; 745 } 746 } 747 | Popular Tags |