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