001package org.apache.maven.doxia.module.rtf; 002 003/* 004 * Licensed to the Apache Software Foundation (ASF) under one 005 * or more contributor license agreements. See the NOTICE file 006 * distributed with this work for additional information 007 * regarding copyright ownership. The ASF licenses this file 008 * to you under the Apache License, Version 2.0 (the 009 * "License"); you may not use this file except in compliance 010 * with the License. You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, 015 * software distributed under the License is distributed on an 016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 017 * KIND, either express or implied. See the License for the 018 * specific language governing permissions and limitations 019 * under the License. 020 */ 021 022import java.awt.Color; 023 024import java.io.BufferedOutputStream; 025import java.io.BufferedWriter; 026import java.io.IOException; 027import java.io.OutputStream; 028import java.io.OutputStreamWriter; 029import java.io.PrintWriter; 030import java.io.Writer; 031 032import java.util.HashMap; 033import java.util.Hashtable; 034import java.util.Iterator; 035import java.util.Map; 036import java.util.Set; 037import java.util.StringTokenizer; 038import java.util.TreeSet; 039import java.util.Vector; 040 041import org.apache.maven.doxia.sink.AbstractTextSink; 042import org.apache.maven.doxia.sink.Sink; 043import org.apache.maven.doxia.sink.SinkEventAttributes; 044 045/** 046 * <a href="http://en.wikipedia.org/wiki/Rich_Text_Format">RTF</a> Sink implementation. 047 * 048 * @version $Id: RtfSink.html 905940 2014-04-12 16:27:29Z hboutemy $ 049 * @since 1.0 050 */ 051public class RtfSink 052 extends AbstractTextSink 053{ 054 /** Paper width, 21 cm */ 055 public static final double DEFAULT_PAPER_WIDTH = 21.; /*cm*/ 056 057 /** Paper height, 29.7 cm */ 058 public static final double DEFAULT_PAPER_HEIGHT = 29.7; /*cm*/ 059 060 /** Paper top margin, 2 cm */ 061 public static final double DEFAULT_TOP_MARGIN = 2.; /*cm*/ 062 063 /** Paper bottom margin, 2 cm */ 064 public static final double DEFAULT_BOTTOM_MARGIN = 2.; /*cm*/ 065 066 /** Paper left margin, 2 cm */ 067 public static final double DEFAULT_LEFT_MARGIN = 2.; /*cm*/ 068 069 /** Paper right margin, 2 cm */ 070 public static final double DEFAULT_RIGHT_MARGIN = 2.; /*cm*/ 071 072 /** Font size, 10 pts */ 073 public static final int DEFAULT_FONT_SIZE = 10; /*pts*/ 074 075 /** Spacing, 10 pts */ 076 public static final int DEFAULT_SPACING = 10; /*pts*/ 077 078 /** Resolution, 72 dpi */ 079 public static final int DEFAULT_RESOLUTION = 72; /*dpi*/ 080 081 /** Image format, bmp */ 082 public static final String DEFAULT_IMAGE_FORMAT = "bmp"; 083 084 /** Image type, palette */ 085 public static final String DEFAULT_IMAGE_TYPE = "palette"; 086 087 /** Data format, ascii */ 088 public static final String DEFAULT_DATA_FORMAT = "ascii"; 089 090 /** Codepage, 1252 */ 091 public static final int DEFAULT_CODE_PAGE = 1252; 092 093 /** Constant <code>DEFAULT_CHAR_SET=0</code> */ 094 public static final int DEFAULT_CHAR_SET = 0; 095 096 /** Constant <code>IMG_FORMAT_BMP="bmp"</code> */ 097 public static final String IMG_FORMAT_BMP = "bmp"; 098 099 /** Constant <code>IMG_FORMAT_WMF="wmf"</code> */ 100 public static final String IMG_FORMAT_WMF = "wmf"; 101 102 /** Constant <code>IMG_TYPE_PALETTE="palette"</code> */ 103 public static final String IMG_TYPE_PALETTE = "palette"; 104 105 /** Constant <code>IMG_TYPE_RGB="rgb"</code> */ 106 public static final String IMG_TYPE_RGB = "rgb"; 107 108 /** Constant <code>IMG_DATA_ASCII="ascii"</code> */ 109 public static final String IMG_DATA_ASCII = "ascii"; 110 111 /** Constant <code>IMG_DATA_RAW="raw"</code> */ 112 public static final String IMG_DATA_RAW = "raw"; 113 114 /** Constant <code>STYLE_ROMAN=0</code> */ 115 public static final int STYLE_ROMAN = 0; 116 117 /** Constant <code>STYLE_ITALIC=1</code> */ 118 public static final int STYLE_ITALIC = 1; 119 120 /** Constant <code>STYLE_BOLD=2</code> */ 121 public static final int STYLE_BOLD = 2; 122 123 /** Constant <code>STYLE_TYPEWRITER=3</code> */ 124 public static final int STYLE_TYPEWRITER = 3; 125 126 private static final int CONTEXT_UNDEFINED = 0; 127 128 private static final int CONTEXT_VERBATIM = 1; 129 130 private static final int CONTEXT_TABLE = 2; 131 132 private static final int UNIT_MILLIMETER = 1; 133 134 private static final int UNIT_CENTIMETER = 2; 135 136 private static final int UNIT_INCH = 3; 137 138 private static final int UNIT_PIXEL = 4; 139 140 private static final int LIST_INDENT = 300; /*twips*/ 141 142 private static final String LIST_ITEM_HEADER = "- "; 143 144 private static final int DEFINITION_INDENT = 300; /*twips*/ 145 146 private static final int CELL_HORIZONTAL_PAD = 60; /*twips*/ 147 148 private static final int CELL_VERTICAL_PAD = 20; /*twips*/ 149 150 private static final int BORDER_WIDTH = 15; /*twips*/ 151 152 private double paperWidth = DEFAULT_PAPER_WIDTH; 153 154 private double paperHeight = DEFAULT_PAPER_HEIGHT; 155 156 private double topMargin = DEFAULT_TOP_MARGIN; 157 158 private double bottomMargin = DEFAULT_BOTTOM_MARGIN; 159 160 private double leftMargin = DEFAULT_LEFT_MARGIN; 161 162 private double rightMargin = DEFAULT_RIGHT_MARGIN; 163 164 protected int fontSize = DEFAULT_FONT_SIZE; 165 166 private int resolution = DEFAULT_RESOLUTION; 167 168 private String imageFormat = DEFAULT_IMAGE_FORMAT; 169 170 private String imageType = DEFAULT_IMAGE_TYPE; 171 172 private String imageDataFormat = DEFAULT_DATA_FORMAT; 173 174 private boolean imageCompression = true; 175 176 private int codePage = DEFAULT_CODE_PAGE; 177 178 private int charSet = DEFAULT_CHAR_SET; 179 180 private final Hashtable fontTable; 181 182 private Context context; 183 184 private Paragraph paragraph; 185 186 protected Indentation indentation; 187 188 protected Space space; 189 190 private int listItemIndent; 191 192 private final Vector numbering; 193 194 private final Vector itemNumber; 195 196 private int style = STYLE_ROMAN; 197 198 private int sectionLevel; 199 200 private boolean emptyHeader; 201 202 private StringBuilder verbatim; 203 204 private boolean frame; 205 206 private Table table; 207 208 private Row row; 209 210 private Cell cell; 211 212 private Line line; 213 214 protected PrintWriter writer; 215 216 protected OutputStream stream; // for raw image data 217 218 /** Map of warn messages with a String as key to describe the error type and a Set as value. 219 * Using to reduce warn messages. */ 220 private Map warnMessages; 221 222 // ----------------------------------------------------------------------- 223 224 /** 225 * <p>Constructor for RtfSink.</p> 226 * 227 * @throws java.io.IOException if any 228 */ 229 protected RtfSink() 230 throws IOException 231 { 232 this( System.out ); 233 } 234 235 /** 236 * <p>Constructor for RtfSink.</p> 237 * 238 * @param output not null 239 * @throws java.io.IOException if any 240 */ 241 protected RtfSink( OutputStream output ) 242 throws IOException 243 { 244 this( output, null ); 245 } 246 247 /** 248 * <p>Constructor for RtfSink.</p> 249 * 250 * @param output not null 251 * @param encoding a valid charset 252 * @throws java.io.IOException if any 253 */ 254 protected RtfSink( OutputStream output, String encoding ) 255 throws IOException 256 { 257 this.fontTable = new Hashtable(); 258 this.numbering = new Vector(); 259 this.itemNumber = new Vector(); 260 261 Writer w; 262 this.stream = new BufferedOutputStream( output ); 263 // TODO: encoding should be consistent with codePage 264 if ( encoding != null ) 265 { 266 w = new OutputStreamWriter( stream, encoding ); 267 } 268 else 269 { 270 w = new OutputStreamWriter( stream ); 271 } 272 this.writer = new PrintWriter( new BufferedWriter( w ) ); 273 274 init(); 275 } 276 277 /** 278 * setPaperSize. 279 * 280 * @param width in cm. 281 * @param height in cm. 282 */ 283 public void setPaperSize( double width /*cm*/, double height /*cm*/ ) 284 { 285 paperWidth = width; 286 paperHeight = height; 287 } 288 289 /** 290 * <p>Setter for the field <code>topMargin</code>.</p> 291 * 292 * @param margin margin. 293 */ 294 public void setTopMargin( double margin ) 295 { 296 topMargin = margin; 297 } 298 299 /** 300 * <p>Setter for the field <code>bottomMargin</code>.</p> 301 * 302 * @param margin margin. 303 */ 304 public void setBottomMargin( double margin ) 305 { 306 bottomMargin = margin; 307 } 308 309 /** 310 * <p>Setter for the field <code>leftMargin</code>.</p> 311 * 312 * @param margin margin 313 */ 314 public void setLeftMargin( double margin ) 315 { 316 leftMargin = margin; 317 } 318 319 /** 320 * <p>Setter for the field <code>rightMargin</code>.</p> 321 * 322 * @param margin margin 323 */ 324 public void setRightMargin( double margin ) 325 { 326 rightMargin = margin; 327 } 328 329 /** 330 * <p>Setter for the field <code>fontSize</code>.</p> 331 * 332 * @param size in pts 333 */ 334 public void setFontSize( int size /*pts*/ ) 335 { 336 fontSize = size; 337 } 338 339 /** 340 * <p>setSpacing.</p> 341 * 342 * @param spacing in pts. 343 */ 344 public void setSpacing( int spacing /*pts*/ ) 345 { 346 space.set( 20 * spacing ); 347 } 348 349 /** 350 * <p>Setter for the field <code>resolution</code>.</p> 351 * 352 * @param resolution in dpi 353 */ 354 public void setResolution( int resolution /*dpi*/ ) 355 { 356 this.resolution = resolution; 357 } 358 359 /** 360 * <p>Setter for the field <code>imageFormat</code>.</p> 361 * 362 * @param format 363 */ 364 public void setImageFormat( String format ) 365 { 366 imageFormat = format; 367 } 368 369 /** 370 * <p>Setter for the field <code>imageType</code>.</p> 371 * 372 * @param type 373 */ 374 public void setImageType( String type ) 375 { 376 imageType = type; 377 } 378 379 /** 380 * <p>Setter for the field <code>imageDataFormat</code>.</p> 381 * 382 * @param format 383 */ 384 public void setImageDataFormat( String format ) 385 { 386 imageDataFormat = format; 387 } 388 389 /** 390 * <p>Setter for the field <code>imageCompression</code>.</p> 391 * 392 * @param compression 393 */ 394 public void setImageCompression( boolean compression ) 395 { 396 imageCompression = compression; 397 } 398 399 /** 400 * <p>Setter for the field <code>codePage</code>.</p> 401 * 402 * @param cp 403 */ 404 public void setCodePage( int cp ) 405 { 406 codePage = cp; 407 } 408 409 /** 410 * <p>Setter for the field <code>charSet</code>.</p> 411 * 412 * @param cs 413 */ 414 public void setCharSet( int cs ) 415 { 416 charSet = cs; 417 } 418 419 /** {@inheritDoc} */ 420 public void head() 421 { 422 init(); 423 424 writer.println( "{\\rtf1\\ansi\\ansicpg" + codePage + "\\deff0" ); 425 426 writer.println( "{\\fonttbl" ); 427 writer.println( "{\\f0\\froman\\fcharset" + charSet + " Times;}" ); 428 writer.println( "{\\f1\\fmodern\\fcharset" + charSet + " Courier;}" ); 429 writer.println( "}" ); 430 431 writer.println( "{\\stylesheet" ); 432 for ( int level = 1; level <= 5; ++level ) 433 { 434 writer.print( "{\\s" + styleNumber( level ) ); 435 writer.print( "\\outlinelevel" + level ); 436 writer.print( " Section Title " + level ); 437 writer.println( ";}" ); 438 } 439 writer.println( "}" ); 440 441 writer.println( "\\paperw" + toTwips( paperWidth, UNIT_CENTIMETER ) ); 442 writer.println( "\\paperh" + toTwips( paperHeight, UNIT_CENTIMETER ) ); 443 writer.println( "\\margl" + toTwips( leftMargin, UNIT_CENTIMETER ) ); 444 writer.println( "\\margr" + toTwips( rightMargin, UNIT_CENTIMETER ) ); 445 writer.println( "\\margt" + toTwips( topMargin, UNIT_CENTIMETER ) ); 446 writer.println( "\\margb" + toTwips( bottomMargin, UNIT_CENTIMETER ) ); 447 448 space.set( space.get() / 2 ); 449 space.setNext( 0 ); 450 451 emptyHeader = true; 452 } 453 454 /** {@inheritDoc} */ 455 public void head_() 456 { 457 space.restore(); 458 if ( emptyHeader ) 459 { 460 space.setNext( 0 ); 461 } 462 else 463 { 464 space.setNext( 2 * space.get() ); 465 } 466 } 467 468 /** 469 * <p>toTwips.</p> 470 * 471 * @param length a double. 472 * @param unit a int. 473 * @return a int. 474 */ 475 protected int toTwips( double length, int unit ) 476 { 477 double points; 478 479 switch ( unit ) 480 { 481 case UNIT_MILLIMETER: 482 points = ( length / 25.4 ) * 72.; 483 break; 484 case UNIT_CENTIMETER: 485 points = ( length / 2.54 ) * 72.; 486 break; 487 case UNIT_INCH: 488 points = length * 72.; 489 break; 490 case UNIT_PIXEL: 491 default: 492 points = ( length / resolution ) * 72.; 493 break; 494 } 495 496 return (int) Math.rint( points * 20. ); 497 } 498 499 /** {@inheritDoc} */ 500 public void title() 501 { 502 Paragraph p = new Paragraph( STYLE_BOLD, fontSize + 6 ); 503 p.justification = Sink.JUSTIFY_CENTER; 504 beginParagraph( p ); 505 emptyHeader = false; 506 } 507 508 /** {@inheritDoc} */ 509 public void title_() 510 { 511 endParagraph(); 512 } 513 514 /** {@inheritDoc} */ 515 public void author() 516 { 517 Paragraph p = new Paragraph( STYLE_ROMAN, fontSize + 2 ); 518 p.justification = Sink.JUSTIFY_CENTER; 519 beginParagraph( p ); 520 emptyHeader = false; 521 } 522 523 /** {@inheritDoc} */ 524 public void author_() 525 { 526 endParagraph(); 527 } 528 529 /** {@inheritDoc} */ 530 public void date() 531 { 532 Paragraph p = new Paragraph( STYLE_ROMAN, fontSize ); 533 p.justification = Sink.JUSTIFY_CENTER; 534 beginParagraph( p ); 535 emptyHeader = false; 536 } 537 538 /** {@inheritDoc} */ 539 public void date_() 540 { 541 endParagraph(); 542 } 543 544 /** {@inheritDoc} */ 545 public void body() 546 { 547 // nop 548 } 549 550 /** {@inheritDoc} */ 551 public void body_() 552 { 553 writer.println( "}" ); 554 writer.flush(); 555 } 556 557 /** {@inheritDoc} */ 558 public void section1() 559 { 560 sectionLevel = 1; 561 } 562 563 /** {@inheritDoc} */ 564 public void section1_() 565 { 566 // nop 567 } 568 569 /** {@inheritDoc} */ 570 public void section2() 571 { 572 sectionLevel = 2; 573 } 574 575 /** {@inheritDoc} */ 576 public void section2_() 577 { 578 // nop 579 } 580 581 /** {@inheritDoc} */ 582 public void section3() 583 { 584 sectionLevel = 3; 585 } 586 587 /** {@inheritDoc} */ 588 public void section3_() 589 { 590 // nop 591 } 592 593 /** {@inheritDoc} */ 594 public void section4() 595 { 596 sectionLevel = 4; 597 } 598 599 /** {@inheritDoc} */ 600 public void section4_() 601 { 602 // nop 603 } 604 605 /** {@inheritDoc} */ 606 public void section5() 607 { 608 sectionLevel = 5; 609 } 610 611 /** {@inheritDoc} */ 612 public void section5_() 613 { 614 // nop 615 } 616 617 /** {@inheritDoc} */ 618 public void sectionTitle() 619 { 620 int stl = STYLE_BOLD; 621 int size = fontSize; 622 623 switch ( sectionLevel ) 624 { 625 case 1: 626 size = fontSize + 6; 627 break; 628 case 2: 629 size = fontSize + 4; 630 break; 631 case 3: 632 size = fontSize + 2; 633 break; 634 case 4: 635 break; 636 case 5: 637 stl = STYLE_ROMAN; 638 break; 639 } 640 641 Paragraph p = new Paragraph( stl, size ); 642 p.style = styleNumber( sectionLevel ); 643 644 beginParagraph( p ); 645 } 646 647 /** {@inheritDoc} */ 648 public void sectionTitle_() 649 { 650 endParagraph(); 651 } 652 653 private int styleNumber( int level ) 654 { 655 return level; 656 } 657 658 /** {@inheritDoc} */ 659 public void list() 660 { 661 indentation.add( LIST_INDENT ); 662 space.set( space.get() / 2 ); 663 } 664 665 /** {@inheritDoc} */ 666 public void list_() 667 { 668 indentation.restore(); 669 space.restore(); 670 } 671 672 /** {@inheritDoc} */ 673 public void listItem() 674 { 675 Paragraph p = new Paragraph(); 676 p.leftIndent = indentation.get() + listItemIndent; 677 p.firstLineIndent = ( -listItemIndent ); 678 beginParagraph( p ); 679 680 beginStyle( STYLE_BOLD ); 681 writer.println( LIST_ITEM_HEADER ); 682 endStyle(); 683 684 indentation.add( listItemIndent ); 685 space.set( space.get() / 2 ); 686 } 687 688 /** {@inheritDoc} */ 689 public void listItem_() 690 { 691 endParagraph(); 692 693 indentation.restore(); 694 space.restore(); 695 } 696 697 /** {@inheritDoc} */ 698 public void numberedList( int numbering ) 699 { 700 this.numbering.addElement( Integer.valueOf( numbering ) ); 701 itemNumber.addElement( new Counter( 0 ) ); 702 703 indentation.add( LIST_INDENT ); 704 space.set( space.get() / 2 ); 705 } 706 707 /** {@inheritDoc} */ 708 public void numberedList_() 709 { 710 numbering.removeElementAt( numbering.size() - 1 ); 711 itemNumber.removeElementAt( itemNumber.size() - 1 ); 712 713 indentation.restore(); 714 space.restore(); 715 } 716 717 /** {@inheritDoc} */ 718 public void numberedListItem() 719 { 720 ( (Counter) itemNumber.lastElement() ).increment(); 721 722 int indent = 0; 723 String header = getItemHeader(); 724 Font font = getFont( STYLE_TYPEWRITER, fontSize ); 725 if ( font != null ) 726 { 727 indent = textWidth( header, font ); 728 } 729 730 Paragraph p = new Paragraph(); 731 p.leftIndent = indentation.get() + indent; 732 p.firstLineIndent = ( -indent ); 733 beginParagraph( p ); 734 735 beginStyle( STYLE_TYPEWRITER ); 736 writer.println( header ); 737 endStyle(); 738 739 indentation.add( indent ); 740 space.set( space.get() / 2 ); 741 } 742 743 /** {@inheritDoc} */ 744 public void numberedListItem_() 745 { 746 endParagraph(); 747 748 indentation.restore(); 749 space.restore(); 750 } 751 752 private String getItemHeader() 753 { 754 int nmb = ( (Integer) this.numbering.lastElement() ).intValue(); 755 int iNmb = ( (Counter) this.itemNumber.lastElement() ).get(); 756 StringBuilder buf = new StringBuilder(); 757 758 switch ( nmb ) 759 { 760 case Sink.NUMBERING_DECIMAL: 761 default: 762 buf.append( iNmb ); 763 buf.append( ". " ); 764 while ( buf.length() < 4 ) 765 { 766 buf.append( ' ' ); 767 } 768 break; 769 770 case Sink.NUMBERING_LOWER_ALPHA: 771 buf.append( AlphaNumerals.toString( iNmb, true ) ); 772 buf.append( ") " ); 773 break; 774 775 case Sink.NUMBERING_UPPER_ALPHA: 776 buf.append( AlphaNumerals.toString( iNmb, false ) ); 777 buf.append( ". " ); 778 break; 779 780 case Sink.NUMBERING_LOWER_ROMAN: 781 buf.append( RomanNumerals.toString( iNmb, true ) ); 782 buf.append( ") " ); 783 while ( buf.length() < 6 ) 784 { 785 buf.append( ' ' ); 786 } 787 break; 788 789 case Sink.NUMBERING_UPPER_ROMAN: 790 buf.append( RomanNumerals.toString( iNmb, false ) ); 791 buf.append( ". " ); 792 while ( buf.length() < 6 ) 793 { 794 buf.append( ' ' ); 795 } 796 break; 797 } 798 799 return buf.toString(); 800 } 801 802 /** {@inheritDoc} */ 803 public void definitionList() 804 { 805 int next = space.getNext(); 806 807 indentation.add( LIST_INDENT ); 808 space.set( space.get() / 2 ); 809 space.setNext( next ); 810 } 811 812 /** {@inheritDoc} */ 813 public void definitionList_() 814 { 815 indentation.restore(); 816 space.restore(); 817 } 818 819 /** {@inheritDoc} */ 820 public void definitionListItem() 821 { 822 int next = space.getNext(); 823 space.set( space.get() / 2 ); 824 space.setNext( next ); 825 } 826 827 /** {@inheritDoc} */ 828 public void definitionListItem_() 829 { 830 space.restore(); 831 } 832 833 /** {@inheritDoc} */ 834 public void definedTerm() 835 { 836 // nop 837 } 838 839 /** {@inheritDoc} */ 840 public void definedTerm_() 841 { 842 endParagraph(); 843 } 844 845 /** {@inheritDoc} */ 846 public void definition() 847 { 848 int next = space.getNext(); 849 850 indentation.add( DEFINITION_INDENT ); 851 space.set( space.get() / 2 ); 852 space.setNext( next ); 853 } 854 855 /** {@inheritDoc} */ 856 public void definition_() 857 { 858 endParagraph(); 859 860 indentation.restore(); 861 space.restore(); 862 } 863 864 /** {@inheritDoc} */ 865 public void table() 866 { 867 // nop 868 } 869 870 /** {@inheritDoc} */ 871 public void table_() 872 { 873 // nop 874 } 875 876 /** {@inheritDoc} */ 877 public void tableRows( int[] justification, boolean grid ) 878 879 { 880 table = new Table( justification, grid ); 881 context.set( CONTEXT_TABLE ); 882 } 883 884 /** {@inheritDoc} */ 885 public void tableRows_() 886 { 887 boolean bb = false; 888 boolean br = false; 889 890 int offset = ( pageWidth() - ( table.width() + indentation.get() ) ) / 2; 891 int x0 = indentation.get() + offset; 892 893 space.skip(); 894 895 for ( int i = 0; i < table.rows.size(); ++i ) 896 { 897 Row r = (Row) table.rows.elementAt( i ); 898 899 writer.print( "\\trowd" ); 900 writer.print( "\\trleft" + x0 ); 901 writer.print( "\\trgaph" + CELL_HORIZONTAL_PAD ); 902 writer.println( "\\trrh" + r.height() ); 903 904 if ( table.grid ) 905 { 906 if ( i == ( table.rows.size() - 1 ) ) 907 { 908 bb = true; 909 } 910 br = false; 911 } 912 913 for ( int j = 0, x = x0; j < table.numColumns; ++j ) 914 { 915 if ( table.grid ) 916 { 917 if ( j == ( table.numColumns - 1 ) ) 918 { 919 br = true; 920 } 921 setBorder( true, bb, true, br ); 922 x += BORDER_WIDTH; 923 } 924 x += table.columnWidths[j]; 925 writer.println( "\\clvertalc\\cellx" + x ); 926 } 927 928 for ( int j = 0; j < table.numColumns; ++j ) 929 { 930 if ( j >= r.cells.size() ) 931 { 932 break; 933 } 934 Cell c = (Cell) r.cells.elementAt( j ); 935 936 writer.print( "\\pard\\intbl" ); 937 setJustification( table.justification[j] ); 938 writer.println( "\\plain\\f0\\fs" + ( 2 * fontSize ) ); 939 940 for ( int k = 0; k < c.lines.size(); ++k ) 941 { 942 if ( k > 0 ) 943 { 944 writer.println( "\\line" ); 945 } 946 Line l = (Line) c.lines.elementAt( k ); 947 948 for ( int n = 0; n < l.items.size(); ++n ) 949 { 950 Item item = (Item) l.items.elementAt( n ); 951 writer.print( "{" ); 952 setStyle( item.style ); 953 writer.println( escape( item.text ) ); 954 writer.println( "}" ); 955 } 956 } 957 958 writer.println( "\\cell" ); 959 } 960 961 writer.println( "\\row" ); 962 } 963 964 context.restore(); 965 } 966 967 private int pageWidth() 968 { 969 double width = paperWidth - ( leftMargin + rightMargin ); 970 return toTwips( width, UNIT_CENTIMETER ); 971 } 972 973 private void setBorder( boolean bt, boolean bb, boolean bl, boolean br ) 974 { 975 if ( bt ) 976 { 977 writer.println( "\\clbrdrt\\brdrs\\brdrw" + BORDER_WIDTH ); 978 } 979 if ( bb ) 980 { 981 writer.println( "\\clbrdrb\\brdrs\\brdrw" + BORDER_WIDTH ); 982 } 983 if ( bl ) 984 { 985 writer.println( "\\clbrdrl\\brdrs\\brdrw" + BORDER_WIDTH ); 986 } 987 if ( br ) 988 { 989 writer.println( "\\clbrdrr\\brdrs\\brdrw" + BORDER_WIDTH ); 990 } 991 } 992 993 private void setJustification( int justification ) 994 { 995 switch ( justification ) 996 { 997 case Sink.JUSTIFY_LEFT: 998 default: 999 writer.println( "\\ql" ); 1000 break; 1001 case Sink.JUSTIFY_CENTER: 1002 writer.println( "\\qc" ); 1003 break; 1004 case Sink.JUSTIFY_RIGHT: 1005 writer.println( "\\qr" ); 1006 break; 1007 } 1008 } 1009 1010 private void setStyle( int style ) 1011 { 1012 switch ( style ) 1013 { 1014 case STYLE_ITALIC: 1015 writer.println( "\\i" ); 1016 break; 1017 case STYLE_BOLD: 1018 writer.println( "\\b" ); 1019 break; 1020 case STYLE_TYPEWRITER: 1021 writer.println( "\\f1" ); 1022 break; 1023 default: 1024 break; 1025 } 1026 } 1027 1028 /** {@inheritDoc} */ 1029 public void tableRow() 1030 { 1031 row = new Row(); 1032 } 1033 1034 /** {@inheritDoc} */ 1035 public void tableRow_() 1036 { 1037 table.add( row ); 1038 } 1039 1040 /** {@inheritDoc} */ 1041 public void tableHeaderCell() 1042 { 1043 tableCell(); 1044 } 1045 1046 /** {@inheritDoc} */ 1047 public void tableHeaderCell_() 1048 { 1049 tableCell_(); 1050 } 1051 1052 /** {@inheritDoc} */ 1053 public void tableCell() 1054 { 1055 cell = new Cell(); 1056 line = new Line(); 1057 } 1058 1059 /** {@inheritDoc} */ 1060 public void tableCell_() 1061 { 1062 cell.add( line ); 1063 row.add( cell ); 1064 } 1065 1066 /** {@inheritDoc} */ 1067 public void tableCaption() 1068 { 1069 Paragraph p = new Paragraph(); 1070 p.justification = Sink.JUSTIFY_CENTER; 1071 p.spaceBefore /= 2; 1072 beginParagraph( p ); 1073 } 1074 1075 /** {@inheritDoc} */ 1076 public void tableCaption_() 1077 { 1078 endParagraph(); 1079 } 1080 1081 /** {@inheritDoc} */ 1082 public void paragraph() 1083 { 1084 if ( paragraph == null ) 1085 { 1086 beginParagraph( new Paragraph() ); 1087 } 1088 } 1089 1090 /** {@inheritDoc} */ 1091 public void paragraph_() 1092 { 1093 endParagraph(); 1094 } 1095 1096 private void beginParagraph( Paragraph p ) 1097 { 1098 p.begin(); 1099 this.paragraph = p; 1100 if ( style != STYLE_ROMAN ) 1101 { 1102 beginStyle( style ); 1103 } 1104 } 1105 1106 private void endParagraph() 1107 { 1108 if ( paragraph != null ) 1109 { 1110 if ( style != STYLE_ROMAN ) 1111 { 1112 endStyle(); 1113 } 1114 paragraph.end(); 1115 paragraph = null; 1116 } 1117 } 1118 1119 /** {@inheritDoc} */ 1120 public void verbatim( boolean boxed ) 1121 { 1122 verbatim = new StringBuilder(); 1123 frame = boxed; 1124 1125 context.set( CONTEXT_VERBATIM ); 1126 } 1127 1128 /** {@inheritDoc} */ 1129 public void verbatim_() 1130 { 1131 String text = verbatim.toString(); 1132 1133 Paragraph p = new Paragraph(); 1134 p.fontStyle = STYLE_TYPEWRITER; 1135 p.frame = frame; 1136 1137 beginParagraph( p ); 1138 1139 StringTokenizer t = new StringTokenizer( text, EOL, true ); 1140 while ( t.hasMoreTokens() ) 1141 { 1142 String s = t.nextToken(); 1143 if ( s.equals( EOL ) && t.hasMoreTokens() ) 1144 { 1145 writer.println( "\\line" ); 1146 } 1147 else 1148 { 1149 writer.println( escape( s ) ); 1150 } 1151 } 1152 1153 endParagraph(); 1154 1155 context.restore(); 1156 } 1157 1158 /** {@inheritDoc} */ 1159 public void figure() 1160 { 1161 // nop 1162 } 1163 1164 /** {@inheritDoc} */ 1165 public void figure_() 1166 { 1167 // nop 1168 } 1169 1170 /** {@inheritDoc} */ 1171 public void figureGraphics( String name ) 1172 { 1173 Paragraph p = new Paragraph(); 1174 p.justification = Sink.JUSTIFY_CENTER; 1175 beginParagraph( p ); 1176 1177 try 1178 { 1179 writeImage( name ); 1180 } 1181 catch ( Exception e ) 1182 { 1183 getLog().error( e.getMessage(), e ); 1184 } 1185 1186 endParagraph(); 1187 } 1188 1189 private void writeImage( String source ) 1190 throws Exception 1191 { 1192 if ( !source.toLowerCase().endsWith( ".ppm" ) ) 1193 { 1194 // TODO support more image types! 1195 String msg = 1196 "Unsupported image type for image file: '" + source + "'. Only PPM image type is " 1197 + "currently supported."; 1198 logMessage( "unsupportedImage", msg ); 1199 1200 return; 1201 } 1202 1203 int bytesPerLine; 1204 PBMReader ppm = new PBMReader( source ); 1205 WMFWriter.Dib dib = new WMFWriter.Dib(); 1206 WMFWriter wmf = new WMFWriter(); 1207 1208 int srcWidth = ppm.width(); 1209 int srcHeight = ppm.height(); 1210 1211 dib.biWidth = srcWidth; 1212 dib.biHeight = srcHeight; 1213 dib.biXPelsPerMeter = (int) ( resolution * 100. / 2.54 ); 1214 dib.biYPelsPerMeter = dib.biXPelsPerMeter; 1215 1216 if ( imageType.equals( IMG_TYPE_RGB ) ) 1217 { 1218 dib.biBitCount = 24; 1219 dib.biCompression = WMFWriter.Dib.BI_RGB; // no compression 1220 1221 bytesPerLine = 4 * ( ( 3 * srcWidth + 3 ) / 4 ); 1222 dib.bitmap = new byte[srcHeight * bytesPerLine]; 1223 1224 byte[] l = new byte[3 * srcWidth]; 1225 for ( int i = ( srcHeight - 1 ); i >= 0; --i ) 1226 { 1227 ppm.read( l, 0, l.length ); 1228 for ( int j = 0, k = ( i * bytesPerLine ); j < l.length; j += 3 ) 1229 { 1230 // component order = BGR 1231 dib.bitmap[k++] = l[j + 2]; 1232 dib.bitmap[k++] = l[j + 1]; 1233 dib.bitmap[k++] = l[j]; 1234 } 1235 } 1236 } 1237 else 1238 { 1239 dib.biBitCount = 8; 1240 1241 bytesPerLine = 4 * ( ( srcWidth + 3 ) / 4 ); 1242 byte[] bitmap = new byte[srcHeight * bytesPerLine]; 1243 1244 Vector colors = new Vector( 256 ); 1245 colors.addElement( Color.white ); 1246 colors.addElement( Color.black ); 1247 1248 byte[] l = new byte[3 * srcWidth]; 1249 for ( int i = ( srcHeight - 1 ); i >= 0; --i ) 1250 { 1251 ppm.read( l, 0, l.length ); 1252 for ( int j = 0, k = ( i * bytesPerLine ); j < l.length; ) 1253 { 1254 int r = (int) l[j++] & 0xff; 1255 int g = (int) l[j++] & 0xff; 1256 int b = (int) l[j++] & 0xff; 1257 Color color = new Color( r, g, b ); 1258 int index = colors.indexOf( color ); 1259 if ( index < 0 ) 1260 { 1261 if ( colors.size() < colors.capacity() ) 1262 { 1263 colors.addElement( color ); 1264 index = colors.size() - 1; 1265 } 1266 else 1267 { 1268 index = 1; 1269 } 1270 } 1271 bitmap[k++] = (byte) index; 1272 } 1273 } 1274 1275 dib.biClrUsed = colors.size(); 1276 dib.biClrImportant = dib.biClrUsed; 1277 dib.palette = new byte[4 * dib.biClrUsed]; 1278 for ( int i = 0, j = 0; i < dib.biClrUsed; ++i, ++j ) 1279 { 1280 Color color = (Color) colors.elementAt( i ); 1281 dib.palette[j++] = (byte) color.getBlue(); 1282 dib.palette[j++] = (byte) color.getGreen(); 1283 dib.palette[j++] = (byte) color.getRed(); 1284 } 1285 1286 if ( imageCompression ) 1287 { 1288 dib.biCompression = WMFWriter.Dib.BI_RLE8; 1289 dib.bitmap = new byte[bitmap.length + ( 2 * ( bitmap.length / 255 + 1 ) )]; 1290 dib.biSizeImage = WMFWriter.Dib.rlEncode8( bitmap, 0, bitmap.length, dib.bitmap, 0 ); 1291 } 1292 else 1293 { 1294 dib.biCompression = WMFWriter.Dib.BI_RGB; 1295 dib.bitmap = bitmap; 1296 } 1297 } 1298 1299 if ( imageFormat.equals( IMG_FORMAT_WMF ) ) 1300 { 1301 int[] parameters; 1302 WMFWriter.Record record; 1303 1304 /* 1305 * See the libwmf library documentation 1306 * (http://www.wvware.com/wmf_doc_index.html) 1307 * for a description of WMF records. 1308 */ 1309 1310 // set mapping mode to MM_TEXT (logical unit = pixel) 1311 parameters = new int[1]; 1312 parameters[0] = 1; 1313 record = new WMFWriter.Record( 0x0103, parameters ); 1314 wmf.add( record ); 1315 1316 // set window origin and dimensions 1317 parameters = new int[2]; 1318 record = new WMFWriter.Record( 0x020b, parameters ); 1319 wmf.add( record ); 1320 parameters = new int[2]; 1321 parameters[0] = srcHeight; 1322 parameters[1] = srcWidth; 1323 record = new WMFWriter.Record( 0x020c, parameters ); 1324 wmf.add( record ); 1325 1326 parameters = new int[WMFWriter.DibBitBltRecord.P_COUNT]; 1327 // raster operation = SRCCOPY (0x00cc0020) 1328 parameters[WMFWriter.DibBitBltRecord.P_ROP_H] = 0x00cc; 1329 parameters[WMFWriter.DibBitBltRecord.P_ROP_L] = 0x0020; 1330 parameters[WMFWriter.DibBitBltRecord.P_WIDTH] = srcWidth; 1331 parameters[WMFWriter.DibBitBltRecord.P_HEIGHT] = srcHeight; 1332 record = new WMFWriter.DibBitBltRecord( parameters, dib ); 1333 wmf.add( record ); 1334 } 1335 1336 if ( imageFormat.equals( IMG_FORMAT_WMF ) ) 1337 { 1338 writer.print( "{\\pict\\wmetafile1" ); 1339 writer.println( "\\picbmp\\picbpp" + dib.biBitCount ); 1340 } 1341 else 1342 { 1343 writer.print( "{\\pict\\dibitmap0\\wbmplanes1" ); 1344 writer.print( "\\wbmbitspixel" + dib.biBitCount ); 1345 writer.println( "\\wbmwidthbytes" + bytesPerLine ); 1346 } 1347 1348 writer.print( "\\picw" + srcWidth ); 1349 writer.print( "\\pich" + srcHeight ); 1350 writer.print( "\\picwgoal" + toTwips( srcWidth, UNIT_PIXEL ) ); 1351 writer.println( "\\pichgoal" + toTwips( srcHeight, UNIT_PIXEL ) ); 1352 1353 if ( imageFormat.equals( IMG_FORMAT_WMF ) ) 1354 { 1355 if ( imageDataFormat.equals( IMG_DATA_RAW ) ) 1356 { 1357 writer.print( "\\bin" + ( 2 * wmf.size() ) + " " ); 1358 writer.flush(); 1359 wmf.write( stream ); 1360 stream.flush(); 1361 } 1362 else 1363 { 1364 wmf.print( writer ); 1365 } 1366 } 1367 else 1368 { 1369 if ( imageDataFormat.equals( IMG_DATA_RAW ) ) 1370 { 1371 writer.print( "\\bin" + ( 2 * dib.size() ) + " " ); 1372 writer.flush(); 1373 dib.write( stream ); 1374 stream.flush(); 1375 } 1376 else 1377 { 1378 dib.print( writer ); 1379 } 1380 } 1381 1382 writer.println( "}" ); 1383 } 1384 1385 /** {@inheritDoc} */ 1386 public void figureCaption() 1387 { 1388 Paragraph p = new Paragraph(); 1389 p.justification = Sink.JUSTIFY_CENTER; 1390 p.spaceBefore /= 2; 1391 beginParagraph( p ); 1392 } 1393 1394 /** {@inheritDoc} */ 1395 public void figureCaption_() 1396 { 1397 endParagraph(); 1398 } 1399 1400 /** {@inheritDoc} */ 1401 public void horizontalRule() 1402 { 1403 writer.print( "\\pard\\li" + indentation.get() ); 1404 1405 int skip = space.getNext(); 1406 if ( skip > 0 ) 1407 { 1408 writer.print( "\\sb" + skip ); 1409 } 1410 space.setNext( skip ); 1411 1412 writer.print( "\\brdrb\\brdrs\\brdrw" + BORDER_WIDTH ); 1413 writer.println( "\\plain\\fs1\\par" ); 1414 } 1415 1416 /** {@inheritDoc} */ 1417 public void pageBreak() 1418 { 1419 writer.println( "\\page" ); 1420 } 1421 1422 /** {@inheritDoc} */ 1423 public void anchor( String name ) 1424 { 1425 // nop 1426 } 1427 1428 /** {@inheritDoc} */ 1429 public void anchor_() 1430 { 1431 // nop 1432 } 1433 1434 /** {@inheritDoc} */ 1435 public void link( String name ) 1436 { 1437 // nop 1438 } 1439 1440 /** {@inheritDoc} */ 1441 public void link_() 1442 { 1443 // nop 1444 } 1445 1446 /** {@inheritDoc} */ 1447 public void italic() 1448 { 1449 beginStyle( STYLE_ITALIC ); 1450 } 1451 1452 /** {@inheritDoc} */ 1453 public void italic_() 1454 { 1455 endStyle(); 1456 } 1457 1458 /** {@inheritDoc} */ 1459 public void bold() 1460 { 1461 beginStyle( STYLE_BOLD ); 1462 } 1463 1464 /** {@inheritDoc} */ 1465 public void bold_() 1466 { 1467 endStyle(); 1468 } 1469 1470 /** {@inheritDoc} */ 1471 public void monospaced() 1472 { 1473 beginStyle( STYLE_TYPEWRITER ); 1474 } 1475 1476 /** {@inheritDoc} */ 1477 public void monospaced_() 1478 { 1479 endStyle(); 1480 } 1481 1482 private void beginStyle( int style ) 1483 { 1484 this.style = style; 1485 1486 switch ( context.get() ) 1487 { 1488 case CONTEXT_TABLE: 1489 break; 1490 default: 1491 if ( paragraph != null ) 1492 { 1493 switch ( style ) 1494 { 1495 case STYLE_ITALIC: 1496 writer.println( "{\\i" ); 1497 break; 1498 case STYLE_BOLD: 1499 writer.println( "{\\b" ); 1500 break; 1501 case STYLE_TYPEWRITER: 1502 writer.println( "{\\f1" ); 1503 break; 1504 default: 1505 writer.println( "{" ); 1506 break; 1507 } 1508 } 1509 break; 1510 } 1511 } 1512 1513 private void endStyle() 1514 { 1515 style = STYLE_ROMAN; 1516 1517 switch ( context.get() ) 1518 { 1519 case CONTEXT_TABLE: 1520 break; 1521 default: 1522 if ( paragraph != null ) 1523 { 1524 writer.println( "}" ); 1525 } 1526 break; 1527 } 1528 } 1529 1530 /** {@inheritDoc} */ 1531 public void lineBreak() 1532 { 1533 switch ( context.get() ) 1534 { 1535 case CONTEXT_TABLE: 1536 cell.add( line ); 1537 line = new Line(); 1538 break; 1539 default: 1540 writer.println( "\\line" ); 1541 break; 1542 } 1543 } 1544 1545 /** {@inheritDoc} */ 1546 public void nonBreakingSpace() 1547 { 1548 switch ( context.get() ) 1549 { 1550 case CONTEXT_TABLE: 1551 line.add( new Item( style, " " ) ); 1552 break; 1553 default: 1554 writer.println( "\\~" ); 1555 break; 1556 } 1557 } 1558 1559 /** {@inheritDoc} */ 1560 public void text( String text ) 1561 { 1562 switch ( context.get() ) 1563 { 1564 case CONTEXT_VERBATIM: 1565 verbatim.append( text ); 1566 break; 1567 1568 case CONTEXT_TABLE: 1569 StringTokenizer t = new StringTokenizer( text, EOL, true ); 1570 while ( t.hasMoreTokens() ) 1571 { 1572 String token = t.nextToken(); 1573 if ( token.equals( EOL ) ) 1574 { 1575 cell.add( line ); 1576 line = new Line(); 1577 } 1578 else 1579 { 1580 line.add( new Item( style, normalize( token ) ) ); 1581 } 1582 } 1583 break; 1584 1585 default: 1586 if ( paragraph == null ) 1587 { 1588 beginParagraph( new Paragraph() ); 1589 } 1590 writer.println( escape( normalize( text ) ) ); 1591 } 1592 } 1593 1594 /** 1595 * {@inheritDoc} 1596 * 1597 * Unkown events just log a warning message but are ignored otherwise. 1598 * @see org.apache.maven.doxia.sink.Sink#unknown(String,Object[],SinkEventAttributes) 1599 */ 1600 public void unknown( String name, Object[] requiredParams, SinkEventAttributes attributes ) 1601 { 1602 String msg = "Unknown Sink event: '" + name + "', ignoring!"; 1603 logMessage( "unknownEvent", msg ); 1604 } 1605 1606 private static String normalize( String s ) 1607 { 1608 int length = s.length(); 1609 StringBuilder buffer = new StringBuilder( length ); 1610 1611 for ( int i = 0; i < length; ++i ) 1612 { 1613 char c = s.charAt( i ); 1614 1615 if ( Character.isWhitespace( c ) ) 1616 { 1617 if ( buffer.length() == 0 || buffer.charAt( buffer.length() - 1 ) != ' ' ) 1618 { 1619 buffer.append( ' ' ); 1620 } 1621 } 1622 1623 else 1624 { 1625 buffer.append( c ); 1626 } 1627 } 1628 1629 return buffer.toString(); 1630 } 1631 1632 private static String escape( String s ) 1633 { 1634 int length = s.length(); 1635 StringBuilder buffer = new StringBuilder( length ); 1636 1637 for ( int i = 0; i < length; ++i ) 1638 { 1639 char c = s.charAt( i ); 1640 switch ( c ) 1641 { 1642 case '\\': 1643 buffer.append( "\\\\" ); 1644 break; 1645 case '{': 1646 buffer.append( "\\{" ); 1647 break; 1648 case '}': 1649 buffer.append( "\\}" ); 1650 break; 1651 default: 1652 buffer.append( c ); 1653 } 1654 } 1655 1656 return buffer.toString(); 1657 } 1658 1659 /** 1660 * <p>getFont.</p> 1661 * 1662 * @param style a int. 1663 * @param size a int. 1664 * @return a {@link org.apache.maven.doxia.module.rtf.Font} object. 1665 */ 1666 protected Font getFont( int style, int size ) 1667 { 1668 Font font = null; 1669 1670 StringBuilder buf = new StringBuilder(); 1671 buf.append( style ); 1672 buf.append( size ); 1673 String key = buf.toString(); 1674 1675 Object object = fontTable.get( key ); 1676 if ( object == null ) 1677 { 1678 try 1679 { 1680 font = new Font( style, size ); 1681 fontTable.put( key, font ); 1682 } 1683 catch ( Exception ignored ) 1684 { 1685 if ( getLog().isDebugEnabled() ) 1686 { 1687 getLog().debug( ignored.getMessage(), ignored ); 1688 } 1689 } 1690 } 1691 else 1692 { 1693 font = (Font) object; 1694 } 1695 1696 return font; 1697 } 1698 1699 private static int textWidth( String text, Font font ) 1700 { 1701 int width = 0; 1702 StringTokenizer t = new StringTokenizer( text, EOL ); 1703 1704 while ( t.hasMoreTokens() ) 1705 { 1706 int w = font.textExtents( t.nextToken() ).width; 1707 if ( w > width ) 1708 { 1709 width = w; 1710 } 1711 } 1712 1713 return width; 1714 } 1715 1716 1717 /** {@inheritDoc} */ 1718 public void flush() 1719 { 1720 writer.flush(); 1721 } 1722 1723 /** {@inheritDoc} */ 1724 public void close() 1725 { 1726 writer.close(); 1727 1728 if ( getLog().isWarnEnabled() && this.warnMessages != null ) 1729 { 1730 for ( Iterator it = this.warnMessages.entrySet().iterator(); it.hasNext(); ) 1731 { 1732 Map.Entry entry = (Map.Entry) it.next(); 1733 1734 Set set = (Set) entry.getValue(); 1735 1736 for ( Iterator it2 = set.iterator(); it2.hasNext(); ) 1737 { 1738 String msg = (String) it2.next(); 1739 1740 getLog().warn( msg ); 1741 } 1742 } 1743 1744 this.warnMessages = null; 1745 } 1746 1747 init(); 1748 } 1749 1750 /** 1751 * If debug mode is enabled, log the <code>msg</code> as is, otherwise add unique msg in <code>warnMessages</code>. 1752 * 1753 * @param key not null 1754 * @param msg not null 1755 * @see #close() 1756 * @since 1.1.1 1757 */ 1758 private void logMessage( String key, String msg ) 1759 { 1760 msg = "[RTF Sink] " + msg; 1761 if ( getLog().isDebugEnabled() ) 1762 { 1763 getLog().debug( msg ); 1764 1765 return; 1766 } 1767 1768 if ( warnMessages == null ) 1769 { 1770 warnMessages = new HashMap(); 1771 } 1772 1773 Set set = (Set) warnMessages.get( key ); 1774 if ( set == null ) 1775 { 1776 set = new TreeSet(); 1777 } 1778 set.add( msg ); 1779 warnMessages.put( key, set ); 1780 } 1781 1782 /** {@inheritDoc} */ 1783 protected void init() 1784 { 1785 super.init(); 1786 1787 this.fontTable.clear(); 1788 this.context = new Context(); 1789 this.paragraph = null; 1790 this.indentation = new Indentation( 0 ); 1791 this.space = new Space( 20 * DEFAULT_SPACING ); 1792 Font font = getFont( STYLE_BOLD, fontSize ); 1793 if ( font != null ) 1794 { 1795 this.listItemIndent = textWidth( LIST_ITEM_HEADER, font ); 1796 } 1797 this.numbering.clear(); 1798 this.itemNumber.clear(); 1799 this.style = STYLE_ROMAN; 1800 this.sectionLevel = 0; 1801 this.emptyHeader = false; 1802 this.verbatim = null; 1803 this.frame = false; 1804 this.table = null; 1805 this.row = null; 1806 this.cell = null; 1807 this.line = null; 1808 this.warnMessages = null; 1809 } 1810 1811 // ----------------------------------------------------------------------- 1812 1813 static class Counter 1814 { 1815 private int value; 1816 1817 Counter( int value ) 1818 { 1819 set( value ); 1820 } 1821 1822 void set( int value ) 1823 { 1824 this.value = value; 1825 } 1826 1827 int get() 1828 { 1829 return value; 1830 } 1831 1832 void increment() 1833 { 1834 increment( 1 ); 1835 } 1836 1837 void increment( int value ) 1838 { 1839 this.value += value; 1840 } 1841 } 1842 1843 static class Context 1844 { 1845 private int context = CONTEXT_UNDEFINED; 1846 1847 private Vector stack = new Vector(); 1848 1849 void set( int context ) 1850 { 1851 stack.addElement( Integer.valueOf( this.context ) ); 1852 this.context = context; 1853 } 1854 1855 void restore() 1856 { 1857 if ( !stack.isEmpty() ) 1858 { 1859 context = ( (Integer) stack.lastElement() ).intValue(); 1860 stack.removeElementAt( stack.size() - 1 ); 1861 } 1862 } 1863 1864 int get() 1865 { 1866 return context; 1867 } 1868 } 1869 1870 class Paragraph 1871 { 1872 int style = 0; 1873 1874 int justification = Sink.JUSTIFY_LEFT; 1875 1876 int leftIndent = indentation.get(); 1877 1878 int rightIndent = 0; 1879 1880 int firstLineIndent = 0; 1881 1882 int spaceBefore = space.getNext(); 1883 1884 int spaceAfter = 0; 1885 1886 boolean frame = false; 1887 1888 int fontStyle = STYLE_ROMAN; 1889 1890 int fontSize = RtfSink.this.fontSize; 1891 1892 Paragraph() 1893 { 1894 // nop 1895 } 1896 1897 Paragraph( int style, int size ) 1898 { 1899 fontStyle = style; 1900 fontSize = size; 1901 } 1902 1903 void begin() 1904 { 1905 writer.print( "\\pard" ); 1906 if ( style > 0 ) 1907 { 1908 writer.print( "\\s" + style ); 1909 } 1910 switch ( justification ) 1911 { 1912 case Sink.JUSTIFY_LEFT: 1913 default: 1914 break; 1915 case Sink.JUSTIFY_CENTER: 1916 writer.print( "\\qc" ); 1917 break; 1918 case Sink.JUSTIFY_RIGHT: 1919 writer.print( "\\qr" ); 1920 break; 1921 } 1922 if ( leftIndent != 0 ) 1923 { 1924 writer.print( "\\li" + leftIndent ); 1925 } 1926 if ( rightIndent != 0 ) 1927 { 1928 writer.print( "\\ri" + rightIndent ); 1929 } 1930 if ( firstLineIndent != 0 ) 1931 { 1932 writer.print( "\\fi" + firstLineIndent ); 1933 } 1934 if ( spaceBefore != 0 ) 1935 { 1936 writer.print( "\\sb" + spaceBefore ); 1937 } 1938 if ( spaceAfter != 0 ) 1939 { 1940 writer.print( "\\sa" + spaceAfter ); 1941 } 1942 1943 if ( frame ) 1944 { 1945 writer.print( "\\box\\brdrs\\brdrw" + BORDER_WIDTH ); 1946 } 1947 1948 writer.print( "\\plain" ); 1949 switch ( fontStyle ) 1950 { 1951 case STYLE_ROMAN: 1952 default: 1953 writer.print( "\\f0" ); 1954 break; 1955 case STYLE_ITALIC: 1956 writer.print( "\\f0\\i" ); 1957 break; 1958 case STYLE_BOLD: 1959 writer.print( "\\f0\\b" ); 1960 break; 1961 case STYLE_TYPEWRITER: 1962 writer.print( "\\f1" ); 1963 break; 1964 } 1965 writer.println( "\\fs" + ( 2 * fontSize ) ); 1966 } 1967 1968 void end() 1969 { 1970 writer.println( "\\par" ); 1971 } 1972 } 1973 1974 class Space 1975 { 1976 private int space; 1977 1978 private int next; 1979 1980 private Vector stack = new Vector(); 1981 1982 Space( int space /*twips*/ ) 1983 { 1984 this.space = space; 1985 next = space; 1986 } 1987 1988 void set( int space /*twips*/ ) 1989 { 1990 stack.addElement( Integer.valueOf( this.space ) ); 1991 this.space = space; 1992 next = space; 1993 } 1994 1995 int get() 1996 { 1997 return space; 1998 } 1999 2000 void restore() 2001 { 2002 if ( !stack.isEmpty() ) 2003 { 2004 space = ( (Integer) stack.lastElement() ).intValue(); 2005 stack.removeElementAt( stack.size() - 1 ); 2006 next = space; 2007 } 2008 } 2009 2010 void setNext( int space /*twips*/ ) 2011 { 2012 next = space; 2013 } 2014 2015 int getNext() 2016 { 2017 int nxt = this.next; 2018 this.next = space; 2019 return nxt; 2020 } 2021 2022 void skip() 2023 { 2024 skip( getNext() ); 2025 } 2026 2027 void skip( int space /*twips*/ ) 2028 { 2029 writer.print( "\\pard" ); 2030 if ( ( space -= 10 ) > 0 ) 2031 { 2032 writer.print( "\\sb" + space ); 2033 } 2034 writer.println( "\\plain\\fs1\\par" ); 2035 } 2036 } 2037 2038 static class Indentation 2039 { 2040 private int indent; 2041 2042 private Vector stack = new Vector(); 2043 2044 Indentation( int indent /*twips*/ ) 2045 { 2046 this.indent = indent; 2047 } 2048 2049 void set( int indent /*twips*/ ) 2050 { 2051 stack.addElement( Integer.valueOf( this.indent ) ); 2052 this.indent = indent; 2053 } 2054 2055 int get() 2056 { 2057 return indent; 2058 } 2059 2060 void restore() 2061 { 2062 if ( !stack.isEmpty() ) 2063 { 2064 indent = ( (Integer) stack.lastElement() ).intValue(); 2065 stack.removeElementAt( stack.size() - 1 ); 2066 } 2067 } 2068 2069 void add( int indent /*twips*/ ) 2070 { 2071 set( this.indent + indent ); 2072 } 2073 } 2074 2075 static class Table 2076 { 2077 int numColumns; 2078 2079 int[] columnWidths; 2080 2081 int[] justification; 2082 2083 boolean grid; 2084 2085 Vector rows; 2086 2087 Table( int[] justification, boolean grid ) 2088 { 2089 numColumns = justification.length; 2090 columnWidths = new int[numColumns]; 2091 this.justification = justification; 2092 this.grid = grid; 2093 rows = new Vector(); 2094 } 2095 2096 void add( Row row ) 2097 { 2098 rows.addElement( row ); 2099 2100 for ( int i = 0; i < numColumns; ++i ) 2101 { 2102 if ( i >= row.cells.size() ) 2103 { 2104 break; 2105 } 2106 Cell cell = (Cell) row.cells.elementAt( i ); 2107 int width = cell.boundingBox().width; 2108 if ( width > columnWidths[i] ) 2109 { 2110 columnWidths[i] = width; 2111 } 2112 } 2113 } 2114 2115 int width() 2116 { 2117 int width = 0; 2118 for ( int i = 0; i < numColumns; ++i ) 2119 { 2120 width += columnWidths[i]; 2121 } 2122 if ( grid ) 2123 { 2124 width += ( numColumns + 1 ) * BORDER_WIDTH; 2125 } 2126 return width; 2127 } 2128 } 2129 2130 static class Row 2131 { 2132 Vector cells = new Vector(); 2133 2134 void add( Cell cell ) 2135 { 2136 cells.addElement( cell ); 2137 } 2138 2139 int height() 2140 { 2141 int height = 0; 2142 int numCells = cells.size(); 2143 for ( int i = 0; i < numCells; ++i ) 2144 { 2145 Cell cell = (Cell) cells.elementAt( i ); 2146 Box box = cell.boundingBox(); 2147 if ( box.height > height ) 2148 { 2149 height = box.height; 2150 } 2151 } 2152 return height; 2153 } 2154 } 2155 2156 class Cell 2157 { 2158 Vector lines = new Vector(); 2159 2160 void add( Line line ) 2161 { 2162 lines.addElement( line ); 2163 } 2164 2165 Box boundingBox() 2166 { 2167 int width = 0; 2168 int height = 0; 2169 2170 for ( int i = 0; i < lines.size(); ++i ) 2171 { 2172 int w = 0; 2173 int h = 0; 2174 Line line = (Line) lines.elementAt( i ); 2175 2176 for ( int j = 0; j < line.items.size(); ++j ) 2177 { 2178 Item item = (Item) line.items.elementAt( j ); 2179 Font font = getFont( item.style, fontSize ); 2180 if ( font == null ) 2181 { 2182 continue; 2183 } 2184 Font.TextExtents x = font.textExtents( item.text ); 2185 w += x.width; 2186 if ( x.height > h ) 2187 { 2188 h = x.height; 2189 } 2190 } 2191 2192 if ( w > width ) 2193 { 2194 width = w; 2195 } 2196 height += h; 2197 } 2198 2199 width += ( 2 * CELL_HORIZONTAL_PAD ); 2200 height += ( 2 * CELL_VERTICAL_PAD ); 2201 2202 // allow one more pixel for grid outline 2203 width += toTwips( 1., UNIT_PIXEL ); 2204 2205 return new Box( width, height ); 2206 } 2207 } 2208 2209 static class Line 2210 { 2211 Vector items = new Vector(); 2212 2213 void add( Item item ) 2214 { 2215 items.addElement( item ); 2216 } 2217 } 2218 2219 static class Item 2220 { 2221 int style; 2222 2223 String text; 2224 2225 Item( int style, String text ) 2226 { 2227 this.style = style; 2228 this.text = text; 2229 } 2230 } 2231 2232 static class Box 2233 { 2234 int width; 2235 2236 int height; 2237 2238 Box( int width, int height ) 2239 { 2240 this.width = width; 2241 this.height = height; 2242 } 2243 } 2244}