1 50 51 package org.openlaszlo.iv.flash.util; 52 53 import java.io.*; 54 import java.awt.geom.*; 55 56 import org.openlaszlo.iv.flash.api.*; 57 import org.openlaszlo.iv.flash.api.text.*; 58 59 70 public final class TextLayout { 71 72 private static final int MAX_CHARS_IN_LINE = 250; 73 74 79 public final class TextLine extends FlashItem { 80 81 87 public IVVector records = new IVVector(4); 88 89 private TextItem lastItem; 90 private TextRecord lastRecord; 91 92 public TextLine() {} 93 94 97 public void newRecord() { 98 flushLastRecord(); 99 lastItem = curItem; 100 lastRecord = new TextRecord( MAX_CHARS_IN_LINE ); 101 } 102 103 private void flushLastRecord() { 104 if( lastRecord != null && lastRecord.getSize() != 0 ) { 105 int size = records.size(); 106 if( size%2 == 0 ) { records.addElement( getStyleChange( lastItem ) ); 108 } 109 records.addElement( lastRecord ); 110 lastRecord = null; 111 } 112 } 113 114 122 private TextStyleChangeRecord getStyleChange( TextItem item ) { 123 TextStyleChangeRecord ts = new TextStyleChangeRecord(); 125 126 if( item != null ) { 127 ts.setFont( item.font ); 128 ts.setHeight( item.height ); 129 ts.setColor( item.color ); 130 } 131 132 return ts; 133 } 134 135 144 public void add( char ch, int index, int adv ) { 145 if( lastRecord.getSize() >= MAX_CHARS_IN_LINE ) { 146 newRecord(); 147 } 148 lastRecord.add( ch, index, adv ); 149 } 150 151 158 public int trimEnd() { 159 flushLastRecord(); 160 161 int w = 0; 162 163 for( int i=records.size(); --i>0; ) { 164 TextRecord tr = (TextRecord) records.elementAt(i); 165 w += tr.trimEnd(); 166 if( tr.getSize() > 0 ) break; 167 records.removeElementAt(i); records.removeElementAt(--i); } 170 171 return w; 172 } 173 174 181 public int trimStart() { 182 flushLastRecord(); 183 184 int w = 0; 185 186 TextStyleChangeRecord lastts = null; 187 188 for(;records.size()>0;) { 189 TextStyleChangeRecord ts = (TextStyleChangeRecord) records.elementAt(0); 190 if( lastts != null ) { 191 lastts.mergeTo(ts); 192 } 193 lastts = ts; 194 TextRecord tr = (TextRecord) records.elementAt(1); 195 w += tr.trimStart(); 196 if( tr.getSize() > 0 ) break; 197 records.removeElementAt(1); records.removeElementAt(0); } 200 201 return w; 202 } 203 204 209 public void markPosition() { 210 markedPosition = lastRecord.getSize(); 211 } 212 213 216 public void rollBack() { 217 lastRecord.setSize( markedPosition ); 218 } 219 220 228 public void endLine( int x, int y ) { 229 flushLastRecord(); 230 if( records.size() > 0 ) { 231 TextStyleChangeRecord ts = (TextStyleChangeRecord) records.elementAt(0); 232 ts.setX(x); 233 ts.setY(y); 234 } 235 } 236 237 242 public int getMaxAdvance() { 243 int max = Integer.MIN_VALUE; 244 for( int i=1; i<records.size(); i+=2 ) { 246 TextRecord tr = (TextRecord) records.elementAt(i); 247 int adv = tr.getMaxAdvance(); 248 if( adv > max ) max = adv; 249 } 250 if( max == Integer.MIN_VALUE ) return 0; 251 return max; 252 } 253 254 259 public int getMaxIndex() { 260 int max = Integer.MIN_VALUE; 261 for( int i=1; i<records.size(); i+=2 ) { 263 TextRecord tr = (TextRecord) records.elementAt(i); 264 int idx = tr.getMaxIndex(); 265 if( idx > max ) max = idx; 266 } 267 if( max == Integer.MIN_VALUE ) return 0; 268 return max; 269 } 270 271 276 public int getWidth() { 277 if( records.size() == 0 ) return 0; 278 TextStyleChangeRecord ts = (TextStyleChangeRecord) records.elementAt(0); 279 int width = ts.getX(); 280 for( int i=1; i<records.size(); i+=2 ) { 281 TextRecord tr = (TextRecord) records.elementAt(i); 282 width += tr.getWidth(); 283 } 284 return width; 285 } 286 287 292 public int getSize() { 293 int size = 0; 294 for( int i=1; i<records.size(); i+=2 ) { 296 TextRecord tr = (TextRecord) records.elementAt(i); 297 size += tr.getSize(); 298 } 299 if( lastRecord != null ) size += lastRecord.getSize(); 300 return size; 301 } 302 303 public void printContent( PrintStream out, String indent ) { 304 out.println( indent+"TextLine: size="+records.size()+" x="+x+" y="+y ); 305 records.printContent(out, indent+" "); 306 } 307 308 public void write( FlashOutput fob ) { 309 records.write(fob); 310 } 311 312 protected FlashItem copyInto( FlashItem item, ScriptCopier copier ) { 313 ((TextLine)item).records = records.getCopy(copier); 314 return item; 316 } 317 318 public FlashItem getCopy( ScriptCopier copier ) { 319 return copyInto( new TextLine(), copier ); 320 } 321 } 322 323 private int markedPosition = 0; 325 private TextItem curItem; 326 private TextLine curLine; 327 private boolean line_continued; 328 private int line_width; 329 private int line_window; 330 private int x; 331 private int y; 332 private int max_ascent; 333 private int max_descent; 334 private int max_linesp; 335 336 private int rect_width; 337 private Text myText; 338 private Rectangle2D bounds; 339 private IVVector lines = new IVVector(); 340 341 private void reCalcHeights() { 342 Font font = curItem.font; 343 int height = curItem.height; 344 max_ascent = (font.ascent * height) / 1024; 345 max_linesp = curItem.linesp; 346 max_descent = (font.descent * height) / 1024 + max_linesp; 347 } 348 349 private void endLine() { 350 if( curLine != null ) { 351 y += max_ascent; curLine.endLine(x, y); 353 y += max_descent; curLine = null; 355 } 356 } 357 358 363 private void newLine( boolean cont ) { 364 endLine(); 365 reCalcHeights(); 366 curLine = new TextLine(); 367 line_continued = cont; 368 lines.addElement( curLine ); 369 curLine.newRecord(); 370 line_width = 0; 371 x = curItem.marginleft; 372 line_window = rect_width-(curItem.marginleft+curItem.marginright); 373 } 374 375 private void newParagraph() { 376 newLine( false ); 377 line_window -= curItem.indent; 378 x += curItem.indent; 379 } 380 381 387 public TextLayout( Text myText, Rectangle2D bounds ) { 388 this.myText = myText; 389 this.bounds = bounds; 390 } 391 392 399 public IVVector getTextRecords( Font font ) { 400 IVVector trs = new IVVector(); 401 Font lastFont = null; 402 for( int i=0; i<lines.size(); i++ ) { 403 TextLine line = (TextLine) lines.elementAt(i); 404 for( int k=0; k<line.records.size(); k++ ) { 405 Object o = line.records.elementAt(k); 406 if( o instanceof TextStyleChangeRecord ) { 407 Font f = ((TextStyleChangeRecord)o).getFont(); 408 if( f != null ) lastFont = f; 409 } else { 410 if( lastFont == font ) trs.addElement(o); 411 } 412 } 413 } 414 return trs; 415 } 416 417 422 public IVVector getAllTextRecords() { 423 IVVector trs = new IVVector(); 424 for( int i=0; i<lines.size(); i++ ) { 425 TextLine line = (TextLine) lines.elementAt(i); 426 for( int k=0; k<line.records.size(); k++ ) { 427 trs.addElement( line.records.elementAt(k) ); 428 } 429 } 430 return trs; 431 } 432 433 442 public void changeFont( Font old_font, Font new_font ) { 443 for( int i=0; i<lines.size(); i++ ) { 444 TextLine line = (TextLine) lines.elementAt(i); 445 IVVector records = line.records; 446 FontDef.changeRecordsFont(records, old_font, new_font); 447 } 448 } 449 450 453 private void alignLine() { 454 switch( curItem.align ) { 455 case 0: if( curLine != null ) { 457 line_width -= curLine.trimStart(); 458 } 459 break; 460 case 1: if( curLine != null ) { 462 line_width -= curLine.trimEnd(); 463 } 464 x += line_window-line_width; 465 break; 466 case 2: if( curLine != null ) { 468 line_width -= curLine.trimEnd() + curLine.trimStart(); 469 } 470 x += (line_window-line_width)/2; 471 break; 472 case 3: break; 475 } 476 } 477 478 486 private void trimEnd( IVVector items ) { 487 for( int i=items.size(); --i>=0; ) { 488 TextItem item = (TextItem) items.elementAt(i); 489 String t = item.text; 490 int j=t.length(); 491 for( ; --j>=0; ) { 492 char ch = t.charAt(j); 493 if( !Character.isWhitespace(ch) ) break; 494 } 495 if( j >= 0 ) { 496 item.text = t.substring(0, j+1); 497 return; 498 } else { 499 items.removeElementAt(i); 500 } 501 } 502 } 503 504 507 public void layout() { 508 IVVector items = myText.getTextItems(); 509 trimEnd(items); 510 512 rect_width = (int) bounds.getWidth(); 513 514 y = 0; 515 516 if( items.size() > 0 ) { 517 curItem = (TextItem) items.elementAt(0); 518 519 newParagraph(); 520 521 int i=0; 522 for(;;) { 523 Font font = curItem.font; 524 int height = curItem.height; 525 int ascent = (font.ascent * height) / 1024; 526 int descent = (font.descent * height) / 1024 + curItem.linesp; 527 if( ascent > max_ascent ) max_ascent = ascent; 528 if( descent > max_descent ) { 529 max_descent = descent; 530 max_linesp = curItem.linesp; 531 } 532 533 String text = curItem.text; 534 boolean isNowWord = false; 535 int word_width = 0; 536 int start_word = 0; 537 538 int text_len = text.length(); 539 for( int k=0; k<text_len; k++ ) { 540 char ch = text.charAt(k); 541 boolean isWord = isWord(ch); 542 if( !isNowWord && isWord ) { curLine.markPosition(); 544 word_width = 0; 545 start_word = k; 546 isNowWord = true; 547 } else { 548 isNowWord = isWord; 549 } 550 if( ch == '\r' || ch == '\n' ) { 551 alignLine(); 552 if( k == text_len-1 ) endLine(); 553 else { 554 char ch1 = text.charAt(k+1); 555 if( ch != ch1 && (ch1 == '\r' || ch1 == '\n') ) k++; 556 if( k == text_len-1 ) endLine(); 557 else newParagraph(); 558 } 559 } else { 560 int idx = font.getIndex(ch); 561 int ch_adv = font.getAdvanceValue(idx); 562 if( k != text_len-1 ) ch_adv += font.getKerning(ch, text.charAt(k+1)); 563 int adv = (ch_adv * height) / 1024; 564 adv += curItem.kerning; 565 line_width += adv; 566 word_width += adv; 567 if( line_width <= line_window ) { 568 curLine.add(ch,idx,adv); 569 } else { 570 if( curLine.getSize() == 0 ) { line_window = line_width; 573 curLine.add(ch,idx,adv); 574 alignLine(); 575 newLine( true ); 576 isNowWord = false; 577 } else if( isNowWord ) { 578 if( word_width > line_window ) { 579 line_width -= adv; 581 k--; 582 alignLine(); 583 newLine( true ); 584 } else { 585 line_width -= word_width; 587 curLine.rollBack(); 588 k = start_word-1; 589 alignLine(); 590 newLine( true ); 591 isNowWord = false; 592 } 593 } else { 594 line_width -= adv; 595 k--; 596 alignLine(); 597 newLine( true ); 598 } 599 } 600 } 601 } 602 603 i++; 604 if( i >= items.size() ) break; 605 curItem = (TextItem) items.elementAt(i); 606 if( curLine == null ) newParagraph(); 607 else curLine.newRecord(); 608 } 609 alignLine(); 610 endLine(); 611 612 y -= max_linesp; 613 } 614 615 optimize(); 617 618 int maxX = 0; 620 for( int l=0; l<lines.size(); l++ ) { 621 TextLine line = (TextLine) lines.elementAt(l); 622 int max = line.getWidth(); 623 if( max > maxX ) maxX = max; 624 } 625 626 int bs = myText.getBoundsStyle(); 629 boolean isMMStyle = bs == Text.PROPERTY_CONTROLLED? 630 PropertyManager.textMMStyle: 631 bs == Text.MM_STYLE; 632 if( isMMStyle ) { 633 bounds.setFrame(bounds.getMinX(), bounds.getMinY(), maxX, y); 635 } else { 636 double width = bounds.getWidth(); 638 double height = bounds.getHeight(); 639 if( maxX > width ) width = maxX; 640 if( y > height ) height = y; 641 bounds.setFrame(bounds.getMinX(), bounds.getMinY(), width, height); 642 } 643 644 myText.setBounds(bounds); 645 } 646 647 protected int getNGlyphBits() { 648 int maxIdx = 0; 649 for( int l=0; l<lines.size(); l++ ) { 650 TextLine line = (TextLine) lines.elementAt(l); 651 int max = line.getMaxIndex(); 652 if( max > maxIdx ) maxIdx = max; 653 } 654 655 return Util.getMinBitsU(maxIdx); 656 } 657 658 protected int getNAdvanceBits() { 659 int maxAdv = 0; 660 for( int l=0; l<lines.size(); l++ ) { 661 TextLine line = (TextLine) lines.elementAt(l); 662 int max = Math.abs(line.getMaxAdvance()); 663 if( max > maxAdv ) maxAdv = max; 664 } 665 666 return Util.getMinBitsS(maxAdv); 667 } 668 669 672 public void optimize() { 673 Font lastFont = null; 674 int lastHeight = -1; 675 Color lastColor = null; 676 677 for( int k=0; k<lines.size(); k++ ) { 678 TextLine line = (TextLine) lines.elementAt(k); 679 IVVector records = line.records; 680 for( int i=0; i<records.size(); i+=2 ) { 681 TextStyleChangeRecord ts = (TextStyleChangeRecord) records.elementAt(i); 682 if( lastFont != null && lastFont == ts.getFont() ) { 683 if( lastHeight == ts.getHeight() ) { 684 ts.setFont(null); 685 } else { 686 lastHeight = ts.getHeight(); 687 } 688 } else { 689 lastFont = ts.getFont(); 690 lastHeight = ts.getHeight(); 691 } 692 if( lastColor != null && lastColor.equals(ts.getColor()) ) { 693 ts.setColor(null); 694 } else { 695 lastColor = ts.getColor(); 696 } 697 } 698 } 699 } 700 701 public void write( FlashOutput fob ) { 702 int nGlyphBits = getNGlyphBits(); 703 int nAdvanceBits = getNAdvanceBits(); 704 fob.writeByte(nGlyphBits); 705 fob.writeByte(nAdvanceBits); 706 fob.setUserData( new int[] {nGlyphBits, nAdvanceBits} ); 708 709 for( int i=0; i<lines.size(); i++ ) { 710 TextLine line = (TextLine) lines.elementAt(i); 711 line.write(fob); 712 } 714 fob.writeByte(0); 715 } 716 717 public TextLayout getCopy( ScriptCopier copier ) { 718 TextLayout tl = new TextLayout(myText,bounds); 719 tl.lines = lines.getCopy(copier); 720 return tl; 721 } 722 723 private static boolean isWord( char ch ) { 724 727 if (ch == '-' ) 728 return false; 729 730 return !Character.isWhitespace(ch); 731 } 732 } 733 | Popular Tags |