You are viewing a plain text version of this content. The canonical link for it is here.
Posted to fop-commits@xmlgraphics.apache.org by ar...@apache.org on 2001/02/11 06:37:22 UTC

cvs commit: xml-fop/jpfop-0.17.0/src/org/apache/fop/layout LineArea.java

arved       01/02/10 21:37:22

  Added:       jpfop-0.17.0/src/org/apache/fop/layout LineArea.java
  Log:
  Modified JPFOP line area class
  
  Revision  Changes    Path
  1.1                  xml-fop/jpfop-0.17.0/src/org/apache/fop/layout/LineArea.java
  
  Index: LineArea.java
  ===================================================================
  /*-- $Id: LineArea.java,v 1.1 2001/02/11 05:37:21 arved Exp $ --
  
   ============================================================================
                     The Apache Software License, Version 1.1
   ============================================================================
  
      Copyright (C) 1999 The Apache Software Foundation. All rights reserved.
  
   Redistribution and use in source and binary forms, with or without modifica-
   tion, are permitted provided that the following conditions are met:
  
   1. Redistributions of  source code must  retain the above copyright  notice,
      this list of conditions and the following disclaimer.
  
   2. Redistributions in binary form must reproduce the above copyright notice,
      this list of conditions and the following disclaimer in the documentation
      and/or other materials provided with the distribution.
  
   3. The end-user documentation included with the redistribution, if any, must
      include  the following  acknowledgment:  "This product includes  software
      developed  by the  Apache Software Foundation  (http://www.apache.org/)."
      Alternately, this  acknowledgment may  appear in the software itself,  if
      and wherever such third-party acknowledgments normally appear.
  
   4. The names "FOP" and  "Apache Software Foundation"  must not be used to
      endorse  or promote  products derived  from this  software without  prior
      written permission. For written permission, please contact
      apache@apache.org.
  
   5. Products  derived from this software may not  be called "Apache", nor may
      "Apache" appear  in their name,  without prior written permission  of the
      Apache Software Foundation.
  
   THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
   INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
   FITNESS  FOR A PARTICULAR  PURPOSE ARE  DISCLAIMED.  IN NO  EVENT SHALL  THE
   APACHE SOFTWARE  FOUNDATION  OR ITS CONTRIBUTORS  BE LIABLE FOR  ANY DIRECT,
   INDIRECT, INCIDENTAL, SPECIAL,  EXEMPLARY, OR CONSEQUENTIAL  DAMAGES (INCLU-
   DING, BUT NOT LIMITED TO, PROCUREMENT  OF SUBSTITUTE GOODS OR SERVICES; LOSS
   OF USE, DATA, OR  PROFITS; OR BUSINESS  INTERRUPTION)  HOWEVER CAUSED AND ON
   ANY  THEORY OF LIABILITY,  WHETHER  IN CONTRACT,  STRICT LIABILITY,  OR TORT
   (INCLUDING  NEGLIGENCE OR  OTHERWISE) ARISING IN  ANY WAY OUT OF THE  USE OF
   THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  
   This software  consists of voluntary contributions made  by many individuals
   on  behalf of the Apache Software  Foundation and was  originally created by
   James Tauber <jt...@jtauber.com>. For more  information on the Apache
   Software Foundation, please see <http://www.apache.org/>.
  
   */
  
  package org.apache.fop.layout;
  
  //fop
  import org.apache.fop.render.Renderer;
  import org.apache.fop.messaging.MessageHandler;
  import org.apache.fop.layout.inline.*;
  import org.apache.fop.datatypes.IDNode;
  import org.apache.fop.fo.properties.WrapOption;
  import org.apache.fop.fo.properties.WhiteSpaceCollapse;
  import org.apache.fop.fo.properties.TextAlign;
  import org.apache.fop.fo.properties.TextAlignLast;
  import org.apache.fop.fo.properties.LeaderPattern;
  import org.apache.fop.fo.properties.Hyphenate;
  import org.apache.fop.fo.properties.CountryMaker;
  import org.apache.fop.fo.properties.LanguageMaker;
  import org.apache.fop.fo.properties.LeaderAlignment;
  import org.apache.fop.fo.properties.VerticalAlign;
  import org.apache.fop.layout.hyphenation.Hyphenation;
  import org.apache.fop.layout.hyphenation.Hyphenator;
  
  //java
  import java.util.Vector;
  import java.util.Enumeration;
  import java.awt.Rectangle;
  
  
  public class LineArea extends Area {
  
      protected int lineHeight;
      protected int halfLeading;
      protected int nominalFontSize;
      protected int nominalGlyphHeight;
  
      protected int allocationHeight;
      protected int startIndent;
      protected int endIndent;
  
      private int placementOffset;
  
      private FontState currentFontState; // not the nominal, which is
      // in this.fontState
      private float red, green, blue;
      private int wrapOption;
      private int whiteSpaceCollapse;
      int vAlign;
  
      /*hyphenation*/
      protected int hyphenate;
      protected char hyphenationChar;
      protected int hyphenationPushCharacterCount;
      protected int hyphenationRemainCharacterCount;
      protected String language;
      protected String country;
  
      /* the width of text that has definitely made it into the line
         area */
      protected int finalWidth = 0;
  
      /* the position to shift a link rectangle in order to compensate for links embedded within a word*/
      protected int embeddedLinkStart = 0;
  
      /* the width of the current word so far */
  //    protected int wordWidth = 0;
  
      /* values that prev (below) may take */
      protected static final int NOTHING = 0;
      protected static final int WHITESPACE = 1;
      protected static final int TEXT = 2;
  
      /* the character type of the previous character */
      protected int prev = NOTHING;
  
      /* the position in data[] of the start of the current word */
  //    protected int wordStart;
  
      /* the length (in characters) of the current word */
  //    protected int wordLength = 0;
  
      /* width of spaces before current word */
      protected int spaceWidth = 0;
  
      /* the inline areas that have not yet been added to the line
         because subsequent characters to come (in a different addText)
         may be part of the same word */
      protected Vector pendingAreas = new Vector();
  
      /* the width of the pendingAreas */
      protected int pendingWidth = 0;
  
      /* text-decoration of the previous text */
      protected boolean prevUlState = false;
      protected boolean prevOlState = false;
      protected boolean prevLTState = false;
  
      public LineArea(FontState fontState, int lineHeight,
                      int halfLeading, int allocationWidth, int startIndent,
                      int endIndent, LineArea prevLineArea) {
          super(fontState);
  
          this.currentFontState = fontState;
          this.lineHeight = lineHeight;
          this.nominalFontSize = fontState.getFontSize();
          this.nominalGlyphHeight =
            fontState.getAscender() - fontState.getDescender();
  
          this.placementOffset = fontState.getAscender();
          this.contentRectangleWidth =
            allocationWidth - startIndent - endIndent;
          this.fontState = fontState;
  
          this.allocationHeight = this.nominalGlyphHeight;
          this.halfLeading = this.lineHeight - this.allocationHeight;
  
          this.startIndent = startIndent;
          this.endIndent = endIndent;
  
          if (prevLineArea != null) {
              Enumeration e = prevLineArea.pendingAreas.elements();
              while (e.hasMoreElements()) {
                  pendingAreas.addElement(e.nextElement());
              }
              pendingWidth = prevLineArea.getPendingWidth();
          }
      }
  
      public void render(Renderer renderer) {
          renderer.renderLineArea(this);
      }
  
      public int addPageNumberCitation(String refid, LinkSet ls) {
  
          /* We should add code here to handle the case where the page number doesn't fit on the current line
          */
  
          //Space must be alloted to the page number, so currently we give it 3 spaces
          int width = currentFontState.width(32) * 3;
  
          PageNumberInlineArea pia =
            new PageNumberInlineArea(currentFontState, this.red,
                                     this.green, this.blue, refid, width);
  
          pia.setYOffset(placementOffset);
          pendingAreas.addElement(pia);
          pendingWidth += width;
          prev = TEXT;
  
          return -1;
      }
  
  
      /**
         * adds text to line area
         *
         * @return int character position
         */
      public int addText(char odata[], int start, int end, LinkSet ls,
                         TextState textState) {
          // this prevents an array index out of bounds
          // which occurs when some text is laid out again.
          if(start == -1) return -1;
          boolean overrun = false;
  
  		//ForJAPANIZE
  		boolean isCJKChar = false;
  
          int wordStart = start;
          int wordLength = 0;
          int wordWidth = 0;
          char[] data = new char[odata.length];
          for (int count = 0; count < odata.length; count++) {
              data[count] = odata[count];
          }
  
          /* iterate over each character */
          for (int i = start; i < end; i++) {
              int charWidth;
              /* get the character */
              char c = data[i];
  
              if (c > 127) {
                  /* this class shouldn't be hard coded */
                  char d = org.apache.fop.render.pdf.CodePointMapping.map[c];
                  if (d != 0) {
                      c = data[i] = d;
                  } else {
  					// ForJAPANIZE
  					// MessageHandler.error("ch" + (int) c + "?");
  					// c = data[i] = '#';
  					isCJKChar = true;
                  }
              }
  
              charWidth = currentFontState.width(c);
  
              if ((c == ' ') || (c == '\n') || (c == '\r') ||
                      (c == '\t')) { // whitespace
  
                  if (prev == WHITESPACE) {
  
                      // if current & previous are WHITESPACE
  
                      if (this.whiteSpaceCollapse ==
                              WhiteSpaceCollapse.FALSE) {
                          if (c == ' ') {
                              spaceWidth += currentFontState.width(32);
                          } else if (c == '\n') {
                              // force line break
                              return i;
                          } else if (c == '\t') {
                              spaceWidth += 8 * currentFontState.width(32);
                          }
                      } // else ignore it
  
                  } else if (prev == TEXT) {
  
                      // if current is WHITESPACE and previous TEXT
                      // the current word made it, so
                      // add the space before the current word (if there
                      // was some)
  
                      if (spaceWidth > 0) {
                          InlineSpace is = new InlineSpace(spaceWidth);
                          if (prevUlState) {
                              is.setUnderlined(textState.getUnderlined());
                          }
                          if (prevOlState) {
                              is.setOverlined(textState.getOverlined());
                          }
                          if (prevLTState) {
                              is.setLineThrough(textState.getLineThrough());
                          }
                          addChild(is);
  						//ForJAPANIZE
  						isCJKChar = false;
                          finalWidth += spaceWidth;
                          spaceWidth = 0;
                      }
  
                      // add any pending areas
  
                      Enumeration e = pendingAreas.elements();
                      while (e.hasMoreElements()) {
                          Box box = (Box) e.nextElement();
                          if (box instanceof InlineArea) {
                              if (ls != null) {
                                  Rectangle lr = new Rectangle(finalWidth, 0,
                                                               ((InlineArea) box).
                                                               getContentWidth(),
                                                               fontState.getFontSize());
                                  ls.addRect(lr, this);
                              }
                          }
                          addChild(box);
  						//ForJAPANIZE
  						isCJKChar = false;
                      }
  
                      finalWidth += pendingWidth;
  
                      // reset pending areas array
                      pendingWidth = 0;
                      pendingAreas = new Vector();
  
                      // add the current word
  
                      if (wordLength > 0) {
                          WordArea ia = new WordArea(currentFontState,
                                                         this.red, this.green, this.blue,
                                                         new String(data, wordStart,
                                                                    wordLength), wordWidth);
                          ia.setYOffset(placementOffset);
                          ia.setUnderlined(textState.getUnderlined());
                          prevUlState = textState.getUnderlined();
                          ia.setOverlined(textState.getOverlined());
                          prevOlState = textState.getOverlined();
                          ia.setLineThrough(textState.getLineThrough());
                          prevLTState = textState.getLineThrough();
                          ia.setVerticalAlign(vAlign);
  
                          addChild(ia);
  						//ForJAPANIZE
  						isCJKChar = false;
                          if (ls != null) {
                              Rectangle lr = new Rectangle(finalWidth, 0,
                                                           ia.getContentWidth(),
                                                           fontState.getFontSize());
                              ls.addRect(lr, this);
                          }
                          finalWidth += wordWidth;
  
                          // reset word width
                          wordWidth = 0;
                      }
  
                      // deal with this new whitespace following the
                      // word we just added
  
                      prev = WHITESPACE;
  
                      embeddedLinkStart = 0; //reset embeddedLinkStart since a space was encountered
  
                      spaceWidth = currentFontState.width(32);
  
                      /*
                      here is the place for space-treatment value 'ignore':
                      if (this.spaceTreatment ==
                              SpaceTreatment.IGNORE) {
                          // do nothing
                  } else {
                              spaceWidth = currentFontState.width(32);
                  }
  
                      */
  
  
                      if (this.whiteSpaceCollapse ==
                              WhiteSpaceCollapse.FALSE) {
                          if (c == '\n') {
                              // force a line break
                              return i;
                          } else if (c == '\t') {
                              spaceWidth = currentFontState.width(32);
                          }
                      }
  
                  } else {
  
                      // if current is WHITESPACE and no previous
  
                      if (this.whiteSpaceCollapse ==
                              WhiteSpaceCollapse.FALSE) {
                          prev = WHITESPACE;
                          spaceWidth = currentFontState.width(32);
                      } else {
                          // skip over it
                          start++;
                      }
                  }
  
              } else { // current is TEXT
  
                  if (prev == WHITESPACE) {
  
                      // if current is TEXT and previous WHITESPACE
  
                      wordWidth = charWidth;
                      if ((finalWidth + spaceWidth + wordWidth) >
                              this.getContentWidth()) {
                          if (overrun)
                              MessageHandler.error(">");
                          if (this.wrapOption == WrapOption.WRAP)
                              return i;
                      }
                      prev = TEXT;
                      wordStart = i;
                      wordLength = 1;
                  } else if (prev == TEXT) {
  
                      wordLength++;
                      wordWidth += charWidth;
                  } else { // nothing previous
  
                      prev = TEXT;
                      wordStart = i;
                      wordLength = 1;
                      wordWidth = charWidth;
                  }
  
                  if ((finalWidth + spaceWidth + pendingWidth +
                          wordWidth) > this.getContentWidth()) {
  
  					//ForJAPANIZE
  					if ( isCJKChar && this.wrapOption == WrapOption.WRAP &&
  															charWidth <= this.getContentWidth() ) {
  						// The word does not separated WHITSPACE in CJK language .
  						// So, if the word is overran, it is nessesaly to addChild .
  						WordArea ia = new WordArea(currentFontState,
  							this.red, this.green, this.blue,
  							new String(data, wordStart,wordLength-1), wordWidth-charWidth);
  						ia.setUnderlined(textState.getUnderlined());
  						addChild(ia);
  						isCJKChar = false;
  						if (ls != null) {
  							Rectangle lr = new Rectangle(finalWidth, 0,
  								ia.getContentWidth(),fontState.getFontSize());
  							ls.addRect(lr, this);
  						}
  						finalWidth += wordWidth;
  						return i;
  					}
  					
                      // BREAK MID WORD
                      if (wordStart == start) { // if couldn't even fit
                          // first word
                          overrun = true;
                          // if not at start of line, return word start
                          // to try again on a new line
                          if (finalWidth > 0) {
                              return wordStart;
                          }
                      } else if (this.wrapOption == WrapOption.WRAP) {
                        if (this.hyphenate == Hyphenate.TRUE) {
                          return this.doHyphenation(data,i,wordStart,this.getContentWidth() - (finalWidth + spaceWidth + pendingWidth));
                        } else {
                          return wordStart;
                        }
                      }
                  }
  
              }
          } // end of iteration over text
  
          if (prev == TEXT) { 
  
              if (spaceWidth > 0) {
                  InlineSpace pis = new InlineSpace(spaceWidth);
                  if (prevUlState) {
                      pis.setUnderlined(textState.getUnderlined());
                  }
                  if (prevOlState) {
                      pis.setOverlined(textState.getOverlined());
                  }
                  if (prevLTState) {
                      pis.setLineThrough(textState.getLineThrough());
                  }
                  pendingAreas.addElement(pis);
                  pendingWidth += spaceWidth;
                  spaceWidth = 0;
              }
  
              WordArea pia = new WordArea(currentFontState, this.red,
                                              this.green, this.blue,
                                              new String(data, wordStart, wordLength), wordWidth);
  
              pia.setYOffset(placementOffset);
              pia.setUnderlined(textState.getUnderlined());
              prevUlState = textState.getUnderlined();
              pia.setOverlined(textState.getOverlined());
              prevOlState = textState.getOverlined();
              pia.setLineThrough(textState.getLineThrough());
              prevLTState = textState.getLineThrough();
              pia.setVerticalAlign(vAlign);
  
              if (ls != null) {
                  Rectangle lr = new Rectangle(finalWidth + spaceWidth +
                                               embeddedLinkStart, spaceWidth,
                                               pia.getContentWidth(), fontState.getFontSize());
                  ls.addRect(lr, this);
              }
  
              embeddedLinkStart += wordWidth;
              pendingAreas.addElement(pia);
              pendingWidth += wordWidth;
              wordWidth = 0;
          }
  
          if (overrun)
              MessageHandler.error(">");
          return -1;
      }
  
      /**
         * adds a Leader; actually the method receives the leader properties
         * and creates a leader area or an inline area which is appended to
         * the children of the containing line area. <br>
         * leader pattern use-content is not implemented.
         */
      public void addLeader(int leaderPattern, int leaderLengthMinimum,
                            int leaderLengthOptimum, int leaderLengthMaximum,
                            int ruleStyle, int ruleThickness, int leaderPatternWidth,
                            int leaderAlignment) {
          WordArea leaderPatternArea;
          int leaderLength = 0;
          int remainingWidth =
            this.getContentWidth() - this.getCurrentXPosition();
          /** checks whether leaderLenghtOptimum fits into rest of line;
           *  should never overflow, as it has been checked already in BlockArea
           *  first check: use remaining width if it smaller than optimum oder maximum
           * */
          if ((remainingWidth <= leaderLengthOptimum) ||   (remainingWidth <= leaderLengthMaximum)) {
              leaderLength = remainingWidth;
          } else if ((remainingWidth > leaderLengthOptimum) && ( remainingWidth > leaderLengthMaximum)) {
              leaderLength = leaderLengthMaximum;
          } else if ((leaderLengthOptimum > leaderLengthMaximum) && (leaderLengthOptimum < remainingWidth)) {
              leaderLength = leaderLengthOptimum;
          }
          switch (leaderPattern) {
              case LeaderPattern.SPACE:
                  //whitespace setting must be false for this
                  int whiteSpaceSetting = this.whiteSpaceCollapse;
                  this.changeWhiteSpaceCollapse(WhiteSpaceCollapse.FALSE);
                  pendingAreas.addElement(
                    this.buildSimpleLeader(32, leaderLength));
                  this.changeWhiteSpaceCollapse(whiteSpaceSetting);
                  break;
              case LeaderPattern.RULE:
                  LeaderArea leaderArea =
                    new LeaderArea(fontState, red, green, blue, "",
                                   leaderLength, leaderPattern, ruleThickness,
                                   ruleStyle);
                  leaderArea.setYOffset(placementOffset);
                  pendingAreas.addElement(leaderArea);
                  break;
              case LeaderPattern.DOTS:
                  //if the width of a dot is larger than leader-pattern-width
                  //ignore this property
                  if (leaderPatternWidth < this.currentFontState.width(46)) {
                      leaderPatternWidth = 0;
                  }
                  //if value of leader-pattern-width is 'use-font-metrics' (0)
                  if (leaderPatternWidth == 0) {
                      pendingAreas.addElement(
                        this.buildSimpleLeader(46, leaderLength));
                  } else {
                      //if leader-alignment is used, calculate space to insert before leader
                      //so that all dots will be parallel.
                      if (leaderAlignment == LeaderAlignment.REFERENCE_AREA) {
                          int spaceBeforeLeader = this.getLeaderAlignIndent(
                                                    leaderLength, leaderPatternWidth);
                          //appending indent space leader-alignment
                          //setting InlineSpace to false, so it is not used in line justification
                          if (spaceBeforeLeader != 0) {
                              pendingAreas.addElement(
                                new InlineSpace(spaceBeforeLeader,
                                                false));
                              pendingWidth += spaceBeforeLeader;
                              //shorten leaderLength, otherwise - in case of
                              //leaderLength=remaining length - it will cut off the end of
                              //leaderlength
                              leaderLength -= spaceBeforeLeader;
                          }
                      }
  
                      // calculate the space to insert between the dots and create a
                      //inline area with this width
                      InlineSpace spaceBetweenDots =
                        new InlineSpace(leaderPatternWidth -
                                        this.currentFontState.width(46), false);
                      leaderPatternArea =
                        new WordArea(currentFontState, this.red,
                                       this.green, this.blue, new String ("."),
                                       this.currentFontState.width(46));
                      leaderPatternArea.setYOffset(placementOffset);
                      int dotsFactor = (int) Math.floor (
                                         ((double) leaderLength) /
                                         ((double) leaderPatternWidth));
  
                      //add combination of dot + space to fill leader
                      //is there a way to do this in a more effective way?
                      for (int i = 0; i < dotsFactor; i++) {
                          pendingAreas.addElement(leaderPatternArea);
                          pendingAreas.addElement(spaceBetweenDots);
                      }
                      //append at the end some space to fill up to leader length
                      pendingAreas.addElement( new InlineSpace(leaderLength -
                                               dotsFactor * leaderPatternWidth));
                  }
                  break;
                  //leader pattern use-content not implemented.
              case LeaderPattern.USECONTENT:
                  MessageHandler.errorln(
                    "leader-pattern=\"use-content\" not " + "supported by this version of Fop");
                  return;
          }
          //adds leader length to length of pending inline areas
          pendingWidth += leaderLength;
          //sets prev to TEXT and makes so sure, that also blocks only
          //containing leaders are processed
          prev = TEXT;
      }
  
      /**
         * adds pending inline areas to the line area
         * normally done, when the line area is filled and
         * added as child to the parent block area
         */
      public void addPending() {
          if (spaceWidth > 0) {
              addChild(new InlineSpace(spaceWidth));
              finalWidth += spaceWidth;
              spaceWidth = 0;
          }
  
          Enumeration e = pendingAreas.elements();
          while (e.hasMoreElements()) {
              Box box = (Box) e.nextElement();
              addChild(box);
          }
  
          finalWidth += pendingWidth;
  
          // reset pending areas array
          pendingWidth = 0;
          pendingAreas = new Vector();
      }
  
      /**
         * aligns line area
         *
         */
      public void align(int type) {
          int padding = 0;
  
          switch (type) {
              case TextAlign.START: // left
                  padding = this.getContentWidth() - finalWidth;
                  endIndent += padding;
                  break;
              case TextAlign.END: // right
                  padding = this.getContentWidth() - finalWidth;
                  startIndent += padding;
                  break;
              case TextAlign.CENTER: // center
                  padding = (this.getContentWidth() - finalWidth) / 2;
                  startIndent += padding;
                  endIndent += padding;
                  break;
              case TextAlign.JUSTIFY: // justify
                  Vector spaceList = new Vector();
  
                  int spaceCount = 0;
                  Enumeration e = children.elements();
                  while (e.hasMoreElements()) {
                      Box b = (Box) e.nextElement();
                      if (b instanceof InlineSpace) {
                          InlineSpace space = (InlineSpace) b;
                          if (space.getResizeable()) {
                              spaceList.addElement(space);
                              spaceCount++;
                          }
                      }
                  }
                  if (spaceCount > 0) {
                      padding = (this.getContentWidth() - finalWidth) /
                                spaceCount;
                  } else { // no spaces
                      padding = 0;
                  }
                  Enumeration f = spaceList.elements();
                  while (f.hasMoreElements()) {
                      InlineSpace space2 = (InlineSpace) f.nextElement();
                      int i = space2.getSize();
                      space2.setSize(i + padding);
                  }
          }
      }
  
      /**
       * Balance (vertically) the inline areas within this line.
       */
      public void verticalAlign()
      {
          int superHeight = -this.placementOffset;
          int maxHeight = this.allocationHeight;
          Enumeration e = children.elements();
          while (e.hasMoreElements()) {
              Box b = (Box) e.nextElement();
              if(b instanceof InlineArea) {
                  InlineArea ia = (InlineArea)b;
                  if(ia instanceof WordArea) {
                      ia.setYOffset(placementOffset);
                  }
                  if(ia.getHeight() > maxHeight) {
                      maxHeight = ia.getHeight();
                  }
                  int vert = ia.getVerticalAlign();
                  if(vert == VerticalAlign.SUPER) {
                      int fh = fontState.getAscender();
                      ia.setYOffset((int)(placementOffset - (fh / 3.0)));
                  } else if(vert == VerticalAlign.SUB) {
                      int fh = fontState.getAscender();
                      ia.setYOffset((int)(placementOffset + (fh / 3.0)));
                  }
              } else {
              }
          }
          // adjust the height of this line to the
          // resulting alignment height.
          this.allocationHeight = maxHeight;
      }
  
      public void changeColor(float red, float green, float blue) {
          this.red = red;
          this.green = green;
          this.blue = blue;
      }
  
      public void changeFont(FontState fontState) {
          this.currentFontState = fontState;
      }
  
      public void changeWhiteSpaceCollapse(int whiteSpaceCollapse) {
          this.whiteSpaceCollapse = whiteSpaceCollapse;
      }
  
      public void changeWrapOption(int wrapOption) {
          this.wrapOption = wrapOption;
      }
  
      public void changeVerticalAlign(int vAlign) {
          this.vAlign = vAlign;
      }
  
      public int getEndIndent() {
          return endIndent;
      }
  
      public int getHeight() {
          return this.allocationHeight;
      }
  
      public int getPlacementOffset() {
          return this.placementOffset;
      }
  
      public int getStartIndent() {
          return startIndent;
      }
  
      public boolean isEmpty() {
          return !(pendingAreas.size() > 0 || children.size() > 0);
  //        return (prev == NOTHING);
      }
  
      public Vector getPendingAreas() {
          return pendingAreas;
      }
  
      public int getPendingWidth() {
          return pendingWidth;
      }
  
      public void setPendingAreas(Vector areas) {
          pendingAreas = areas;
      }
  
      public void setPendingWidth(int width) {
          pendingWidth = width;
      }
  
      /**
        * sets hyphenation related traits: language, country, hyphenate, hyphenation-character
        * and minimum number of character to remain one the previous line and to be on the
        * next line.
        */
      public void changeHyphenation(String language, String country,
                                    int hyphenate, char hyphenationChar,
                                    int hyphenationPushCharacterCount,
                                    int hyphenationRemainCharacterCount) {
          this.language = language;
          this.country = country;
          this.hyphenate = hyphenate;
          this.hyphenationChar = hyphenationChar;
          this.hyphenationPushCharacterCount = hyphenationPushCharacterCount;
          this.hyphenationRemainCharacterCount =
            hyphenationRemainCharacterCount;
  
      }
  
  
      /**
         * creates a leader as String out of the given char and the leader length
         * and wraps it in an InlineArea which is returned
         */
      private InlineArea buildSimpleLeader(int charNumber, int leaderLength) {
          int factor = (int) Math.floor (leaderLength /
                                         this.currentFontState.width(charNumber));
          char [] leaderChars = new char [factor];
          char fillChar = (char) charNumber;
          for (int i = 0; i < factor; i ++) {
              leaderChars[i] = fillChar;
          }
          WordArea leaderPatternArea =
            new WordArea(currentFontState, this.red, this.green,
                           this.blue, new String (leaderChars), leaderLength);
          leaderPatternArea.setYOffset(placementOffset);
          return leaderPatternArea;
      }
  
      /**
         * calculates the width of space which has to be inserted before the
         * start of the leader, so that all leader characters are aligned.
         * is used if property leader-align is set. At the moment only the value
         * for leader-align="reference-area" is supported.
         *
         */
      private int getLeaderAlignIndent (int leaderLength,
                                        int leaderPatternWidth) {
          //calculate position of used space in line area
          double position = getCurrentXPosition();
          //calculate factor of next leader pattern cycle
          double nextRepeatedLeaderPatternCycle =
            Math.ceil(position / leaderPatternWidth);
          //calculate difference between start of next leader
          //pattern cycle and already used space
          double difference = (leaderPatternWidth *
                               nextRepeatedLeaderPatternCycle) - position;
          return (int) difference;
      }
  
      /**
         * calculates the used space in this line area
         */
      private int getCurrentXPosition() {
          return finalWidth + spaceWidth + startIndent + pendingWidth;
      }
  
      /**
       * extracts a complete word from the character data
       */
      private String  getHyphenationWord (char [] characters, int wordStart) {
          boolean wordendFound = false;
          int counter = 0;
          char [] newWord = new char [100];  //create a buffer
          while ((!wordendFound) && ((wordStart + counter) < characters.length)) {
            char tk = characters[wordStart+counter];
            if (Character.isLetter(tk)) {
              newWord[counter] = tk;
              counter++;
            } else {
              wordendFound = true;
            }
          }
          return new String (newWord,0,counter);
      }
  
  
      /** extracts word for hyphenation and calls hyphenation package, 
       *  handles cases of inword punctuation and quotation marks at the beginning
       *  of words, but not in a internationalized way 
       */
      public int doHyphenation (char [] characters, int position, int wordStart, int remainingWidth) {
          //check whether the language property has been set
          if (this.language.equalsIgnoreCase("none")) {
            MessageHandler.errorln("if property 'hyphenate' is used, a language must be specified");
            return wordStart;
          }
  
          /** remaining part string of hyphenation */
          StringBuffer remainingString = new StringBuffer();
  
          /** for words with some inword punctuation like / or - */
          StringBuffer preString = null;
  
          /**  char before the word, probably whitespace  */
          char startChar = ' ' ;//characters[wordStart-1];
  
          /** in word punctuation character */
          char inwordPunctuation;
  
          /** the complete word handed to the hyphenator */
          String wordToHyphenate;
  
          //width of hyphenation character
          int hyphCharWidth = this.currentFontState.width(this.hyphenationChar);
          remainingWidth -= hyphCharWidth;
  
          //handles ' or " at the beginning of the word
          if (characters[wordStart] == '"' || characters[wordStart] == '\'' ) {
              remainingString.append(characters[wordStart]);
              //extracts whole word from string
              wordToHyphenate = getHyphenationWord(characters,wordStart+1);
          } else {
              wordToHyphenate = getHyphenationWord(characters,wordStart);
          }
  
          //if the extracted word is smaller than the remaining width
          //we have a non letter character inside the word. at the moment
          //we will only handle hard hyphens and slashes
          if (getWordWidth(wordToHyphenate)< remainingWidth) {
              inwordPunctuation = characters[wordStart+wordToHyphenate.length()];
              if (inwordPunctuation == '-' ||
                  inwordPunctuation == '/' ) {
                  preString = new StringBuffer(wordToHyphenate);
                  preString = preString.append(inwordPunctuation);
                  wordToHyphenate = getHyphenationWord(characters,wordStart+wordToHyphenate.length()+1);
                  remainingWidth -= (getWordWidth(wordToHyphenate) + this.currentFontState.width(inwordPunctuation));
              }
          }
  
          //are there any hyphenation points
          Hyphenation hyph = Hyphenator.hyphenate(language,country,wordToHyphenate,hyphenationRemainCharacterCount,hyphenationPushCharacterCount);
          //no hyphenation points and no inword non letter character
          if (hyph == null && preString == null) {
              if (remainingString.length() > 0) {
                  return wordStart-1;
              } else {
                  return wordStart;
              }
  
          //no hyphenation points, but a inword non-letter character
          } else if (hyph == null && preString != null){
              remainingString.append(preString);
              this.addWord(startChar,remainingString);
              return wordStart + remainingString.length();
          //hyphenation points and no inword non-letter character
          } else if (hyph != null && preString == null)  {
              int index = getFinalHyphenationPoint(hyph,remainingWidth);
              if (index != -1) {
                  remainingString.append(hyph.getPreHyphenText(index));
                  remainingString.append(this.hyphenationChar);
                  this.addWord(startChar,remainingString);
                  return wordStart + remainingString.length()-1;
              }
          //hyphenation points and a inword non letter character
          } else if (hyph != null && preString != null) {
              int index = getFinalHyphenationPoint(hyph,remainingWidth);
              if (index != -1) {
                remainingString.append(preString.append(hyph.getPreHyphenText(index)));
                remainingString.append(this.hyphenationChar);
                this.addWord(startChar,remainingString);
                return wordStart + remainingString.length()-1;
              } else {
                remainingString.append(preString) ;
                this.addWord(startChar,remainingString);
                return wordStart + remainingString.length();
              }
          }
          return wordStart;
      }
  
  
      /** calculates the wordWidth using the actual fontstate*/
      private int getWordWidth (String word) {
        int wordLength = word.length();
        int width = 0;
        char [] characters = new char [wordLength];
        word.getChars(0,wordLength,characters,0);
        for (int i = 0; i < wordLength; i++) {
          width += this.currentFontState.width(characters[i]);
        }
        return width;
      }
  
      public int getRemainingWidth()
      {
          return this.getContentWidth() - this.getCurrentXPosition();
      }
  
      public void setLinkSet(LinkSet ls)
      {
      }
  
      public void addInlineArea(Area box)
      {
          addPending();
          addChild(box);
          prev = TEXT;
          finalWidth += box.getContentWidth();
      }
  
      public void addInlineSpace(InlineSpace is, int spaceWidth)
      {
          addChild(is);
          finalWidth += spaceWidth;
  //        spaceWidth = 0;
      }
  
      /** adds a single character to the line area tree*/ 
      public int addCharacter (char data, LinkSet ls, boolean ul) {
          WordArea ia = null;
          int remainingWidth =
            this.getContentWidth() - this.getCurrentXPosition();
          int width = this.currentFontState.width(data);
          //if it doesn't fit, return
          if (width > remainingWidth) {
            return org.apache.fop.fo.flow.Character.DOESNOT_FIT;
          } else {
            //if whitespace-collapse == true, discard character
            if (Character.isSpaceChar(data) && whiteSpaceCollapse == WhiteSpaceCollapse.TRUE) {
              return org.apache.fop.fo.flow.Character.OK;
            }
            //create new InlineArea
            ia = new WordArea(currentFontState,
                                           this.red, this.green, this.blue,
                                           new Character(data).toString(),width);
            ia.setYOffset(placementOffset);
            ia.setUnderlined(ul);
            pendingAreas.addElement(ia);
            if (Character.isSpaceChar(data)) {
              this.spaceWidth =+ width;
              prev = LineArea.WHITESPACE;
            } else {
              pendingWidth += width;
              prev = LineArea.TEXT;
            }
            return org.apache.fop.fo.flow.Character.OK;
          }
      }
  
  
      /** adds a InlineArea containing the String startChar+wordBuf to the line area children.  */
      private void addWord (char startChar, StringBuffer wordBuf) {
          String word = wordBuf.toString();
          WordArea hia;
          int startCharWidth = this.currentFontState.width(startChar);
          if (startChar == ' ') {
              this.addChild(new InlineSpace(startCharWidth));
          } else {
              hia = new WordArea(currentFontState,
                                   this.red, this.green, this.blue,
                                   new Character(startChar).toString(),1);
              hia.setYOffset(placementOffset);
              this.addChild(hia);
          }
          int wordWidth = this.getWordWidth(word);
          hia = new WordArea(currentFontState,
                                   this.red, this.green, this.blue,
                                   word,word.length());
          hia.setYOffset(placementOffset);
          this.addChild(hia);
  
          //calculate the space needed
          finalWidth += startCharWidth + wordWidth ;
      }
  
  
      /** extracts from a hyphenated word the best (most greedy) fit */ 
      private int getFinalHyphenationPoint(Hyphenation hyph, int remainingWidth) {
          int [] hyphenationPoints = hyph.getHyphenationPoints();
          int numberOfHyphenationPoints = hyphenationPoints.length;
  
          int index = -1;
          String wordBegin = "";
          int wordBeginWidth = 0;
  
          for (int i = 0;i <  numberOfHyphenationPoints; i++){
              wordBegin = hyph.getPreHyphenText(i);
              if (this.getWordWidth(wordBegin) > remainingWidth){
                  break;
              }
              index = i;
          }
          return index;
      }
  
  }